web-dev-qa-db-ja.com

LinuxでのTCP再送信のアプリケーション制御

せっかちな人向け:

setsockopt()ioctl()などを使用して、Linuxの単一接続の_/proc/sys/net/ipv4/tcp_retries2_の値を変更する方法、またはそれは可能ですか?

長い説明:

長いポーリングHTTPリクエストを使用するアプリケーションを開発しています。サーバー側では、クライアントが接続を閉じたときにそれを知る必要があります。精度は重要ではありませんが、15分にはなり得ません。 1分近ければ問題ありません。

概念に慣れていない人のために、長いポーリングHTTPリクエストは次のように機能します。

  • クライアントはリクエストを送信します
  • サーバーはHTTPヘッダーで応答しますが、応答は開いたままにします。チャンク転送エンコードが使用され、サーバーはデータが利用可能になったときにビットを送信できます。
  • すべてのデータが送信されると、サーバーは「クローズチャンク」を送信して、応答が完了したことを通知します。

私のアプリケーションでは、サーバーは時々「ハートビート」をクライアントに送信します(デフォルトでは30秒)。ハートビートは、応答チャンクとして送信される単なる改行文字です。これは、接続が失われたことを通知できるように、回線をビジーに保つことを目的としています。

クライアントが正しくシャットダウンしても問題はありません。しかし、強制的にシャットダウンした場合(たとえば、クライアントマシンの電源が切れた場合)、TCPリセットは送信されません。この場合、サーバーはクライアントが送信しないハートビートを送信しますACK:この後、サーバーはあきらめてアプリケーション層(HTTPサーバー)に障害を報告してから約15分間パケットを再送信し続けます。

_/proc/sys/net/ipv4/_の次のファイルに書き込むことで、再送信時間を制御できます。

_tcp_retries1 - INTEGER
    This value influences the time, after which TCP decides, that
    something is wrong due to unacknowledged RTO retransmissions,
    and reports this suspicion to the network layer.
    See tcp_retries2 for more details.

    RFC 1122 recommends at least 3 retransmissions, which is the
    default.

tcp_retries2 - INTEGER
    This value influences the timeout of an alive TCP connection,
    when RTO retransmissions remain unacknowledged.
    Given a value of N, a hypothetical TCP connection following
    exponential backoff with an initial RTO of TCP_RTO_MIN would
    retransmit N times before killing the connection at the (N+1)th RTO.

    The default value of 15 yields a hypothetical timeout of 924.6
    seconds and is a lower bound for the effective timeout.
    TCP will effectively time out at the first RTO which exceeds the
    hypothetical timeout.

    RFC 1122 recommends at least 100 seconds for the timeout,
    which corresponds to a value of at least 8.
_

_tcp_retries2_のデフォルト値は確かに8であり、15分(900秒)の再送信の私の経験は、上記で引用したカーネルのドキュメントに沿っています。

たとえば、_tcp_retries2_の値を5に変更すると、接続はより速く切断されます。しかし、このように設定すると、システム内のすべての接続に影響するため、この1つの長いポーリング接続のみに設定したいのです。

RFC 1122からの引用:

_4.2.3.5  TCP Connection Failures

   Excessive retransmission of the same segment by TCP
   indicates some failure of the remote Host or the Internet
   path.  This failure may be of short or long duration.  The
   following procedure MUST be used to handle excessive
   retransmissions of data segments [IP:11]:

   (a)  There are two thresholds R1 and R2 measuring the amount
        of retransmission that has occurred for the same
        segment.  R1 and R2 might be measured in time units or
        as a count of retransmissions.

   (b)  When the number of transmissions of the same segment
        reaches or exceeds threshold R1, pass negative advice
        (see Section 3.3.1.4) to the IP layer, to trigger
        dead-gateway diagnosis.

   (c)  When the number of transmissions of the same segment
        reaches a threshold R2 greater than R1, close the
        connection.

   (d)  An application MUST be able to set the value for R2 for
        a particular connection.  For example, an interactive
        application might set R2 to "infinity," giving the user
        control over when to disconnect.

   (e)  TCP SHOULD inform the application of the delivery
        problem (unless such information has been disabled by
        the application; see Section 4.2.4.1), when R1 is
        reached and before R2.  This will allow a remote login
        (User Telnet) application program to inform the user,
        for example.
_

Linuxの_tcp_retries1_と_tcp_retries2_はRFCの_R1_と_R2_に対応しているように思えます。 RFCは、(項目d)で、準拠する実装では_R2_の値の設定を許可する必要があることを明確に述べていますが、setsockopt()ioctl()を使用してそれを行う方法は見つかりませんでしたまたはそのような。

別のオプションは、_R1_を超えたときに通知を受け取ることです(項目e)。これは_R2_の設定ほど良くありませんが、_R1_はすぐに(数秒で)ヒットし、_R1_の値は接続ごとに設定できないと思います。少なくともRFCはそれを必要としません。

29
Petri Lehtinen

これはカーネル2.6.37で追加されたようです。 Commit diff カーネルGitからの抜粋 change log 以下からの抜粋;

コミットdca43c75e7e545694a9dd6288553f55c53e2a3a3著者:Jerry Chu日付:Fri Aug 27 19:13:28 2010 +0000

tcp: Add TCP_USER_TIMEOUT socket option.

This patch provides a "user timeout" support as described in RFC793. The
socket option is also needed for the the local half of RFC5482 "TCP User
Timeout Option".

TCP_USER_TIMEOUT is a TCP level socket option that takes an unsigned int,
when > 0, to specify the maximum amount of time in ms that transmitted
data may remain unacknowledged before TCP will forcefully close the
corresponding connection and return ETIMEDOUT to the application. If 
0 is given, TCP will continue to use the system default.

Increasing the user timeouts allows a TCP connection to survive extended
periods without end-to-end connectivity. Decreasing the user timeouts
allows applications to "fail fast" if so desired. Otherwise it may take
upto 20 minutes with the current system defaults in a normal WAN
environment.

The socket option can be made during any state of a TCP connection, but
is only effective during the synchronized states of a connection
(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, or LAST-ACK).
Moreover, when used with the TCP keepalive (SO_KEEPALIVE) option,
TCP_USER_TIMEOUT will overtake keepalive to determine when to close a
connection due to keepalive failure.

The option does not change in anyway when TCP retransmits a packet, nor
when a keepalive probe will be sent.

This option, like many others, will be inherited by an acceptor from its
listener.

Signed-off-by: H.K. Jerry Chu <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
24
Kimvais

Kimvais で説明されている_TCP_USER_TIMEOUT_ソケットオプションが利用可能な場合は、それを使用することをお勧めします。そのソケットオプションが存在しない古いカーネルでは、SIOCOUTQioctl()を繰り返し呼び出して、ソケット送信キューのサイズを決定できます-送信キューがタイムアウトしても減少しない場合これは、ACKが受信されていないことを示し、ソケットを閉じることができます。

13
caf

少し考えて(そしてグーグルで)、あなたは変更できないという結論に達しましたtcp_retries1およびtcp_retries2何らかのパッチをカーネルに適用しない限り、単一ソケットの値。それはあなたにとって実現可能ですか?

それ以外の場合は、TCP_KEEPALIVEソケットオプションは、接続がまだアクティブかどうかを確認することを目的としています(これはまさにあなたが達成しようとしていることなので、理にかなっています)。デフォルトでは約2時間後に切断されるため、デフォルトのパラメーターを少し調整する必要があることに注意してください!!!

3
Simone

これは私の理解のためです。 tcp_retries2は、接続を削除する前にシステムによって許可される再送信の数です。したがって、送信されたデータが未確認のままになる最大時間を指定するTCP_USER_TIMEOUTを使用してtcp_retries2のデフォルト値を変更する場合は、 TCP_USER_TIMEOUTが正しいですか?

その場合、conctionはより長い時間待機し、データパケットを再送信しません。何かが間違っている場合は、私を修正してください。

0
Rndp13