TCP_DEFER_ACCEPT

mod_mysql についてはちょっと作業メモ的なチュートリアルを書こうと思ってブログをはじめたので、実装はまあまあ書けてしまっているんですが一点ものすごくはまったのがこれでした。

サーバに繋げても、なにかパケットを送信しない限り server greeting が帰ってこない。mod_smtpd でも同様。一体なぜだ!と必死に strace してやっと見つけたのがこれでした。

bind(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
listen(3, 511)                          = 0
setsockopt(3, SOL_TCP, TCP_DEFER_ACCEPT, [1], 4) = 0
pipe([4, 5])                            = 0

Linux では TCP_DEFER_ACCEPT を setsockopt() で listening socket にセットすると、accept() が最初にパケットが到達するまでブロックするようになるらしい。

http://builder.com.com/5100-6372-1050771.html より:

The server has now created a socket and is waiting for a connection. The connection procedure in TCP/IP is a so-called “three-way handshake.” First, a client sends a TCP packet with a SYN flag set and no payload (a SYN packet). The server replies by sending a packet with SYN/ACK flags set (a SYN/ACK packet) to acknowledge receipt of the initial packet. The client then sends an ACK packet to acknowledge receipt of the second packet and to finalize the connection procedure. After receiving the SYN/ACK, the packet server wakes up a receiver process while waiting for data. When the three-way handshake is completed, the client starts to send “useful” data to be transferred to the server. Usually, an HTTP request is quite small and fits into a single packet. But in this case, at least four packets will be sent in both directions, adding considerable delay times. Note also that the receiver has already been waiting for the information -- since before the data was ever sent.

To alleviate these problems, Linux (along with some other OSs) includes a TCP_DEFER_ACCEPT option in its TCP implementation. Set on a server-side listening socket, it instructs the kernel not to wait for the final ACK packet and not to initiate the process until the first packet of real data has arrived. After sending the SYN/ACK, the server will then wait for a data packet from a client. Now, only three packets will be sent over the network, and the connection establishment delay will be significantly reduced, which is typical for HTTP.

なるほど、大変勉強になりました。てか、ここにも書いてあるじゃん...

http://httpd.apache.org/docs/2.2/ja/mod/core.html より:

AcceptFilter ディレクティブ

Listen しているソケットに対して、OS が固有に持っているプロトコルについての最適化を 有効にするディレクティブです。大前提となる条件は、データが受信されるか HTTP リクエスト全体がバッファされるかするまで、カーネルがサーバプロセスに ソケットを送らないようになっている、ということです。現在サポートされているのは、 FreeBSD の Accept Filter と Linux のプリミティブな TCP_DEFER_ACCEPT のみです。