web-dev-qa-db-fra.com

Contrôle d'application de TCP retransmission sur Linux

Pour les impatients:

Comment changer la valeur de /proc/sys/net/ipv4/tcp_retries2 pour une seule connexion sous Linux, en utilisant setsockopt(), ioctl() ou autre, ou est-ce possible?

Description plus longue:

Je développe une application qui utilise des requêtes HTTP à interrogation longue. Du côté du serveur, il faut savoir quand le client a fermé la connexion. La précision n’est pas critique, mais elle ne peut certainement pas durer 15 minutes. Plus près d'une minute ferait l'affaire.

Pour ceux qui ne connaissent pas le concept, une requête HTTP à interrogation longue fonctionne comme suit:

  • Le client envoie une demande
  • Le serveur répond avec des en-têtes HTTP, mais laisse la réponse ouverte. Le codage de transfert en bloc est utilisé, ce qui permet au serveur d’envoyer des bits de données dès qu’ils sont disponibles.
  • Lorsque toutes les données sont envoyées, le serveur envoie un "morceau de fermeture" pour signaler que la réponse est complète.

Dans mon application, le serveur envoie des "pulsations" au client de temps en temps (30 secondes par défaut). Une pulsation est simplement un caractère de nouvelle ligne qui est envoyé en tant que morceau de réponse. Cela a pour but de garder la ligne occupée afin que nous puissions notifier la perte de connexion.

Il n'y a pas de problème lorsque le client s'arrête correctement. Mais lorsqu'il est mis hors tension avec force (la machine cliente perd son alimentation, par exemple), une réinitialisation TCP n'est pas envoyée. Dans ce cas, le serveur envoie une pulsation que le client n'accepte pas. Après cela, le serveur retransmet le paquet pendant environ 15 minutes après l'abandon et le signalement de l'échec à la couche application (notre serveur HTTP). Et 15 minutes, c'est trop long à attendre dans mon cas.

Je peux contrôler le temps de retransmission en écrivant dans les fichiers suivants dans /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.

La valeur par défaut de tcp_retries2 est bien de 8 et mon expérience de retransmission de 15 minutes (900 secondes) est conforme à la documentation du noyau citée ci-dessus.

Si je change la valeur de tcp_retries2 en 5 par exemple, la connexion meurt beaucoup plus rapidement. Mais le définir de la sorte affecte toutes les connexions du système, et j'aimerais vraiment le configurer uniquement pour cette connexion à interrogation longue.

Une citation de la 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.

Il me semble que tcp_retries1 et tcp_retries2 sous Linux correspondent à R1 et R2 dans le RFC. La RFC indique clairement (au point d) qu’une implémentation conforme DOIT permettre de définir la valeur R2, mais je n’ai trouvé aucun moyen de le faire en utilisant setsockopt(), ioctl() ou autre.

Une autre option serait d’avoir une notification lorsque R1 est dépassé (élément e). Ce n'est pas aussi bon que de définir R2, cependant, car je pense que R1 est touché très rapidement (dans quelques secondes) et que la valeur de R1 ne peut pas être définie par connexion, ou du moins la RFC ne l'exige pas.

26
Petri Lehtinen

On dirait que cela a été ajouté dans le noyau 2.6.37 . Valider diff du noyau Git et extrait de change log below;

commettre dca43c75e7e545694a9dd6288553f55c53e2a3a3 Auteur: Jerry Chu Date: vendredi 27 août 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

Je suggère que si l'option de socket TCP_USER_TIMEOUT décrite par Kimvais est disponible, vous l'utilisez. Sur les noyaux plus anciens où cette option de socket n’est pas présente, vous pouvez appeler de manière répétée la SIOCOUTQioctl() pour déterminer la taille de la file d’envoi du socket. vous pouvez fermer la prise.

13
caf

Après réflexion (et recherche sur Google), je suis parvenu à la conclusion que vous ne pouvez pas modifier les valeurs tcp_retries1 et tcp_retries2 pour un seul socket à moins d'appliquer une sorte de correctif au noyau. Est-ce faisable pour vous?

Sinon, vous pouvez utiliser l'option de socket TCP_KEEPALIVE dont le but est de vérifier si une connexion est toujours active (il me semble que c'est exactement ce que vous essayez d'atteindre, c'est donc logique). Faites attention au fait que vous devez modifier un peu son paramètre par défaut, car le défaut est de déconnecter après environ 2 heures !!!

3
Simone

C’est pour ma compréhension . Tcp_retries2 est le nombre de retransmissions autorisées par le système avant de supprimer la connexion. Ainsi, si nous voulons modifier la valeur par défaut de tcp_retries2 à l’aide de TCP_USER_TIMEOUT, qui spécifie la durée maximale de conservation des données transmises. non acquitté, nous devons augmenter la valeur de TCP_USER_TIMEOUT, non?

Dans ce cas, la communication attendra plus longtemps et ne retransmettra pas le paquet de données . S'il vous plaît, corrigez-moi si quelque chose ne va pas.

0
Rndp13