1 msg 2 msg 3...lost

Started by
81 comments, last by KalvinB 21 years, 9 months ago
I'm using a non-blocking TCP/IP server. I can send up to two messages just fine. But when I fire off 3 messages to the same destination one right after the other, the third message is lost but Winsock doesn't report any errors. The three messages total 94 bytes. If I put in a 100msec delay between messages all the messages arrive fine. Unless on the client side the last two messages are being combined in the recieve buffer as one message, but that shouldn't happen. -edit- Unfortunatly it is. When messages are sent too fast they're combined into one. The client just needs to recognize when a message actually contains two or more messages. Fortunatly all messages come with a length value so figuring out which need to be broken up is easy. Ben IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com ] [edited by - KalvinB on July 11, 2002 5:01:31 PM]
Advertisement
Thats why it''s called "stream based"
(at least i assume by TCP/IP you actually mean TCP, because if you are using UDP this certainly shouldn''t happen.)
There''s probably an faster way but the quick fix was the following:

more=true
while(more)
process message


  if(message.bytes_pending>message.length+16){int bpend, len;char msg[2048];	bpend=message.bytes_pending;	len=message.length+16;	ZeroMemory(&msg,sizeof(msg));	memcpy(&msg[0],&message,sizeof(message));	ZeroMemory(&message,sizeof(message));	memcpy(&message,&msg[len],bpend-len);	message.bytes_pending=bpend-len;	more=true;}else	more=false;  


16 is the number of bytes in my own packet header.

Ben


IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com ]
Duno exactly what that code snipit is doing.. but this is the way i handle it basically... (note that i cant just dump my code cuz its IOCP implementation which will differ than yours)

BYTE inbuffer[5000] //example read buffer
int inbufpos = 0;

...
call a recv to the current pos in the inbuffer (inbufpos)
while( inbufpos < bytesRead )
{
BYTE packetLen = inbuffer[inbufpos ];
if( packetLen+inbufpos > bytesRead )
{
//there is a packet which is not fully recieved
memcpy( &inbuffer[0], &inbuffer[inbufpos], bytesRead - inbufpos )
inbufpos = bytesRead - inbufpos;
break;
}
//copy the message somewhere or process it
//advance the current pos
inbufpos += packetLen;
}

...

something like that anyway. That way it processes multiple messages and also if say half a message is recieved then it will then not process it but instead move the packet to the start of the buffer and the next recv call will ask for the data to be placed at the end of the existing packet.

does that make sence / help ? also just to clear something up.. im right in thinking in TCP you can also have the case of a message being recieved over two seperate recv calls ( a prime example of this would be if the message was split because of packet size restrictions)

~ Tim
~ Tim
My code does essentially the same thing. You just have a different way of storing the messages.

Ben


IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com ]
Just to say it outright...

TCP does not have a concept of messages. It only has a stream of bytes. You can do three sends and get them all in one recv on the other side. Alternatively you could do one send and have to do three recvs on the other side. You could do three sends of 100 bytes each and on the other side end up with three recvs - for 2, 273, and 25 bytes respectively.

If your stream has logical messages in it you need some way of knowing how big the message is - either embed the size in the stream or hardcode it. If you get a partial recieve of a logical packet you need to be prepared to buffer the data until the rest of the packet arrives.

-Mike
-Mike
"If you get a partial recieve of a logical packet you need to be prepared to buffer the data until the rest of the packet arrives."

That would never happen in the project I''m working one where packets are typically under 50 bytes. TCP guaretees packets come in order and in tact. The breakup point is 8KB. My message struct only supports 1KB. There''s also a checksum in the header that lets me know if a packet somehow managed to get corrupt.

But again, that''s pretty much a waste of 4bytes as TCP will ensure the packet is 100% intact.

"either embed the size in the stream or hardcode it."

I use both. Some messages are variable length (like chat messages) where others are fixed (like when every byte means something). That''s why I had .length all this time in the header so fixing the combined message problem only took about 15 minutes.

Now to get back to the actual project.

Ben


IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com ]
quote:Original post by KalvinB
"If you get a partial recieve of a logical packet you need to be prepared to buffer the data until the rest of the packet arrives."

That would never happen in the project I'm working one where packets are typically under 50 bytes. TCP guaretees packets come in order and in tact. The breakup point is 8KB. My message struct only supports 1KB. There's also a checksum in the header that lets me know if a packet somehow managed to get corrupt.

But again, that's pretty much a waste of 4bytes as TCP will ensure the packet is 100% intact.


This is a misconception. Anon Mike is 100% correct.
TCP doesn't know anything, nor does it care anything about your "message" or its associated length. It is a frequent occurence with TCP to get only a partial amount of the expected data from a recv() call. Yes, if you want reliability, you need to buffer any partial messages that you receive. The whole 100% intact thing is a blatant myth. TCP is stream-based, therefore it implies order and guaranteed delivery, only. It does NOT guarantee that all packets from any particular send() will be ready when you do a recv(). Something else you should consider is gathering all of your send() calls. Instead of calling send() many times per game frame for a single client, buffer them, and make one send() call with all of your buffered messages. It will save bandwidth and CPU in the longrun.

quote:When messages are sent too fast they're combined into one. The client just needs to recognize when a message actually contains two or more messages.

This is an example of the Nagle Algorithm, a fundamental part of TCP. It prevents people from quickly sending ridiculously small packets of data over the network to the extent that TCP and IP headers become the bulk of traffic. It gathers data to send into larger packets so that the overhead from headers is reduced. It's called congestion control and is one of the most researched and debated areas of TCP. Congestion control algorithms are the main culprit in the majority of inherent latency in TCP. It is also one of many reasons most serious game projects rely on UDP for time-sensitive data.

[edited by - fingh on July 12, 2002 6:46:34 PM]
"It is a frequent occurence with TCP to get only a partial amount of the expected data from a recv() call."

That''s interesting because I have yet to see it happen on anything smaller than 8KB and I''m working in packets of less than 100bytes.

"Something else you should consider is gathering all of your send() calls. Instead of calling send() many times per game frame for a single client, buffer them, and make one send() call with all of your buffered messages. It will save bandwidth and CPU in the longrun."

I''ve thought of that and adding that functionality to the server would be cake and the client wouldn''t need any changes made now that it already supports multiple messages per packet.

"It is also one of many reasons most serious game projects rely on UDP for time-sensitive data."

I still don''t have any time sensitive data. That''s great you''re so educated in UDP and want to share this knowledge but I''m working with TCP/IP and am looking for TCP/IP solutions. All of my threads have been about TCP and you consistantly try to sell me on the benifits of UDP.

I''m perfectly aware when UDP is needed and when TCP will be sufficient. When I need help with UDP, I''ll let you know. Thanks.

Ben


IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com ]
The reason you are seeing complete transmissions is probably because you are doing your testing on a LAN or other very highspeed/low latency connection. If you do testing over a high-latency connection, the streamed nature of TCP/IP becomes more apparent

Dire Wolf
www.digitalfiends.com
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com

This topic is closed to new replies.

Advertisement