[Interest] TCP ACK with QDataStream -- OR: disconnect race condition detection

Thiago Macieira thiago.macieira at intel.com
Mon Sep 10 15:27:17 CEST 2012


On segunda-feira, 10 de setembro de 2012 14.53.31, Till Oliver Knoll wrote:
> > If TCP is reliable by doing ACKs, why the hell would you need to track
> > ACKs
> > too?
> 
> Because, since that information is around anyway, why not use it (apart from
> that it seems not accessible in real world implementations)?

Aside from statistics and curiosity, I don't see the point in accessing the 
information.

> As in "I want to sent a message of 1000 bytes. Unless the counterparty's
> Transport layer doesn't acknowledge the receipt of 1000 bytes, I assume
> something went wrong (and take action by re-sending the message after some
> while in an "at least once" scenario)".

TCP will already resend it for you. So you're not resending it in the same 
connection, you're talking about disconnecting and reconnecting, and hoping 
that it will work better.

You could use TCP keepalives for this.

> That is, the application *wants* to know the number of bytes received by the
> counterparty, but doesn't want to apply an Application level protocol.

But it doesn't know how many bytes the peer has received in the Application 
layer. It can only know the number of bytes received in the Transport layer. 
That's the "statistics and curiosity" part I mentioned.

> For instance, the scenario could be that the "network" is the *only* concern
> to the application, or in other words: once the counterparty's Transport
> layer has ACK'ed the packets, you know (or rather: expect) that "all will
> be fine" (and if not, the counterparty just blew up and the connection will
> be closed and you'll know the next time you try to send a message).

That's a huge assumption. There are a number of reasons why a packet's 
contents may never be delivered, including the system rebooting between the 
ACK and the application reading the data. You're not accounting for that or 
many other reasons.

Therefore, it's a bad assumption to make. Don't design applications like that.

> So I might turn around the question: "Why would you NOT want to track (or
> rather: get informed about) the ACK which is done by the underlying layer?"

Because it's none of your business. Doing that means your application depends 
on the inner workings of the underlying layer and will not be portable to 
other transport mechanisms (SCTP, Unix sockets, etc.). It might also imply bad 
assumptions because TCP packets can be received out of order, with the peer 
including the information of which further packets it has already received.

And because there's no easy way of getting the information anyway.

> And coming back to the OP's actual question: currently you can't figure out
> whether the last n bytes did get through or not. Either you check the
> connection *before* you start sending (in which case the connection might
> just break the moment you start sending), or you check *after* (some while)
> whether the connection is "still good" (in which case you still don't know
> for sure wheter all, some or non at all of your bytes have been sent and
> ACK'ed by the counterparty).

A simple "happens-before" relationship: if the peer does something in response 
to a later message, then we know it has received the earlier message.

Note that "has received" does not imply "has acted". Some protocols are 
stream-oriented and still do permit out-of-order actions.

> > It's redundant: if you get the ACK information, that's because TCP got it
> > too;
> 
> ... and your application knows that at least the counterparty's Transport
> layer has received the data. If that's all you care about then you gave
> gained "information".

My point is that no application in the real world will be satisfied with that 
information.

> > if you don't get it, neither did TCP.
> 
> But you can react upon it (after some timeout), whereas if you don't get
> this information at all you're left in the dark whether to re-send the last
> n bytes.
> 
> That's why one might want to track those ACKs.

That's what TCP keepalives are for. Besides, you probably want to act on 
entire messages, not N bytes.

If the application crashes, you will eventually get an RST. Knowing how many 
bytes it received is pointless: since it crashed or was killed, it state was 
inconsistent. You cannot depend on the fact that the N bytes it received were 
acted on. And that's even if you could determine that it did, after all, read 
the bytes that the Transport received.

If the application exits cleanly, you'll get a FIN. But if it exited cleanly 
byt unexpectedly, it's a violation of the Application protocol and clearly the 
state was inconsistent. We go back to the case above.

If there's a network breakdown along the way, no packets will be received 
either way. This is where a timeout is useful for and TCP already has one. It 
might be way too long for modern-day applications, but it exists.

If the peer reboots, then you'll either get an RST or TCP keepalive will be 
necessary.

In any of those scenarios, knowing how many bytes the remote's TCP stack 
received is pointless.

> Assume you're now on some device with a dead slow network connection and you
> want to upload some data. Additionally you want to show some progress bar
> how many bytes have already been received (hint! hint!) by the
> counterparty. Let's further assume the data fits fully into the send buffer
> (which is on its turn much bigger than the packet size).
> 
> Let's have a look at a first naive implementation: you would use
> QIODevice::bytesWritten to update the progress bar and -bang!- you're from
> 0 to 100% in no time! Because we just filled the sender buffer, but maybe
> did not even send a single file onto the wire just yet!

Right.

> Now if we *had* the information about the *received bytes* (note again: here
> it is totally irrelevant what the receiver does with those bytes!) we could
> of course update the progress bar in a much more useful manner.

Right. But you could also update it based on the number of TCP packets sent 
but still not acknowledged.

And in your case, given modern TCP, you might *still* jump from 0 to 100%. 
Suppose that the first PSH packet is lost, but all others are received. The ACK 
count is still at 0, even though the peer has received about 95% of the data. 
Upon retransmission of that single packet, the remote ACKs the full message. 
Then your progress bar will jump from 0 to 100%.

And *even* if this convoluted scenario doesn't happen, it might *still* jump 
due to delayed ACKs. The remote does not need to ACK every single TCP packet. 
As long as it ACKs byte N, we know it received 0..N.

> Now you could still argue that this should be solved by an application level
> protocol, e.g. the receiver should send back the number of bytes it has
> processed or whatever. But a) the receiver's code might not be ours (we
> cannot modify it) and b) I might again turn around Thiago's question: "Why
> should the application duplicate what the underlying level is doing
> anyway?"

If the bandwidth is THAT small (think of dial-up modems under 14.4 kbps), the 
overhead of notifying that the packets are received would probably be 
considerable.

If all you want to know is exactly what TCP already knows, then double 
bookkeeping would be unfortunate. My point is not that. My point is that 
relying on the TCP ACKs can only be done with assumptions that the application 
should not be making in the first place.

> And all of a sudden it *seems* that the information about the received bytes
> is not *that* useless, after all, is it?
> 
> (By the way, that progress bar with a slow connection was exactly the
> initial motivation from the previous Stack Overflow question I referred to
> in my previous post).

Every buffer introduces a delay. See "buffer bloat".

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center
     Intel Sweden AB - Registration Number: 556189-6027
     Knarrarnäsgatan 15, 164 40 Kista, Stockholm, Sweden




More information about the Interest mailing list