In UDP, one send()
is a complete "message", you can't split a "message" across multiple send()
s.
But TCP is a byte stream, so you can make multiple consecutive calls to send()
per "message". So, just send the data length in one send()
, and then send the data in another send()
. TCP will ensure the bytes are received in the same order they are set sent. You don't have to prepend the length bytes to the std::string
itself at all.
This works especially well if "Send Coalescing" (aka the Nagle Algorithm) is enabled, which is usually is by default. That allows the socket stack to buffer outgoing data so it can send packets over the network more efficiently. But even with Nagle disabled, this scheme will still work.
In fact, in TCP there is no guarantee that send()
will accept all of the requested bytes in one go, so you have to be prepared to call send()
multiple times anyway.
Try something like this:
bool sendRaw(int sock, const void *data, size_t len)
{
const char *pdata = static_cast<const char*>(data);
while (len > 0)
{
int numSent = send(sock, pdata, len, 0);
if (numSent < 0) return false; // or throw...
pdata += numSent;
len -= numSent;
}
return true;
}
bool sendUint16(int sock, uint16_t value)
{
value = htons(value);
return sendRaw(sock, &value, sizeof(value));
}
bool sendString(int sock, const std::string &s)
{
if (s.size() > 0xFFFF) return false; // or throw...
uint16_t len = static_cast<uint16_t>(s.size());
bool ok = sendUint16(sock, len);
if (ok) ok = sendRaw(sock, s.c_str(), len);
return ok;
}
std::string udpData = ...;
bool ok = sendString(sock, udpData);
...
And then you can just reverse the process on the receiving side, eg:
int recvRaw(int sock, void *data, size_t len)
{
char *pdata = static_cast<char*>(data);
while (len > 0)
{
int numRecvd = recv(sock, pdata, len, 0);
if (numRecvd <= 0) return numRecvd; // or throw...
pdata += numRecvd;
len -= numRecvd;
}
return 1;
}
int recvUint16(int sock, uint16_t &value)
{
int ret = recvRaw(sock, &value, sizeof(value));
value = (ret == 1) ? ntohs(value) : 0;
return ret;
}
int recvString(int sock, std::string &s)
{
s.clear();
uint16_t len;
int ret = recvUint16(sock, len);
if ((ret == 1) && (len > 0)) {
s.resize(len);
ret = recvRaw(sock, s.data()/*&s[0]*/, len);
}
return ret;
}
std::string udpData;
int ret = recvString(sock, udpData);
...