読者です 読者をやめる 読者になる 読者になる

nigakyのブログ

雑多なメモです。

TCP Fast Open を試してみる

Linux カーネル 3.6 では TCP Fast Open (TFO) という機能がマージされたそうです。詳しくは以下の URL に記載がありますが、一度接続したクライアントは TCP の 3way-handshake を簡略化してコネクションをオープンできる(SYN にデータを載せられる)という機能のようです。

TCP Fast Open: expediting web services [LWN.net]

面白そうな機能なので実際に使ってみて、使い方をまとめてみました。

準備

カーネル

kernel 3.6 ではまだクライアント側の機能しかマージされていないので、さらに新しいカーネルを使います(3.7 でサーバ側の機能もマージされるようです)。今回は git で取ってきた最新 (12250d843e8489ee00b5b7726da855e51694e792) をビルドして使いました。

TFO の有効化

TFO はデフォルトでオフになっているため、以下のパラメータで有効化します。

 # echo 3 > /proc/sys/net/ipv4/tcp_fast_open

TFO を利用したソケットプログラム

TFO を使うにはサーバ側、クライアント側それぞれで配慮が必要です。

具体的には

  • サーバ側はソケットオプション TCP_FASTOPEN (23) をセットする
  • クライアント側は connect(2)/send(2) の代わりに MSG_FASTOPEN (0x20000000) フラグをつけた sendto(2) を使う

以下の様なテストプログラムを使いました。
クライアントは(TFO Cookies をもらうため)一度通常のコネクションで通信し、二回目に TFO を使って通信しています。

#!/usr/bin/env python

import sys, socket

PORT = 8000

TCP_FASTOPEN = 23
MSG_FASTOPEN = 0x20000000

def server():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_TCP, TCP_FASTOPEN, 5) # set TCP_FASTOPEN option
    s.bind(("", PORT))
    s.listen(5)

    while True:
        conn, addr = s.accept()
        ret = conn.recv(1024)
        while len(ret) > 0:
            print ret
            ret = conn.recv(1024)
        conn.close()
    s.close()

def client():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], PORT))
    s.sendall("1. First connection.")
    s.close()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # sendto with MSG_FASTOPEN flag
    s.sendto("2. Sending data by using TCP fast open!!!", MSG_FASTOPEN, (sys.argv[1], PORT))
    s.close()

def main():
    if '-s' in sys.argv:
        server()
    else:
        client()


if __name__ == '__main__':
    main()

試してみる

今回はサーバ/クライアントを一台のループバックでやっています。

サーバ側

# ./TFO_test.py -s
1. First connection.
2. Sending data by using TCP fast open!!!

クライアント側

# ./TFO_test.py localhost

これだけ見てもよく分からないので tcpdump も見てみます。
(TFO を使ってコネクションを確立している部分のダンプです。)

ポイントは

  • クライアントは SYN パケットに TFO Cookies とデータを載せて送信している
  • SYN パケットにデータが載っているため、今回のテストプログラムでは 3way-handshake 終了後に即 FIN が送られてソケットがクローズされている(データを別で送らない分パケットのやり取りが通常のコネクションより一回少ない。)
IP localhost.42887 > localhost.irdmi: Flags [S], seq 914856703:914856744, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 0,nop,wscale 6,Unknown Option 254f9896c02881aefd4e7c0], length 41
IP localhost.irdmi > localhost.42887: Flags [S.], seq 2336617123, ack 914856745, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 182325,nop,wscale 6], length 0
IP localhost.42887 > localhost.irdmi: Flags [.], ack 1, win 683, options [nop,nop,TS val 182325 ecr 182325], length 0
IP localhost.42887 > localhost.irdmi: Flags [F.], seq 1, ack 1, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0
IP localhost.irdmi > localhost.42887: Flags [F.], seq 1, ack 2, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0
IP localhost.42887 > localhost.irdmi: Flags [.], ack 2, win 683, options [nop,nop,TS val 182326 ecr 182326], length 0

たぶんこれで TFO が使えていると思います。
netstat で見ると以下のカウンタも上がっていました。

# netstat -s | grep TCPFastOpen
    TCPFastOpenActive: 7
    TCPFastOpenPassive: 9
    TCPFastOpenCookieReqd: 2

これが Web で使われるようになれば応答性が良くなって幸せになれそうですね。