7

Background

I have an implementation of an HTTP server in C#. Using ab I discovered a weird performance issue. Each request took 5 ms with Keep-Alive Off but 40 ms with Keep-Alive on!

The testpage is generated into a single byte[] which get sent as reply using a single socket.Send call.

The cause is as far as I can tell Nagle's algorithm used in the TCP stack.

TCP Flush?

So far I am using the NoDelay property in the end of every HTTP request served.

socket.NoDelay = true;
socket.NoDelay = false;

Which does solve the problem for now. But I have no documentation to backup my discovery.

This was tested on a linux/mono system.

Is there a standard way of flushing the TCP connection?

Related

This answer is addressing the same issue. The difference here is that I am looking to only temporarily disabling the algorithm.

Community
  • 1
  • 1
hultqvist
  • 17,451
  • 15
  • 64
  • 101

4 Answers4

8

I tested this with Wireshark. Unfortunately,

socket.NoDelay = true;
socket.NoDelay = false;

has no effect. Similarly,

socket.NoDelay = true;
socket.Send(new byte[0]);
socket.NoDelay = false;

also has no effect. From observed behaviour, it appears that the NoDelay property only affects the next call to Send with a non-empty buffer. In other words, you have to send some actual data before NoDelay will have any effect.

Therefore, I conclude that there is no way to explicitly flush the socket if you don’t want to send any extra data.

However, since you are writing an HTTP server, you may be able to use a few tricks:

  • For requests that are served using Transfer-Encoding: chunked, you can send the end-of-stream marker (the "\r\n0\r\n\r\n") with NoDelay = true.
  • If you are serving a file from the local filesystem, you will know when the file ends, so you could set NoDelay = true just before sending the last chunk.
  • For requests that are served using Content-Encoding: gzip, you can set NoDelay = true just before closing the gzip stream; the gzip stream will send some last bits before actually finishing and closing.

I’m certainly going to add the above to my HTTP server now :)

Timwi
  • 65,159
  • 33
  • 165
  • 230
4

If you know the the length of the data you are sending,Setting SendBufferSize will make the socket send the data at once, Following is a working code example:

byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(message);
socket.SendBufferSize = bytesToSend.Length;
socket.Send(bytesToSend);
MrBassam
  • 349
  • 1
  • 5
  • 17
1

Having written a quite popular web server myself I don't think that Nagle algortihm is your real problem.

How do you build your responses and how do you send them?

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • I have updated my question, the data is sent in a single call to Socket.Send(Byte[],,), the content is though rather small 400 bytes. – hultqvist May 03 '11 at 21:16
-3

There is no such thing as flushing in TCP. TCP is a stream based protocol which groups/ungroups/splits/joins your data. By disabling nagle it will only do that less frequent.

Do not disable nagle.

The Nagle TCP/IP algorithm was designed to avoid problems with small packets, called tinygrams, on slow networks. The algorithm says that a TCP/IP connection can have only one outstanding small segment that has not yet been acknowledged. The definition of "small" varies but usually it is defined as "less than the segment size" which on ethernet is about 1500 bytes.

Take a look at here: Disabling TCP/IP Nagle Algorithm Improves Speed on Slow Nets

HABJAN
  • 9,212
  • 3
  • 35
  • 59
  • 3
    Exactly why shouldn't I disable Nagle's algorithm for a short period? It apparently gave me a 8 times improvement in response time. – hultqvist Apr 03 '11 at 11:28
  • Related: [what happened to the TCP Nagle flush](http://stackoverflow.com/questions/6726832/what-happened-to-the-tcp-nagle-flush). I wonder where this idea comes from that TCP flush is meaningless and/or doesn't exist. – Roman Starkov Jul 18 '11 at 13:05
  • Flushing makes sense on streams. And saying that TCP doesn't support flushing is misleading. Most implementations of TCP don't support explicit flushing of their send buffers, but that's not a limitation of TCP, that's a limitation of these implementations. And there are scenarios where a flush to avoid nagle at selected points would be very useful. – CodesInChaos Jul 18 '11 at 22:16