tags:

views:

330

answers:

3

I'm sending messages over TCP/IP, I need to prefix message length in a char array and then send it. How do I do it?

Also can you please provide an example of how to extract it at the another end. And if possible, please explain.

I'm using C++ and Winsock.

EDIT:

string writeBuffer = "Hello";

unsigned __int32 length = htonl(writeBuffer.length());

It's not returning the correct length rather a very large number.

For the receiving part, if I use ntohl(), then I also get a large number instead of the correct length? Why is that so? I'm receiving like this

bool Server::Receive(unsigned int socketIndex)
{
    // Read data from the socket
    if (receivingLength)
    {
     bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
      ((char*)&messageLength) + bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived, 0);

     if (bytesReceived == SOCKET_ERROR)
     {
      return false;
     }

     if (bytesReceived == MESSAGE_LENGTH_SIZE)
     {
      // If uncomment the following line,
      // I won't get the correct length, but a large number
      //messageLength = ntohl(messageLength);
      receivingLength = false;
      bytesReceived = 0;
      bytesLeft = messageLength;
     }
    }
    else
    {
     if (bytesLeft > BUFFER_SIZE)
     {
      return false;
     }

     bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
      &receiveBuffer[bytesReceived], bytesLeft, 0);

     if (bytesReceived == SOCKET_ERROR)
     {
      return false;
     }

     if (bytesReceived == messageLength)
     {
      // we have received full message
      messageReceived = true;

      receiveBuffer[bytesReceived] = '\0';

      // wait for next message
      receivingLength = true;
     }

     bytesLeft -= bytesReceived;
    }

    return true;
}
A: 

I'm not sure if this is what you are asking, but I would send it as a string of a fixed length (let's say 4 characters, actual length up to you). In other words, if the data length is 164, send the string "0164". This gets over any problems of byte order at the other end, and is easy to read and debug.

To produce such strings, you can use a stringstream:

#include <sstream>
#include <iomanip>

std::string MakeLenStr( int n ) {
  std::ostringstream os;
  os << std::setw(4) << std::setfill('0') << n;
  return os.str();
}

To send:

std::string s = MakeLenStr( len );
send( sock, s.c_str(), 4 );

To read it at the other end, something like:

char a[5] = {0};
recv( sock, a, 4 );
int n = atoi( a );
anon
+1  A: 

Pseudocode (error handling omitted, be careful):

sender:

u_long converted = htonl( messageLength ); // convert from local byte order to network byte order
send( socket, (char*)&converted, sizeof( converted ), 0 );

receiver:

u_long messageLength;
recv( socket, (char*)&messageLength, sizeof( messageLength ), 0 );
messageLength = ntohl( messageLength ); convert from network byte order to local byte order
sharptooth
What is the following guy doing? http://tangentsoft.net/wskfaq/examples/packetize.htmlIs the same thing? or is it different
hab
+4  A: 

When sending a length field on a TCP stream, you need to decide two things:

  1. what length should the length have (1 byte, 2 bytes, 4 bytes, variable length)
  2. what endianness should I use

I recommend to use 4 bytes length, and network byte order (i.e. big-endian). For network byte order, the macros htonl and ntohl will convert between host (native) byte order (little-endian, in your case), and network byte order.

To send data, the fragment should look like this:

size_t length = strlen(data);
uint32_t nlength = htonl(length);
send(sock, &nlength, 4, 0);
send(sock, data, length, 0);

On the receiving side, you extract first the length, then the data:

uint32_t length, nlength;
recv(sock, &nlength, 4, 0);
length = ntohl(nlength);
data = malloc(length+1);
recv(sock, data, length, 0);
data[length] = 0;

What this code is missing is error handling: each of the send and receive calls may fail; the recvs may receive less data than expected. But this should give you an idea.

Edit: To deal with the case that the recv returns too few data, run it in a loop, keeping a count of what you have read so far, e.g.

int length_bytes = 0;
while(length_bytes < 4){
   int read = recv(sock, ((char*)&nLength)+length_bytes, 4-length_bytes, 0);
   if (read == -1) some_error_occurred_check_errno();
   length_bytes += read;
}
Martin v. Löwis
What is the following guy doing? http://tangentsoft.net/wskfaq/examples/packetize.htmlIs it the same?
hab
and what error handling should I do? I mean you are giving a long to receive will it explode? or crash?
hab
In the code sample you give, there is a buffer which may contain future data (i.e. subsequent messages). This complicates matters, but may help performance as it avoids small reads (which shouldn't really matter that much). As for error handling: it may happen that the first recv only gives you 2 bytes, not four. Then, nlength will not be fully initialized, and you pass a bogus size to malloc. That may indeed crash. More likely, you will allocate too much data, and then also try to read much more data than you will get, making you end up with nonsense in the string.
Martin v. Löwis
Why is the guy using bitwise operators? and how come the packet_size in that example always comes bigger then the buffer size and it fails?
hab
You should not be using long. The definition of htonl() etc use uint32_t. If you use long on some systems this may potentially fail.
Martin York
@Martin York: Thanks, fixed.
Martin v. Löwis
@Ahmed: the bitwise operations do what ntohl does in my code, for a flexible-length representation of the packet size (although the code fixes prefix_size it to 2 bytes). The packet length is in network byte order, so it is (for 2 byte length) (byte0<<8)|byte1 == byte0*256+byte1. For 4-byte length, the packet size can be computed as (byte0<<24)|(byte1<<16)|(byte2<<8)|byte3 ... and so on.
Martin v. Löwis
@Martin: How do I do like this? If while receiving the length, I received 2 bytes, the next time I call receive for receiving the next two bytes, what buffer do I give? I mean how do I concatenate the two bytes so that a uin32_t is made. Should I recv(sock, or some other way so that the previous two bytes I received doesn't gets lost. Please help
hab
@Ahmed: see my edit.
Martin v. Löwis
I'm using asynchronous sockets, will it pose a problem if I run it in a loop?
hab
I recommend to use blocking sockets. If you absolutely have to use non-blocking sockets, you have to use select() in the loop also, to make it block.
Martin v. Löwis
Okay thanks, I'll look into select()Martin you have been too much helpful. Thanks really appreciate your help
hab
@Martin: using the code in your edit, shouldn't I use ntohl? If I use ntohl, the value I get is invalid, whereas if I didn't then the the I get the length properly and is valid. Can you explain why?
hab
Sure. The loop only replaces the original recv call. Everything else remains (except you need another loop for the second recv).
Martin v. Löwis
but when using ntohl() the length returned is a very big number, let me post my code, see edit
hab