6

The accepted answer of this question explains:

When you send data, your TCP buffers it. So if you send a character it won't send it immediately but wait to see if you've got more.

But what if I don't have that much data to send? How does TCP decide when to send the already buffered data although the buffer isn't full? I'm sure that there has to be some kind of timeout, but I couldn't find informations about that.

3
  • This is OS dependent. Not to give a StackOverflow answer, but from a network programming point, there isn't necessarily a 1:1 correlation between write() and read().
    – Ricky
    Commented Jul 11, 2019 at 1:29
  • 1
    See en.wikipedia.org/wiki/Nagle%27s_algorithm Commented Jul 11, 2019 at 3:46
  • Did any answer help you? If so, you should accept the answer so that the question doesn't keep popping up forever, looking for an answer. Alternatively, you can post and accept your own answer.
    – Ron Maupin
    Commented Dec 20, 2020 at 3:02

3 Answers 3

6

TCP stack implementations in general and in cases follow RFC's

There exist RFC 896

...(small packet problem)
This classic problem is well-known and was first addressed in the
Tymnet network in the late 1960s.  The solution used there was to
impose a limit on the count of datagrams generated per unit time.
This limit was enforced by delaying transmission of small packets


RFC 896    Congestion Control in IP/TCP Internetworks      1/6/84


until a short (200-500ms) time had elapsed, in hope that  another
character  or two would become available for addition to the same
packet before the  timer  ran  out.

So we send data to buffer, network stack wait is there any data in addition, then send packet (very short and simple desribe from my side, there lot of events).

But it's a RFC, and better to check implementation of tcp stack in linux kernel(as example) or another tcp implemetations.

2
  • 4
    The “Nagle Algorithm” described in RFC896 is nearly universally implemented. It basically says collect characters to send as a group for as long as you have un-acknowledged data. So you send 1 character and you don’t send again until either you get a full packet to send or you get the ack for the first char. In Unixes you can disable this with the TCP_NODELAY socket option. Commented Jul 11, 2019 at 7:26
  • @JohnHascall Thanks for expanding my answer. In my answer I try to say: we need to read(know) implementation of network stack to get answer when TCP packet will be sended. Commented Jul 11, 2019 at 8:31
4

You've perfectly outlined the need and use case for the Push flag in TCP.

When the complete data set has been sent, and regardless of the status of the receiving entity's buffer, the sender can send a Push packet which tells the receiver to push everything in the buffer up to the application.

You can see this with Telnet very frequently. Each telnet packet (for the most part) is only a single letter. In ASCII encoding, this is a single byte (8 bits). The other end's TCP buffer definitely isn't full, but the complete data set (the letter) has already been sent. Hence, nearly every Telnet packet turns on the Push flag, to push the data from the receiver's buffer into the application.

So in the end, TCP doesn't decide. The sender decides by enabling the Push flag.


Let me expand a bit.

If you aren't already familiar with the OSI model, I'd suggest reading through this article. At least understand that TCP/UDP are L4, and have independent functions of L3, L2, L1, and L5+ (the application).

When the application needs to send something across a wire, it sends it to L4. L4 breaks it up into little chunks (based upon the MSS size) and sends it across the wire. For simplicity, let's assume the MSS is 1000 bytes.

To illustrate, let's use two examples:

  1. Example A - The application is telnet, and the data being sent are individual letters
  2. Example B - The application is some photo sharing app, and the data being sent is a 4500 byte image

In Example A, a single letter is sent (1 byte), and easily fits into one TCP data packet. On the receiving side, TCP holds on to the receive byte and waits for the next. When the next letter is sent (1 more byte), it still easily fits into a single TCP data packet, and also easily fits (along with the first letter) in the receiver's TCP buffer.

The user, however, is waiting for the letter to appear in their application. TCP hasn't "released" the letters to the application yet, therefore the application can not display the letters to the user.

From the user's perspective, it will just look like lag / delay. The sender would have to send enough letters to fill the receiver's TCP buffers before they get purged and pushed to the application.

Hence, Telnet sends the PSH flag with every data packet, telling the receiver not to wait until the buffer is full, but to immediately push the data packets (the letters) to the application. Allowing the application to immediately display them to the user.

But how does TCP know to send the push flag? Read on.

.

In Example 2, the first packet sent will include the first 1000 bytes. The other end will receive it, and buffer it. If the other end pushes the 1000 bytes to the application, then at best the application will only be ale to display about quarter of the image, at worst the application will not know what to do with a quarter of a file (it might not even be understood as an image file, mind you).

The sender would then send the next packets, sized 1000, 1000, 1000, 500. At this point, all 4500 bytes have been sent. But more importantly, everything the application gave to TCP has been sent. That triggers TCP to send the PSH flag, to indicate "hey, that was the entire data set I needed to transfer, let the application do with it what it wants".

.

So what triggers the PSH flag isn't an API called by the application. It is simply the application's complete data sent being handed to TCP, and TCP setting it automatically when the data set is sent completely.

In Example A, each "data set" is 8 bits (one letter), and the PUSH flag is sent along with every 8 bits (one letter).

In Example B, each "data set" contains parts of a 4500 byte image. And the PUSH flag is sent when all 4500 bytes have been sent (the entire image).


If there are devices that do not follow the rules of TCP and try to buffer more or less than they should, the end effect would be the user's experience with the application would suffer. Imagine a telnet session where you only see what is typed after every 100's letter. Imagine an image sharing application that continually only displays partial images, or warnings of corrupted image files.

Disclaimer: Link above is to my blog and is provided to help understand the answer given. The blog is not monetized, contains no ads, and does not require any e-mail subscription. It is provided purely to aid the readers.

4
  • 1
    On the other hand the answer states, that there is no API to set the PSH flag and here: stackoverflow.com/a/13059521/9699530 I read that the PSH would sometimes not work at all. What are the consequences of that?
    – TimSch
    Commented Jul 10, 2019 at 19:19
  • I updated the answer, @TimSch.
    – Eddie
    Commented Jul 10, 2019 at 20:37
  • 2
    At the sending end, it's OS dependent. A series of short writes could result in an equal number of packets, or they could be coalesced -- either by the OS itself, the network device driver, or the physical NIC hardware. (for the record, many modern NICs do receive side coalescing as well.)
    – Ricky
    Commented Jul 11, 2019 at 1:19
  • 3
    It seems to me that the original question is more focused on the sending side than the receiving side. PSH theoretically is, as you write, a signal to the receiving side. On the sending side, "Nagel's algorithm" attempts to address the question the OP asked, i.e. "How does TCP decide when to send the already buffered data although the buffer isn't full?" Commented Jul 11, 2019 at 3:45
1

TCP is the notion of a reliable, in-order byte stream over a transport which doesn't do bytes, and is neither in-order nor reliable. It is both a specification, and an actual implementation (which may be very different on different systems!). A software emulation of some kinda abstract idea, if you will.

Generally, from a relatively naive point of view, TCP simply grabs whatever you throw at it, divides it into segments (for small data, there will be just a single segment), and sends them out one after the other, waiting for an acknowledgement for each segment before sending the next, and re-sending a segment after "some time" if no acknowledgement came in. There's some checksums and sequence numbers added, too, blah blah.
The other end puts the segments together again in the correct order, and provides "bytes" to an application that asks for it via an obscure "socket" (which is more or less the same as a file descriptor), without anyone really knowing where the bytes came from, or what was necessary to provide them.

Now, if the world was perfect, if bandwidth was infinite, if routers didn't have queues, and if round trip times were zero, then this would be a perfectly working approach. Alas, that's not what the world looks like.

For that reason, TCP does a couple of things differently such as sending out several segments before blocking to wait for acknowledgements (with a diversity of different algorithms that adjust the window in different ways to provide best possible throughput without dropping too many packets on the wire).

Also, it tries to avoid sending single-byte (or very-few-byte) datagrams because that's a very obnoxious thing. This is done by what's called Nagle's algorithm, and it is actually pretty simple. Tiny amounts of data are bufferend and held back until the previously sent packet was acknowledged (which is what TCP would normally have done anyway, without delayed ACKs). This automatically rate-limits tiny sends and batches data together without the need for a lot of extra logic. Unless you explicitly specify TCP_NODELAY for a socket, that's the behavior that you normally get on every reasonable system. Well, in theory it's that simple, in practice, with delayed ACKs, it's a tidbit more complicated, but let's ignore that.

On the other hand, every now and then, TCP sends out data, with or without delay at its own discretion, based on unknown, unspecified, obscure metrics. Data size and time are parameters that play a role, without you or me, or anyone knowing exactly. Sending out anything that's present in the buffers every so-and-so-many hundred milliseconds is a common approach. While this sounds rather awful, it's actually good enough for 99.9% of all people, 99.9% of the time.
Remember, TPC is not a guaranteed realtime (or zero-delay) magical delivery system. It's a simulation of a reliable, ordered byte stream. Where "reliable" doesn't even mean that you have a guarantee that data will arrive at the other end. This is a guarantee that no protocol could possibly provide! Imagine what happens if I pull out the network cable, how do you guarantee that data will arrive?
You only have the guarantee that TCP will try its very best (by re-sending, if necessary), and you will know in case it didn't work. Other than with raw IP datagrams or UDP, for example. Where you boldly send out stuff, and nobody tells you whether or not something was sent and received at all, or maybe dropped, lost, whatever.

Sometimes, TCP sending your data on its own behalf is just not what you want, though! Sometimes, you have several smaller pieces of data that you preferrably want to have sent in one block. A "typical" server reply that starts with some header data, followed by the actual contents is an example of that. Sure enough, you don't want TCP to send out the pretty useless header alone, and then send out the actual contents later, only because you need two or three calls to send, and someone tries to be extra smart while you're done half-way.
Partial datagrams waste bandwidth and are more likely to cause a packet getting dropped somewhere on the internet (more packets on the wire means more packets dropped, inevitably).

That is the reason why such things as TCP_CORK (or TCP_NOPUSH in BSD) exist. This is basically the exact opposite of TCP_NODELAY, you're telling the network stack that you are going to add a couple of chunks of data one-by-one, and your expectation is that TCP doesn't in the mean time send out a half-MTU sized or even smaller datagram, or whatever.
The actual behavior, again, differs depending on what OS you run on. BSD, for example, will buffer data until a max-sized datagram worth of data is in the buffers (then send a MTU-sized datagram, and buffer again), or until you set TCP_NOPUSH to zero again, or until you close the socket (at which time it will send out the remainder). Linux, on the other hand side, will strictly comply with your request for 200 milliseconds, and then, devil may care, send out partial data anyway.

As a sidenote, the fact that TCP may send out remaining data upon calling close is a good reason to actually check the return code (which many people don't do, who cares what close may return!). Unless you do check, there's no way of knowing what has been going on, no way of being sure your guarantees are what you think they are.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.