LinuxのTCP実装について質問です。


想定環境
サーバ
192.168.0.10
apache
FedoraCore 4

クライアント
192.168.0.11

上記環境にて以下のことを行います。
1.クライアントがサーバの80番ポートに接続します。
2.クライアントからHTTPデータを一切送りません。クライアントでは、以下のコマンドを実行しただけの状態。
% telnet 192.168.0.10 80


上記の状態のtcpdumpを見てみると以下のようになります。(適宜改行を入れてあります)
http://d.hatena.ne.jp/matsubobo/20070704

■質問
3-way handshakeが終了した後にサーバが一定期間ごとにSYN+ACKを送信することは
正しい実装なのでしょうか?



このようなTCP実装をしている場合、大量のソケットがオープンされてしまい、
サーバのリソースを食ってしまうことが心配事です。

SYN+ACKを再送している間、(18:39:59.729946以降の通信)
サーバのソケットはSYN_RECV状態になってしまいます。


よろしくお願いいたします。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2007/07/11 19:00:21
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答1件)

id:b-wind No.1

回答回数3344ベストアンサー獲得回数440

ポイント60pt

サーバが一定期間ごとにSYN+ACKを送信することは

正しい実装なのでしょうか?

ダンプ情報を見る限り、シーケンス番号が全て同一になっています。

つまり何らかの原因で、3-way handshake が完了せず、パケットの再送を行っているようです。

Manpage of TCPDUMP

-S コマンドで ACK パケットの本来のシーケンス番号が分かるので、きちんと返しているか確認するところからはじめてはどうでしょうか。


TELNET コマンドは色々と機能が有るのでこの目的には適していないように思います。

id:matsubobo

最初は私もそう思いましたが、3-way-handshakeは完了しているようです。

サーバがSYN+ACKの再送と、クライアントがそれに応答する。という繰り返し中にtelnetで文字を入力したら、サーバへ送信されます。

telnetで1文字でも入力すれば上記のような SYN+ACKのやりとりは行われません。

tcpdumpの-Sオプションありがとうございます。このオプションを使って試した結果、何か判明しましたら報告させていただきます。

2007/07/05 00:40:21
  • id:matsubobo
    質問が言葉足らずでした。。。正確には以下です。


    TCPの3-way handshakeが終了した後にクライアント側からパケットがこない場合に、サーバ側から一定期間ごとにSYN+ACKをクライアントへ送信することは正しいTCPの実装なのでしょうか?
  • id:b-wind
    >サーバがSYN+ACKの再送と、クライアントがそれに応答する。
    クライアントの応答をサーバーが受け取っていないので再送する事になるのでは?
    原因がどちらかは分かりませんが。

    >telnetで1文字でも入力すれば上記のような SYN+ACKのやりとりは行われません。
    telnet コマンドの仕様にも左右されるので自作のシンプルなプログラムのほうが検証にはいいと思う。
  • id:dev_zer0
    TCPのkeep aliveパケットという可能性は?
    ちなみにTCPではtcp_keepalive_intvlが設定できます。
    http://www.linux.or.jp/JM/html/LDP_man-pages/man7/tcp.7.html

    なお、それがTCPの実装として正しいかどうかはRFC793を見てみるべきですが面倒なので見てません。
    http://www.ietf.org/rfc/rfc793.txt
  • id:matsubobo
    >dev_zez0さん
    コメントありがとうございます。
    tcp_keepalive周りを調べましたが、今回の問題には関係のない数値のようです。

    その名の通りkeepaliveなので、ある一定期間TCPが無通信の場合に接続先へprobeパケットを送る仕様のようです。
    http://vce.ce-lab.net/doc/vce/ja/linuxtune.html

    また、OSの数値と今回の現象の、タイムアウト値などで関連する数値がないようです。
    [/proc/sys/net/ipv4]% head tcp_keepalive_*
    ==> tcp_keepalive_intvl <==
    75

    ==> tcp_keepalive_probes <==
    9

    ==> tcp_keepalive_time <==
    7200

  • id:matsubobo
    tcpdumpコマンドに-Sオプションをつけて実行してみましたが、新たな発見はありませんでした。

    18:26:01.063285 IP 192.168.0.11.55718 > 192.168.0.10.http: S 3519083444:3519083444(0) win 5840 <mss 1460,sackOK,timestamp 845727479 0,nop,wscale 5>
    18:26:01.063853 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636600923 845727479,nop,wscale 9>
    18:26:01.063338 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845727479 1636600923>

    18:26:04.059577 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636601673 845727479,nop,wscale 9>
    18:26:04.059690 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845728228 1636601673,nop,nop,sack sack 1 {51746706:51746707} >

    18:26:10.059957 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636603173 845728228,nop,wscale 9>
    18:26:10.060069 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845729728 1636603173,nop,nop,sack sack 1 {51746706:51746707} >

    18:26:22.060691 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636606173 845729728,nop,wscale 9>
    18:26:22.060810 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845732728 1636606173,nop,nop,sack sack 1 {51746706:51746707} >

    18:26:46.262211 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636612223 845732728,nop,wscale 9>
    18:26:46.262329 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845738779 1636612223,nop,nop,sack sack 1 {51746706:51746707} >

    18:27:34.265237 IP 192.168.0.10.http > 192.168.0.11.55718: S 51746706:51746706(0) ack 3519083445 win 5792 <mss 1460,sackOK,timestamp 1636624223 845738779,nop,wscale 9>
    18:27:34.265361 IP 192.168.0.11.55718 > 192.168.0.10.http: . ack 51746707 win 183 <nop,nop,timestamp 845750779 1636624223,nop,nop,sack sack 1 {51746706:51746707} >

  • id:b-wind
    やはり 3-way handshake は(少なくともサーバー側にとって)完了していないと見るのが適切なような。

    なお、port 番号が同一の為、
    >大量のソケットがオープンされてしまい、
    と言うのは当てはまりません。
    全て同一のコネクションです。
  • id:matsubobo
    確かに、passive open側にとっては3 way handshakeが適切に終了していないと認識されております。

    しかしなぜ、passive open側がそのように認識し、SYN+ACKを再送しているのかが疑問です。
    (投稿した最初の質問と同様)

    パケットのダンプを見ると、3-way-handshakeは適切に終了しております。

    >>大量のソケットがオープンされてしまい、
    >と言うのは当てはまりません。
    大量のソケットではなく、大量のコネクションと表現した方が正しかったかもしれません。

    例:
    上記のやりとりが増えると、サーバ側のnetstatで以下のようになります。

    tcp 0 0 192.168.0.10:80 192.168.0.20:21743 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:21658 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:21082 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:1249 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:13585 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:28989 SYN_RECV
    tcp 0 0 192.168.0.10:80 192.168.0.20:12056 SYN_RECV
    ......
  • id:b-wind
    >サーバ側のnetstatで以下のようになります。
    それぞれクライアント側のポートが違っているのはなぜですか?
    それを知る為には tcpdump の結果が足りていないように思います。

    自分の環境では再現していないので特定の実装のバグのようにも思います。
  • id:matsubobo
    > それぞれクライアント側のポートが違っているのはなぜですか?
    TCPの仕様によるものです。
    PCでWebを閲覧している際に、
    % netstat -an
    などとしてみると確認できます。

  • id:qpSHiNqp
    かなり昔のご質問なので解決済みかとは思いますが、下記の記事にある"TCP_DEFER_ACCEPT"が関係しているかもしれません。
    http://d.hatena.ne.jp/kazuhooku/20100327/1269682361

    プラクティカルには、AWS ELBのようにトラフィックのバーストに備えて常に「現在の接続数」+αのスペアのTCPコネクションをバックエンドのApache等との間に確立させて置くような場合に発生するようです。
    例えば、Apacheのconfigで
    AcceptFilter http none
    AcceptFilter https none
    と言うような設定を入れるとSYN+ACK再送はなくなるかと思います。
    今更ながら、Apacheの挙動を追っていて発見しました。ご参考までにコメントを投稿させていただきました。
  • id:matsubobo
    @qpSHiNqp
    まさに、`TCP_DEFER_ACCEPT` です!ありがとうございます!!

    TCP_DEFER_ACCEPT自体は正常な状態では良いアプローチですが、今回みたいなケースでは微妙になりますね。

    結果として無くなったので、良かったかな。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません