Sign in to follow this  

TCP/IP packetizing, send/recv call correspondance

This topic is 4305 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I understand that a single tcp call to send on a socket may not be received in a since recv call, but multiple ones. My question is, is this ALWAYS a possibility? Or only when a large amount of data is being sent? Most tutorials i find dont mention or take into account this issue. Is there a 'safe' packet size (something less than the MTU i would imagine) where data from a single call to send is guaranteed to arrive in a single call in recv on the other end? Especially if setting no-delay on (disable nagle's algo) and flush after the send call? If this was possible, it would save me some work/bandwidth, because i dont have to use packetizing techniques (send size of packet first or delimit data).

Share this post


Link to post
Share on other sites
More problematic is that two sends can end up being received in one call to recv(), unless you drop and reconnect every time (which is horribly expensive and I don't imagine you would!). This is true and reasonably commonplace (maybe 5%) with small packets, 20b or so, because I've seen it while testing things. So you probably have to put a length check on anyway.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tylon
I understand that a single tcp call to send on a socket may not be received in a since recv call, but multiple ones.


Moreover, a more important possibility, is that the reverse happens, i.e. multiple calls to send() have their data concatenated and received by the same recv().

Of course there are some ways around this. If you always call send() and recv() with the same number of bytes each time, there will always be an exact 1:1 relationship.

Quote:

My question is, is this ALWAYS a possibility? Or only when a large amount of data is being sent?


No, it's always a possibility. The MTU of a connection is worked out dynamically and there can be absolutely guarantees about it.

Quote:

Is there a 'safe' packet size (something less than the MTU i would imagine) where data from a single call to send is guaranteed to arrive in a single call in recv on the other end?


No, no, no.

If the MTU doesn't divide cleanly into your chunk size, you could end up with two messages being split into three chunks - the final one of which might be very tiny (like, a single byte for example).

Quote:

Especially if setting no-delay on (disable nagle's algo) and flush after the send call?


No, setting SO_NODELAY just makes it less likely, it doesn't make it impossible. That's because, even if the OS *tries* to send the message immediately, it may not succeed (because the hardware is busy, for instance), so it may amalgamate two chunks anyway.

Quote:

If this was possible, it would save me some work/bandwidth, because i dont have to use packetizing techniques (send size of packet first or delimit data).


No, you can't do this with TCP. There are existing protocols on top of TCP you can use implemented by libraries, and there are new, proposed, not widely adopted sequenced packet protocols which exist on top of IP. But there isn't much OS support (or indeed, NAT routers etc).

Mark

Share this post


Link to post
Share on other sites
This is somewhat covered by question 14 of the FAQ. hplus might want to expand the description a bit.

It would also be nice if we could link directly to the answers for each question as long as I'm assigning work to him :).

To generalize a bit more on hplus's comment above what you really need to do is to figure out how big your message is, buffer data until you get (at least) that much, process the message, and reset things such that you can then process the next message. The next message could have piggy-backed in on the tail of the previous message and already be in the buffer, you could have a partial message there, or your buffer might be empty and you start from the very beginning.

There are three basic ways of figuring out how big a message is. You could procede each message with a header that contains the message size (be sure to spec whether or not the size of the header is included in that size). You could infer the (fixed) size based on the message id. Or you could infer the end of the message based on the data itself (NULL terminator, receipt of "</html>", whatever).

Share this post


Link to post
Share on other sites
A length and a code is a good way to go, imho; you can use the code to switch on at the other end independent of the content of the message.

Here is my code (C#) for reading messaged data over TCP:
  void ReadInternal(byte[] buf, int read){
if((OnReadMessage != null) && (MessageType != MessageType.Unmessaged)){
// Messaged mode
int copied;
uint code = 0;
switch(MessageType){
case MessageType.CodeAndLength:
case MessageType.Length:
int length;
if(MessageType == MessageType.Length){
copied = FillHeader(ref buf, 4, read);
if(headerread < 4) break;
length = GetInt(msgheader, 0, 4);
} else{
copied = FillHeader(ref buf, 8, read);
if(headerread < 8) break;
code = (uint)GetInt(msgheader, 0, 4);
length = GetInt(msgheader, 4, 4);
}
bytes.Add(buf, 0, read - copied);
if(bytes.Length >= length){
// A message was received!
headerread = 0;
byte[] msg = bytes.Read(0, length);
OnReadMessage(this, code, msg, length);
// Don't forget to put the rest through the mill
byte[] whatsleft = bytes.Read(length, bytes.Length - length);
bytes.Clear();
if(whatsleft.Length > 0) ReadInternal(whatsleft, whatsleft.Length);
}
//if(OnStatus != null) OnStatus(this, bytes.Length, length);
break;
}
}
}


... and the FillHeader method
  int FillHeader(ref byte[] buf, int to, int read) {
int copied = 0;
if(headerread < to){
// First copy the header into the header variable.
for(int i = 0; (i < read) && (headerread < to); i++, headerread++, copied++){
msgheader[headerread] = buf[i];
}
}
if(copied > 0){
// Take the header bytes off the 'message' section
byte[] newbuf = new byte[read - copied];
for(int i = 0; i < newbuf.Length; i++) newbuf[i] = buf[i + copied];
buf = newbuf;
}
return copied;
}



It uses some other classes that I've written so you can't just steal and run (although I'll send complete source to anyone who wants it), but hopefully it gives you the idea of what's required at the receiving end, i.e. waiting for an entire message to arrive and then re-processing any extra data which is part of the next message, remembering your data can be split anywhere in a message or the (fixed size) header.

This is a bit overcomplicated for your use because my ClientInfo class lets you choose whether to include a code or not.

Share this post


Link to post
Share on other sites

This topic is 4305 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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