Payload split over two TCP packets when using Boost ASIO, when it fits within the MTU

Deadly 提交于 2019-12-05 02:16:59

Your code:

out << (char) 0x1 << (char) 0x2 << (char) 0x3;

Will make 3 calls of operator<< function.

Because of Nagle's algorithm of TCP, TCP stack will send available data ((char)0x1) to peer immediately after/during the first operator<< call. So the rest of the data (0x2 and 0x3) will go to the next packet.

Solution for avoiding 1 byte TCP segments: Call sending functions with bigger bunch of data.

Don't worry, you are from from the only one to have this problem. There is definitely a solution. In fact, you have TWO problems with your legacy protocol and not only one.

Your old legacy protocol requires one "application message" to fit in "one and only one TCP packet" (because it incorrectly use a TCP stream-oriented protocol as a packet-oriented protocol). So we must make sure that :

  1. no "application message" is split across multiple TCP packets (the problem you are seeing)
  2. no TCP packet contains more than one "application message" (you are not seeing this but it may definitely happen)

The solution :

problem 1

You must feed your socket with all your "message" data at once. This is currently not happening because, as other people have outlined it, the boost stream API you use put data into the socket in separated calls when you use successive "<<" and the underlying TCP/IP stack of your OS doesn't buffer it enough (and with reasons, for better performance)

Multiple solutions :

  • you pass a char buffer instead of separate chars so that you make only one call to <<
  • you forget about boost, open an OS socket and feed it in one call to send() (on windows, look for the "winsock2" API, or look for "sys/socket.h" on unix/cygwin)

problem 2

You MUST activate the TCP_NODELAY option on your socket. This option is especially made for such legacy protocol cases. It will ensure that the OS TCP/IP stack send your data "without delay" and doesn't buffer it together with another application message you may send later.

  • if you stick with Boost, look for the TCP_NODELAY option, it is in the doc
  • if you use OS sockets, you'll have to use the setsockopt() function on your socket.

Conclusion

If you solve those two problems, you should be fine !

The OS socket API, either on windows or linux, is a bit tricky to use, but you'll gain full control about its behaviour. Unix example

I am not sure who would have imposed such a thing as having a requirement that an entire payload be within one TCP packet. TCP by its nature is a streamed protocol and much of the details in number of packets sent and payload size etc. are left up to the TCP stack implementation of the operating system.

I would double check to see if this is an actual restriction of your protocol or not.

I agree with User1's answer. You probably invoke operator << several times; on the first invocation it immediately sends the first byte over the network, then the Nagle's algorithm comes into play, hence the remaining data is sent within a single packet.

Nevertheless, even if the packetization was not an issue, the even fact that you invoke a socket sending function frequently on small pieces of data is a big problem. Every function called on a socket invokes a heavy kernel-mode transaction (system call), calling send for every byte is simply insane!

You should first format your message in the memory, and then send it. For your design I'd suggest creating a sort of a cache stream, that would accumulate the data in its internal buffer and send it at once to the underlying stream.

It is erroneous to think of data sent over a TCP socket as packets. It is a stream of bytes, how you frame the data is application specific.

Any suggestions?

I suggest you implement a protocol such that the receiver knows how many bytes to expect. One popular way to accomplish this is to send a fixed size header indicating the number of bytes for the payload.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!