17

Is there a way to configure the timeout in which a socket expects to receive an ACK for sent data before it decides that the connection has failed?

I'm aware this can be done at the application level as well, but since every packet I send is ACK'd anyway, and I just want to know if my data is received, using additional data at the application level to accomplish the same thing seems wasteful. (Not to mention, my particular application uses per-byte charged cellular links.)

Note: As per my previous question -- What conditions cause NetworkStream.Write to block? -- you cannot rely on .Write throwing an exception in order to determine that data is not being sent properly.

Community
  • 1
  • 1
David Pfeffer
  • 38,869
  • 30
  • 127
  • 202
  • 3
    You need to work from the *promise* that TCP is a reliable protocol that ensures what you write does indeed end up on the other end. Everything around it is designed around that guarantee. If an earth quake dumps California into the Pacific then you'll find out about it when you Close(). – Hans Passant Oct 05 '11 at 21:10
  • That's a good point w.r.t. `Close()`. However, the purpose of ACK to let the system make its own decision about whether the connection is dead or not, and to retransmit if it believes it has not been long enough to be dead. – David Pfeffer Oct 05 '11 at 21:13
  • Like Hans said, assume your packets do make it. If an exception happens, *then* determine where you left off. Data that didn't make it is the corner case and should always cause an exception. Just make sure you have a way to pick-up where you left off. – Bengie Oct 05 '11 at 21:20
  • Oh, let me clarify. I don't care about "picking up where I left off." I just need to know if the connection fails ASAP, not in X minutes when the OS finally decides it died. So, I want to handle the lack-of-ACK as a sign of a failed connection after perhaps 30 seconds. – David Pfeffer Oct 05 '11 at 21:25
  • 2
    Great low-level question. :) Any way I could suggest sending a small UDP packet back/forth every 3 sec, and then you can assume no response in 10 sec = connection lost (or high traffic)? (Would happen in parallel to your main TCP connection, and ideal for LAN or configurable internet. Firewalls,proxies, commercial environment, forget about it). – Kieren Johnstone Oct 05 '11 at 21:31
  • I'd actually do the entire protocol (which I authored) over UDP, which would make more sense. Unfortunately, Windows Azure, where my server is hosted, lacks UDP support at the moment. – David Pfeffer Oct 05 '11 at 21:43
  • @DavidPfeffer Did you ever resolve this? I'm in a very similar situation, but unfortunately since I'm talking to a microcontroller on the other end and have no access to the firmware, I cannot change the protocol. – Matt Kline Jan 03 '14 at 19:36
  • No, instead (re: my Oct 5 '11 21:43 comment) Azure started supporting UDP. I'm still extremely dissatisfied though that this wasn't solved. I would imagine there's a way to do this using Windows API calls. – David Pfeffer Jan 04 '14 at 02:11

4 Answers4

6

There is mention of a "user timeout" in some IETF RFCs (5482 793) which does what is being asked for.

Some other operating systems support this as a socket option but not Windows unfortunately.

Without this option, the only ways to reduce the time until abort in this scenario would be to reduce the number of retransmission attempts, or reduce the initial RTT.

On Windows the former can be controlled (machine wide..) via netsh/registry: Tcp Max Data Retransmissions.

Is it feasible to just abandon the current connection via your own timeout, and make another if required?

  • The applications would have to establish when the connection is to be abandoned - possibly some "time to live" established at the start of the TCP conversation, based on time of inactivity or effective data rate
  • There would a bit of data overhead due to the retransmits from the old connection
  • The server app may need changed to accept more than one concurrent conection
  • This process should not be repeated indefinately by the client in case the network never achieves quite enough speed for your timeout
Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
  • While it's not the answer I wanted to hear ("It can't be done/can only be done on a system-wide level"), thanks for your research effort and offering additional suggestions. – Matt Kline Jan 10 '14 at 17:09
  • It can be done... [but not on Windows](http://stackoverflow.com/questions/5907527/application-control-of-tcp-retransmission-on-linux). – Peter Wishart Jan 11 '14 at 12:27
4

This is an old question, but it hits home with me... As alluded to in your original question, this should be done at the application layer.

I'm hoping my experience may be helpful as I had the exact same thoughts as you (and even fought with other developers on my team over this insisting TCP should get the job done). In reality its quite easy to mess up TCP with wireless connections, conflicting network MTUs and sometimes poorly implemented routers/access points which ACK prematurely or during failure conditions. But also because TCP is intended to stream from one source to one destination, not really to ensure full-duplex transacted communications.

I spent a number of years working for an embedded device manufacturer and wrote a complete client-server system for wireless barcode terminals in a warehouse. Not cellular in this case, but wifi can be just as bad (but even WiFi will prove the desired task useless). FYI, my system is still running reliably in production today after almost 7 years, so I think my implementation is reasonably robust (it experiences regular interference from industrial manufacturing machines/welders/air compressors/mice chewing network wires, etc).

Understanding the problem

@rodolk has posted some good info. TCP level ACKs do not necessarily correspond 1-1 with each of your application network transmissions (and will invariably NOT be 1-1 if you send more than the network's MTU or maximum packet size even if Nagle is disabled).

Ultimately the mechanisms of TCP & IP (Transport and Network layers) are to ensure delivery of your traffic in one direction (from source to destination) with some limits on maximum retries/etc. Application communication is ultimately about full duplex (two-way) Application layer communications that sit on top of TCP/IP. Mixing those layers is not a good strategy. Think of HTTP request-response on top of TCP/IP. HTTP does not rely on TCP ACKS to implement its own time outs, etc. HTTP would be a great spec to study if you are interested.

But let's even pretend that it was doing what you want. You always send less than 1 MTU (or max packet size) in 1 transmission and receive exactly 1 ACK. Introduce your wireless environment and everything gets more complex. You can have a failure between the successful transmission and the corresponding ACK!

The problem is that each direction of the wireless communication stream is not necessarily of equal quality or reliability and can change over time based on local environmental factors and movement of the wireless device.

Devices often receive better than they can transmit. It is common for the device to receive your transmission perfectly, reply with some kind of "ACK" which is transmitted, but that wireless ACK never reaches its destination due to signal quality, transmission distance, RF interference, signal attenuation, signal reflection, etc. In industrial applications this could be heavy machinery turning on, welding machines, fridges/freezers, fluorescent lighting, etc. In urban environment it could be mobility within structures, parking garages, steel building structures, etc.

At what point in this scenario does the client take action (save/commit data or change state) and at what point does the server consider the action successful (save/commit data or change state)? This is very difficult to solve reliably without additional communication checks in your application layer (sometimes including 2-way ACK for transactions ie: client transmits, server ACKS, client ACKS the ACK :-) You should not rely on TCP level ACKs here as they will not reliably equate to successful full duplex communication and will not facilitate a reliable retry mechanism for your application.

Application layer technique for unreliable wireless communications on embedded devices

Our technique was that every application level message was sent with a couple byte application level header that included a packet ID # (just an incrementing integer), the length of the entire message in bytes and a CRC32 checksum for the entire message. I can't remember for sure, but I believe we did this in 8 bytes, 2 | 2 | 4. (Depending on the maximum message length you want to support).

So let's say you are counting inventory in the warehouse, you count an item and count 5 units, the barcode terminal sends a message to the server saying "Ben counted 5 units of Item 1234". When the server receives the message, it would wait until it received the full message, verify the message length first, then CRC32 checksum (if the length matched). If this all passed we sent back an application response to this message (something like an ACK for the application). During this time the barcode terminal is waiting for the ACK from the server and will retransmit if it doesn't hear back from the server. If the server receives multiple copies of the same packet ID it can de-duplicate by abandoning uncommitted transactions. However if the barcode scanner does receives its ACK from the server, it would then reply with one more final "COMMIT" command to the server. Because the first 2 messages just validated a working full duplex connection, the commit is incredibly unlikely to fail within this couple ms timeframe. FYI, this failure condition is fairly easy to replicate at the edge of your WiFi coverage, so take your laptop/device and go for a walk until the wifi is just "1 bar" or the lowest connection speed often 1 mbps.

So you are adding 8 bytes header to the beginning of your message, and optionally adding one extra final COMMIT message transmission if you require a transacted request/response when only one side of the wireless communication might fail.

It will be very hard to justify saving 8 bytes per message with a complex application layer to transport layer hooking system (such as hooking into winpcap). Also you may or may not be able to replicate this transport layer hooking on other devices (maybe your system will run on other devices in the future? Android, iOS, Windows Phone, Linux, can you implement the same application layer communication for all these platforms? I would argue you should be able to implement your application on each device regardless of how the TCP stack is implemented.)

I'd recommend you keep your application layer separate from the transport and network layers for good separation of concerns, and tight control over retry conditions, time-outs and potentially transacted application state changes.

BenSwayne
  • 16,810
  • 3
  • 58
  • 75
  • Re: "It will be very hard to justify saving 8 bytes per message with a complex application layer to transport layer hooking system (such as hooking into winpcap)." -- In my circumstance, total payload is 14 bytes, which would mean this would increase the payload by more than 50%. Also, given the tiny payload size, its unlikely that this will ever result in fragmentation. – David Pfeffer Jan 10 '14 at 18:47
  • @BenSwayne: Let me add some comments. 1) Not only for fragmented IP datagrams is that one message could be sent in N datagrams. Other case could be because the counterpart TCP stack is advertising a TCP window size smaller than the data your application is trying to send. (BTW, I'm assuming the complete message is passed to kernel with only one write/send function call). 2) Regarding the CRC32, it should not be necessary because you also have TCP checksum, for the app-to-app communication, and 802.11's Frame Check Sequence (FCS), for Wi-Fi links, doing that job. – rodolk Jan 10 '14 at 19:46
  • @DavidPfeffer: I'm sure you have your reasons trying to avoid app layer ACK but depending on the nature of your application, you could implement it with only 1 Byte (if you have stop-and-wait kind of app, or 2/3 Bytes to send an ACK and the sequence/message number the server is ack'ing) in a response message. Now, if you don't care about portability I'd give a try to winpcap in Windows or pcap in Linux. – rodolk Jan 10 '14 at 19:53
  • Thanks for such a detailed answer. I meant to give you the bounty, but unfortunately wasn't able to make it online the day it expired. Unfortunately, I don't have any control over the application layer without adding additional hardware, as the wireless modem dumps the data it receives straight into the device, whose firmware I do not control. – Matt Kline Jan 13 '14 at 14:57
3

I'm not a C# expert but I think I can help respond. You are trying to have TCP-layer control data from the application. This is not easy and as with any application layer protocol you would need some kind of application layer response like Request-Response in HTTP.

The problem with knowing that ALL your written data was actually received by the other end is that TCP is stream oriented. That means that you might send 1KB of data through the socket, that KB is stored in a TCP snd buffer, and that KB might be sent with 3 TCP segments that may be acknowledged (TCP ACK) altogether or separately. It's asynchronous. So, at some point TCP might have sent only 300 Bytes of your 1,000 KB of data, just an example.

Now the other question is whether you open the connection and close the connection every time you send a chunk of data (A) or you have the connection always open (B).

In (A) it is simpler because, if the connection fails opening, that's it. It might take more than one minute to have the timeout but you don't send more than a few 20-Byte IP and (20-Byte) TCP headers (sometimes more than 20 Bytes for IP and TCP options).

In (B) you will realize of success or failure when you want to send data. There are 3 cases I would consider:

1-The other end of the socket closed or reset the TCP connection. In that case you should immediately receive and error response or, in C, a signal indicating broken pipe, and I suppose it'll become an exception in C#.

2-The other end becomes unreachable and hasn't closed/reset the socket. This is difficult to detect because TCP will send messages that will time out and after a few retry/timeouts it will decide the connection is broken. The time for timeout and number of retries may be configurable but at OS level (for all applications). I don't think you can configure that by socket. In this case your application won't realize at the moment it sends the data.

3-Data was successfully received by the other end and acknowledged at TCP layer.

The complex part is to differentiate between (2) and (3) as fast as possible. I will assume you are asking about this. I don't think there is any possibility of doing it completely unless you hack the kernel.

Anyway, getting an ACK from the server at application layer could mean just 1 or 2 Bytes telling the amount of data received. That in addition to 20+20 Bytes for IP and TCP basic headers.

If there is any possibility of doing what you say, I would try this but I have never tested:

You can play with the send buffer size and select function. You can set the send buffer size of a socket with setsockopt and OS_SNDBUF socket option. http://msdn.microsoft.com/en-us/library/system.net.sockets.socket_methods(v=vs.110).aspx

If you know you are always going to send 2 KB, you set the send buffer size at 2 KB. Usually you can change it only after connecting. http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.sendbuffersize(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

Then you call Select or Poll method on the Socket to check if it is writable.

As long as one TCP message is acked, Select or Poll should indicate the socket is writable because the sent data is removed from the send buffer.

Note this algorithm has limitations:

  1. The operating system could define a minimum buffer size.
  2. If the algorithm is possible, Select and Poll will tell you the socket is writable when there is buffer space available but only one part of you data was actually received and ACKED by the other end.
  3. If you send variable-size messages this is not possible.

If you cannot apply the mentioned algorithm you might need to pay the extra cost of an additional TCP message with ~42 Bytes with an application-layer simple ACK.

Sorry for not being able to provide a definitive solution. Maybe OS's should implement the capability to tell you available buffer Bytes and that would solve your problem.

EDIT: I'm adding another suggestion from my comments.

If you have the possibility of having other process using Winpcap, you could capture TCP responses from the other end!!! For example, using local IPC like shared memory or just sockets one application can tell the other about the data of the socekt (src IP, src port, dst IP, dst port). The other second process, call it monitoring process, can detect an ACK received from the other endpoint by sniffing the connection. Also winpcap could be used linking to native code ...

rodolk
  • 5,606
  • 3
  • 28
  • 34
  • I'm not sure why you are focused on bytes in the buffer. Can you explain more? The TCP send buffer is expected to be transmitted immediately (assuming Nagle's algorithm is disabled) and it is really there mainly both as a cushion in case you are overwhelming the network connection's bandwidth and also to enable retransmission of dropped packets. The issue here isn't that we do not know that the packet was sent -- it was sent. The issue here is that we do not know if the peer received the bytes, or if some other bad thing happened. – David Pfeffer Jan 08 '14 at 21:51
  • 1
    I'm also assuming Nagle is disabled. Said that, let me disagree with you. The data is not necessarily sent immediately. When the write/send function returns, it means only that application data was written in the TCP send buffer. And also it is possible that only part of the data is sent in one TCP segment and and the rest in other TCP segments. This is very common. After that, TCP expects all (1 or n) segments are ACKed. Only after an ACK is received for a chunk of data sent in one segment, those Bytes are removed from the TCP snd buffer. – rodolk Jan 09 '14 at 00:09
  • 1
    Continuing with my response: TCP removes the Bytes from the snd buffer after the corresponding ACK was receives because if there is a timeout TCP has to resend. There is no way for you in app layer to know about the ACK's received. So, the only workaround I could think about is that you play with the data in the snd buffer. – rodolk Jan 09 '14 at 00:12
  • 1
    Continuing: maybe there is another workaround. Do you have the possibility of having other process using Winpcap? In that case you could capture TCP responses from the other end!!! For example, using local IPC like shared memory or just sockets one application can tell the other about the data of the socekt (src IP, src port, dst, IP, dst port). The other second process, call it monitoring process, can detect an ACK received from the other endpoint by sniffing the connection. – rodolk Jan 09 '14 at 00:17
-3

You can use TcpClient.SendTimeout to do this. It causes write operations to throw a SocketException if the specified timeout expires before the operation completes successfully.

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.sendtimeout.aspx

Also, see this page for more info on how to set up sockets with more customisable and reliable timeouts:

http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx

Polynomial
  • 27,674
  • 12
  • 80
  • 107
  • 1
    `.SendTimeout` will just set the timeout for the `Write` call. As mentioned in the question, you can't rely on that to determine if packets are ACK'd. – David Pfeffer Oct 05 '11 at 21:03