getting down with TCP

Started by
7 comments, last by deftware 18 years, 5 months ago
:) ...... i'm trying to send snapshots via TCP ala Quake3 netcode of entities, and they are delta-compressed. how do i manage them on the receiving end when they arrive in fragments or clumped together with other snapshots ? could anyone write me some pseudo-code for re-creating whole packets out of a TCP stream? everyone talks about it like it's so easy, but i can hardly fathom how to go about achieving the creation of a system that can re-organize clumped/fragmented data into the original chunks. i understand sending the size of the packet at the beginning of it, but it's still very confusing for me. the faq tells me to do this, but not how.
Advertisement
You can send() the length as a 4 byte long unsigned integer. Then the packet.

On the receiving end, you recv() until you read 4 bytes. This will be the size of your packet. Then you recv() until you get 'length' number of bytes. A recv() call will return the number of bytes returned in the buffer, 0 on close or less than 0 on error.

So:
int tcp_read(int sock, char* buf, int len)
{
int t=0;
int r;
while(t <
This question is answered in the Forum FAQ.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
This question is answered in the Forum FAQ.


thanks. to some degree it is answered. it only told me what the problem was (which helped me understand better), but for me it wasn't enough information for me to create a sufficient solution.

as for the anonymous answer, wouldn't TCP sometimes clump a 4-byte packet with other packets ?
Quote:Original post by radioteeth
Quote:Original post by hplus0603
This question is answered in the Forum FAQ.


thanks. to some degree it is answered. it only told me what the problem was (which helped me understand better), but for me it wasn't enough information for me to create a sufficient solution.

as for the anonymous answer, wouldn't TCP sometimes clump a 4-byte packet with other packets ?


Just think of it as a stream when using TCP rather than packets (which would be like UDP). If the first 4 bytes are the length of your packet, then you read that many more bytes. Which will align you correctly to read the next 4 bytes, which will be the length of the next packet you have to read.

It is sent the same way you send a packet (using the send()) function.

What I did (not sure if it is a correct method or not), is on the receiving side, I would read as much as possible, and store the data, then analyze the first 4 bytes, and determine if I have a complete packet. I'd process the packet and repeat this until all packets are processed or one is fragmented. I'd save this until more data arrives. I figured this is better than blocking while waiting for a packet to arrive.
I have a class which does what you wish to do, I am however not home atm, but I'll give you the code as soon as I get home (sometime tonight).
Just as nrpz said, basically what you do is have a header with the packet size for each packet, as has already been mentioned. Then you keep reading off the socket until you have the full "packet", then you store any extra data received in the last read call in a buffer, which will be start of the next packet(s). The method returns whenever it has a full packet, and stores the extra read data for the next method call.


Hope this helps

Regards,
/Omid
Best regards, Omid
This is how the FAQ tells you to solve this problem:

Quote:To construct message semantics on TCP, each time you send a message, you have to precede it with the length of the message, as a short or a long (use htons() or htonl() to make it cross-platform). Then write that many bytes of message. On the receiving side, you'd first read out the length (a short or long) and then that many bytes of message data, re-constituting each message sent from the other end.


In code, it would look something like:

void send_message( int socket, void const * message, short size ) {  short wire_size = htons( size );  send( socket, &wire_size, sizeof( wire_size ), 0 );  send( socket, message, size, 0 );}void read_message( int socket, void * outMessage, short maxMessageSize, short * outSize ) {  short wire_size;  read( socket, &wire_size, sizeof( wire_size ) );  *outSize = ntohs( wire_size );  if( *outSize > maxMessageSize ) {    abort();  }  read( socket, outMessage, *outSize );}


On Windows, read() can't be used on sockets, to use recv() instead.

As long as ALL messages are sent through send_message(), and all messages are received through receive_message(), the protocol will not go out of sync, so it won't matter if TCP decides to lump multiple sends into a single IP packet -- TCP is a stream protocol, not a packet protocol.
enum Bool { True, False, FileNotFound };
class CPacketReceiver{public:	CPacketReceiver(int bufferSize) 	{ 		mHeldBytes = 0;		mBufferSize = bufferSize;		mHoldingBuffer = new char[mBufferSize];	};	~CPacketReceiver()	{		delete[] mHoldingBuffer;	}	int ReceivePacket(SOCKET socket, char* pOutBuffer);	int HeldBytes() { return mHeldBytes; };private:	static const int mPrefixSize = sizeof(CMessageHeader);	char* mHoldingBuffer;	int mHeldBytes;	int mBufferSize;};int CPacketReceiver::ReceivePacket(SOCKET socket, char* pOutBuffer){    int bytes_read = 0;    int packet_size;    CMessageHeader* pHeader;    bool building_prefix = true;    // Copy any data remaining from previous call into output buffer.    if (mHeldBytes > 0)	{        memcpy(pOutBuffer, mHoldingBuffer, mHeldBytes);        bytes_read += mHeldBytes;        mHeldBytes = 0;    }    // Read the packet    while (1) 	{         if (building_prefix) 		{            if (bytes_read >= mPrefixSize)			{							pHeader = (CMessageHeader*) pOutBuffer;				packet_size = pHeader->mMessageSize;                building_prefix = false;                if (packet_size > mBufferSize) 				{                    /* Buffer's too small to hold the packet!					 */					// :: TODO :: Error handling                    return 0;                }            }        }        if (!building_prefix && (bytes_read >= packet_size))		{            break;  // finished building packet        }        int new_bytes_read = recv(socket, 								  pOutBuffer + bytes_read, 								  mBufferSize - bytes_read, 0);		if ((new_bytes_read == 0) || (new_bytes_read == SOCKET_ERROR)) 		{			Sleep(100);        }		else		{			bytes_read += new_bytes_read;		}	}    /* If anything is left in the read buffer, keep a copy of it	 * for the next call.     */     mHeldBytes = bytes_read - packet_size;    memcpy(mHoldingBuffer, pOutBuffer + packet_size, mHeldBytes);    return packet_size;}



The indenting got slightly messed up, but I guess you get the psuedo idea :)
And no garantuees that this code is good code, but it works and gives you an idea :)

Hope this helps

Regards,
/Omid
Best regards, Omid

Awesome. Thanks a lot guys. You have been a huge help!

This topic is closed to new replies.

Advertisement