TCP: deux sockets différents peuvent-ils partager un port?

124

C'est peut-être une question très basique mais cela me déroute.

Deux prises connectées différentes peuvent-elles partager un port? J'écris un serveur d'application qui devrait être capable de gérer plus de 100k connexions simultanées, et nous savons que le nombre de ports disponibles sur un système est d'environ 60k (16 bits). Un socket connecté est affecté à un nouveau port (dédié), ce qui signifie que le nombre de connexions simultanées est limité par le nombre de ports, à moins que plusieurs sockets puissent partager le même port. Donc la question.

Merci pour l'aide à l'avance!

KJ
la source

Réponses:

175

Un socket serveur écoute sur un seul port. Toutes les connexions clientes établies sur ce serveur sont associées à ce même port d'écoute du côté serveur de la connexion. Une connexion établie est identifiée de manière unique par la combinaison de paires IP / Port côté client et côté serveur. Plusieurs connexions sur le même serveur peuvent partager la même paire IP / Port côté serveur à condition qu'elles soient associées à différentes paires IP / Port côté client , et le serveur serait capable de gérer autant de clients que les ressources système disponibles le permettent à.

Du côté client , il est courant que les nouvelles connexions sortantes utilisent un port côté client aléatoire , auquel cas il est possible de manquer de ports disponibles si vous établissez beaucoup de connexions dans un court laps de temps.

Remy Lebeau
la source
2
Merci pour la réponse, Remy! Votre réponse est tout ce qui m'intéressait. ;)
KJ
2
Les connexions @Remy sont discriminées non seulement par le port source / destination / IP, mais aussi par un protocole (TCP, UDP etc.), si je ne me trompe pas.
Ondrej Peterka
1
@OndraPeterka: oui, mais toutes les plateformes ne se limitent pas à cela. Par exemple, Windows autorise volontiers des sockets de serveur IPv4 et IPv6 séparés à écouter sur la même adresse IP locale: Port sans sauter à travers les cerceaux, mais les systèmes * Nix (y compris Linux et Android) ne le font pas.
Remy Lebeau
6
@ user2268997: Vous ne pouvez pas utiliser un seul socket pour vous connecter à plusieurs serveurs. Vous devez créer un socket distinct pour chaque connexion.
Remy Lebeau
3
@FernandoGonzalezSanchez: Un seul client peut avoir plusieurs sockets TCP liés à la même paire IP / Port locale tant qu'ils sont connectés à différentes paires IP / Port distantes. Cela n'est pas spécifique à Windows, cela fait partie du fonctionnement de TCP en général.
Remy Lebeau
182

Écoute TCP / HTTP sur les ports: comment plusieurs utilisateurs peuvent-ils partager le même port?

Alors, que se passe-t-il lorsqu'un serveur écoute les connexions entrantes sur un port TCP? Par exemple, disons que vous avez un serveur Web sur le port 80. Supposons que votre ordinateur a l'adresse IP publique 24.14.181.229 et que la personne qui essaie de se connecter à vous a l'adresse IP 10.1.2.3. Cette personne peut se connecter à vous en ouvrant une socket TCP vers 24.14.181.229:80. Assez simple.

Intuitivement (et à tort), la plupart des gens supposent que cela ressemble à quelque chose comme ceci:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

C'est intuitif, car du point de vue du client, il a une adresse IP, et se connecte à un serveur à IP: PORT. Puisque le client se connecte au port 80, alors son port doit être 80 aussi? C'est une chose sensée à penser, mais en fait pas ce qui se passe. Si cela était correct, nous ne pourrions servir qu'un seul utilisateur par adresse IP étrangère. Une fois qu'un ordinateur distant se connecte, il monopolise la connexion du port 80 au port 80 et personne d'autre ne peut se connecter.

Trois choses doivent être comprises:

1.) Sur un serveur, un processus écoute sur un port. Une fois qu'il obtient une connexion, il la transmet à un autre thread. La communication ne monopolise jamais le port d'écoute.

2.) Les connexions sont identifiées de manière unique par le système d'exploitation par les 5 tuples suivants: (IP local, port local, IP distant, port distant, protocole). Si un élément du tuple est différent, il s'agit d'une connexion complètement indépendante.

3.) Lorsqu'un client se connecte à un serveur, il choisit un port source aléatoire et inutilisé . De cette façon, un seul client peut avoir jusqu'à ~ 64 000 connexions au serveur pour le même port de destination.

Donc, c'est vraiment ce qui est créé lorsqu'un client se connecte à un serveur:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

Regard sur ce qui se passe réellement

Tout d'abord, utilisons netstat pour voir ce qui se passe sur cet ordinateur. Nous utiliserons le port 500 au lieu de 80 (car tout un tas de choses se passe sur le port 80 car c'est un port commun, mais fonctionnellement cela ne fait aucune différence).

    netstat -atnp | grep -i ":500 "

Comme prévu, la sortie est vide. Maintenant, démarrons un serveur Web:

    sudo python3 -m http.server 500

Maintenant, voici la sortie de l'exécution de netstat à nouveau:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

Alors maintenant, il y a un processus qui écoute activement (état: LISTEN) sur le port 500. L'adresse locale est 0.0.0.0, qui est le code pour "écouter toutes les adresses IP". Une erreur facile à faire est d'écouter uniquement sur le port 127.0.0.1, qui n'acceptera que les connexions de l'ordinateur actuel. Donc ce n'est pas une connexion, cela signifie simplement qu'un processus a demandé à bind () au port IP, et que ce processus est responsable de gérer toutes les connexions à ce port. Cela indique la limitation qu'il ne peut y avoir qu'un seul processus par ordinateur en écoute sur un port (il existe des moyens de contourner cela en utilisant le multiplexage, mais c'est un sujet beaucoup plus compliqué). Si un serveur Web écoute sur le port 80, il ne peut pas partager ce port avec d'autres serveurs Web.

Alors maintenant, connectons un utilisateur à notre machine:

    quicknet -m tcp -t localhost:500 -p Test payload.

Il s'agit d'un simple script ( https://github.com/grokit/quickweb ) qui ouvre un socket TCP, envoie la charge utile ("Test payload." Dans ce cas), attend quelques secondes et se déconnecte. Faire à nouveau netstat pendant que cela se produit affiche les éléments suivants:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

Si vous vous connectez avec un autre client et refaites netstat, vous verrez ce qui suit:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... c'est-à-dire que le client a utilisé un autre port aléatoire pour la connexion. Il n'y a donc jamais de confusion entre les adresses IP.

Rien
la source
11
C'est la meilleure réponse que j'ai jamais vue sur SO.
Emplois le
1
@ N0thing "De cette façon, un seul client peut avoir jusqu'à ~ 64k connexions au serveur pour le même port de destination." Ainsi, en pratique, si un client ne se connecte pas au même serveur et port, deux ou plusieurs fois simultanément, un client peut avoir même plus de ~ 64K connexions. Est-ce vrai. Si oui, cela implique qu'à partir d'un seul port côté client, il peut être connecté à de nombreux processus serveur différents (de sorte que la connexion socket est différente). Donc, dans l'ensemble, plusieurs sockets client peuvent résider sur le même port sur la machine cliente? Veuillez lire mon commentaire à la réponse "Remey Lebeau". Merci: D
Prem KTiw
6
@premktiw: Oui, plusieurs sockets client peuvent être liés à la même paire IP / port locale en même temps, s'ils sont connectés à différentes paires IP / Port de serveur de sorte que les tuples des paires locales + distantes soient uniques. Et oui, il est possible pour un client d'avoir plus de 64 000 connexions simultanées au total. À partir d'un seul port, il peut être connecté à un nombre potentiellement infini de serveurs (limité par les ressources du système d'exploitation disponibles, les ports de routeur disponibles, etc.) tant que les paires IP / port du serveur sont uniques.
Remy Lebeau
1
@RemyLebeau Satisfait. Merci beaucoup: D
Prem KTiw
1
@bibstha Comment le pare-feu gère-t-il les ports aléatoires, lorsque toutes les connexions entrantes sont rejetées?
PatrykG
35

Une prise connectée est affectée à un nouveau port (dédié)

C'est une intuition courante, mais elle est incorrecte. Une prise connectée n'est pas affectée à un nouveau port / dédié. La seule contrainte réelle que la pile TCP doit satisfaire est que le tuple de (local_address, local_port, remote_address, remote_port) doit être unique pour chaque connexion de socket. Ainsi, le serveur peut avoir de nombreuses sockets TCP utilisant le même port local, tant que chacune des sockets sur le port est connectée à un emplacement distant différent.

Consultez le paragraphe "Socket Pair" à l' adresse : http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false

Jeremy Friesner
la source
1
Merci pour la réponse parfaite, Jeremy!
KJ
6
Ce que vous dites est tout à fait vrai du côté serveur. Cependant, la structure de l'API BSD Sockets signifie que les ports côté client sortants doivent être uniques en pratique, car l' bind()opération précède l' connect()opération, même implicitement.
Marquis of Lorne
1
@EJP Salut, je pensais qu'il bind()n'était utilisé que côté serveur avant accept()?Donc, le côté client liera également le port particulier?
GMsoF
5
@GMsoF: bind()peut être utilisé côté client avant connect().
Remy Lebeau
10

Théoriquement, oui. Pratique, non. La plupart des noyaux (y compris linux) ne vous permettent pas une seconde bind()sur un port déjà alloué. Ce n'était pas un très gros patch pour rendre cela permis.

Sur le plan de la conception, nous devrions faire la différence entre socket et port . Les sockets sont des points de terminaison de communication bidirectionnels, c'est-à-dire des «objets» où nous pouvons envoyer et recevoir des octets. C'est une chose conceptuelle, il n'y a pas de tel champ dans un en-tête de paquet nommé "socket".

Le port est un identifiant capable d'identifier une socket. Dans le cas du TCP, un port est un entier de 16 bits, mais il existe également d'autres protocoles (par exemple, sur les sockets Unix, un "port" est essentiellement une chaîne).

Le problème principal est le suivant: si un paquet entrant arrive, le noyau peut identifier sa socket par son numéro de port de destination. C'est le moyen le plus courant, mais ce n'est pas la seule possibilité:

  • Les sockets peuvent être identifiés par l'adresse IP de destination des paquets entrants. C'est le cas, par exemple, si nous avons un serveur utilisant deux IP simultanément. Ensuite, nous pouvons exécuter, par exemple, différents serveurs Web sur les mêmes ports, mais sur des IP différentes.
  • Les sockets peuvent également être identifiés par leur port source et leur adresse IP. C'est le cas dans de nombreuses configurations d'équilibrage de charge.

Parce que vous travaillez sur un serveur d'applications, il pourra le faire.

peterh - Réintégrer Monica
la source
2
Il n'a pas demandé de faire une seconde bind().
Marquis of Lorne
1
@ user207421 Avez-vous déjà vu un système d'exploitation sur lequel les prises d'écoute ne sont pas configurées bind()? Je peux l'imaginer, oui c'est tout à fait possible, mais le fait est que WinSock et l'API Posix utilisent l' bind()appel pour cela, même leur paramétrage est pratiquement le même. Même si une API n'a pas cet appel, vous devez en quelque sorte le dire, d'où voulez-vous lire les octets entrants .
peterh - Réintégrer Monica le
1
@ user207421 Bien sûr, 100k connexions TCP ou plus peuvent être gérées avec les mêmes ports, listen()/ les accept()appels API peuvent créer les sockets de manière à ce que le noyau les différencie par leurs ports entrants. La question du PO peut être interprétée de la manière dont il la demande essentiellement. Je pense que c'est assez réaliste, mais ce n'est pas ce que veut dire littéralement sa question.
peterh - Réintégrer Monica le
1

Non. Il n'est pas possible de partager le même port à un instant donné. Mais vous pouvez faire de votre application de telle manière qu'elle rendra l'accès au port à un instant différent.

SAKEER T
la source