Sign in to follow this  
deftware

getting down with TCP

Recommended Posts

deftware    1778
:) ...... 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.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
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 <

Share this post


Link to post
Share on other sites
deftware    1778
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 ?

Share this post


Link to post
Share on other sites
nprz    692
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.

Share this post


Link to post
Share on other sites
Omid Ghavami    1007
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

Share this post


Link to post
Share on other sites
hplus0603    11356
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.

Share this post


Link to post
Share on other sites
Omid Ghavami    1007

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this