Comment fonctionne la fonction accept () de l'API socket?

126

L'API socket est la norme de facto pour les communications TCP / IP et UDP / IP (c'est-à-dire le code réseau tel que nous le connaissons). Cependant, l'une de ses fonctions essentielles accept()est un peu magique.

Pour emprunter une définition semi-formelle:

accept () est utilisé côté serveur. Il accepte une tentative entrante de créer une nouvelle connexion TCP à partir du client distant et crée un nouveau socket associé à la paire d'adresses de socket de cette connexion.

En d'autres termes, acceptrenvoie un nouveau socket via lequel le serveur peut communiquer avec le client nouvellement connecté. L'ancienne socket (sur laquelle accepton a appelé) reste ouverte, sur le même port, à l'écoute des nouvelles connexions.

Comment ça acceptmarche? Comment est-il mis en œuvre? Il y a beaucoup de confusion sur ce sujet. De nombreuses personnes prétendent accepter d'ouvrir un nouveau port et que vous communiquez avec le client via celui-ci. Mais ce n'est évidemment pas vrai, car aucun nouveau port n'est ouvert. Vous pouvez en fait communiquer via le même port avec différents clients, mais comment? Lorsque plusieurs threads appellent recvsur le même port, comment les données savent-elles où aller?

Je suppose que c'est quelque chose du genre que l'adresse du client est associée à un descripteur de socket, et chaque fois que des données arrivent, recvelles sont acheminées vers la bonne socket, mais je ne suis pas sûr.

Ce serait formidable d'obtenir une explication approfondie du fonctionnement interne de ce mécanisme.

Eli Bendersky
la source
2
Ainsi, pour chaque demande client, une toute NOUVELLE connexion socket côté serveur est ouverte. Le serveur doit toujours être ouvert à 80 pour écouter les appels entrants. S'il reçoit un appel, il crée alors immédiatement un NOUVEAU socket avec les quatre tuples comme mentionné ci-dessous, qui établira une connexion TCP entre le client et le serveur. Ma compréhension est-elle correcte?
brain storm
1
C'est une question très fondamentale et j'ai récemment été testée à ce sujet dans une interview: stackoverflow.com/questions/24871827/... Si vous avez des commentaires à ce sujet, veuillez poster
brain storm
@brainstorm Uniquement si vous ignorez complètement l'existence de HTTP keep-alive.
Marquis of Lorne

Réponses:

140

Votre confusion réside dans le fait qu'une socket est identifiée par Server IP: Server Port. En réalité, les sockets sont identifiés de manière unique par un quatuor d'informations:

Client IP : Client Port et Server IP : Server Port

Ainsi, alors que l'adresse IP du serveur et le port du serveur sont constants dans toutes les connexions acceptées, les informations côté client sont ce qui lui permet de suivre où tout se passe.

Exemple pour clarifier les choses:

Disons que nous avons un serveur à 192.168.1.1:80et deux clients, 10.0.0.1et 10.0.0.2.

10.0.0.1ouvre une connexion sur le port local 1234et se connecte au serveur. Maintenant, le serveur a un socket identifié comme suit:

10.0.0.1:1234 - 192.168.1.1:80  

10.0.0.2Ouvre maintenant une connexion sur le port local 5678et se connecte au serveur. Maintenant, le serveur a deux sockets identifiés comme suit:

10.0.0.1:1234 - 192.168.1.1:80  
10.0.0.2:5678 - 192.168.1.1:80
17 sur 26
la source
3
Je ne connais pas les détails d'implémentation (qui varient probablement d'une plateforme à l'autre), je sais juste que conceptuellement, les sockets sont identifiées par le quatuor d'informations que j'ai décrit.
17 du 26
3
Avez-vous des références à ce sujet?
qeek
3
Question aléatoire: que se passe-t-il si NAT est utilisé et que deux clients sur le même réseau tentent d'utiliser le même port local lors de la connexion au serveur? Par exemple, si 10.0.0.1 et 10.0.0.2 sont tous deux connectés à un routeur avec une adresse IP externe de 192.168.0.1, le serveur à 192.168.1.1 voit deux connexions à partir de 192.168.0.1. Que se passe-t-il dans ce cas si, par un coup de chance du générateur de nombres aléatoires, 10.0.0.1 et 10.0.0.2 choisissent le même port local?
aroth
4
Le support NAT dans le routeur s'occupe des détails là-bas. Le trafic réseau passe en fait par deux connexions - client à routeur et routeur à serveur. Le routeur établit les connexions sortantes sur deux ports différents 192.168.0.1:1234 et 192.168.0.1:5678. Le trafic entrant est ensuite redirigé par le routeur vers le bon client.
17 du 26
3
Si une prise est identifiée par le quatuor, quelles sont les informations de quatuor d'une prise d'écoute?
Eric Zheng
74

Juste pour ajouter à la réponse donnée par l'utilisateur "17 sur 26"

Le socket se compose en fait de 5 tuple - (adresse IP source, port source, adresse IP de destination, port de destination, protocole). Ici, le protocole pourrait TCP ou UDP ou tout autre protocole de couche de transport. Ce protocole est identifié dans le paquet à partir du champ «protocole» du datagramme IP.

Ainsi, il est possible d'avoir des applications différentes sur le serveur communiquant avec le même client sur exactement les mêmes 4-uplets mais différents dans le domaine du protocole. Par exemple

Apache côté serveur parlant sur (server1.com:880-client1:1234 sur TCP) et World of Warcraft parlant sur (server1.com:880-client1:1234 sur UDP)

Le client et le serveur traiteront ceci comme le champ de protocole dans le paquet IP dans les deux cas est différent même si tous les 4 autres champs sont identiques.

Methos
la source
13

Ce qui m'a dérouté lorsque j'apprenais cela, c'est que les termes socketet portsuggèrent qu'ils sont quelque chose de physique, alors qu'en fait ce ne sont que des structures de données que le noyau utilise pour résumer les détails du réseau.

En tant que telles, les structures de données sont implémentées pour pouvoir séparer les connexions de différents clients. Quant à la façon dont ils sont mis en œuvre, la réponse est soit a.) Cela n'a pas d'importance, le but de l'API sockets est précisément que l'implémentation ne devrait pas avoir d'importance ou b.) Il suffit de regarder. Outre les livres Stevens hautement recommandés fournissant une description détaillée d'une implémentation, consultez la source sous Linux ou Solaris ou l'un des BSD.

a2800276
la source
Oui, la majeure partie de la terminologie de réseautage consiste simplement à attribuer des noms à certaines collections de bits et à des décisions prises en fonction de leurs valeurs («identificateur de protocole», «routage», «liaison», «socket», etc.). Tout le matériel de votre carte réseau est conçu pour recevoir un flux de bits. Ce qui leur arrive par rapport aux programmes sur votre ordinateur est décidé par le pilote et le système d'exploitation. Nous pourrions nous débarrasser de toute cette terminologie demain si nous le voulions, mais le principe de fournir un flux de bits semble fondamental ...
masterxilo
-1

Comme l'a dit l'autre gars, une socket est identifiée de manière unique par un 4-tuple (IP client, port client, IP serveur, port serveur).

Le processus serveur en cours d'exécution sur l'IP du serveur maintient une base de données (ce qui signifie que je ne me soucie pas du type de structure de données table / liste / arbre / tableau / magie qu'il utilise) de sockets actives et écoute sur le port du serveur. Lorsqu'il reçoit un message (via la pile TCP / IP du serveur), il vérifie l'adresse IP et le port du client par rapport à la base de données. Si l'adresse IP du client et le port client se trouvent dans une entrée de base de données, le message est transmis à un gestionnaire existant, sinon une nouvelle entrée de base de données est créée et un nouveau gestionnaire est généré pour gérer ce socket.

Dans les premiers jours de l'ARPAnet, certains protocoles (FTP pour un) écoutaient un port spécifié pour les demandes de connexion et répondaient avec un port de transfert. D'autres communications pour cette connexion passeraient par le port de transfert. Cela a été fait pour améliorer les performances par paquet: les ordinateurs étaient plusieurs ordres de grandeur plus lents à l'époque.


la source
pouvez-vous élaborer sur la partie «port de transfert»?
Eli Bendersky
1
Ceci est soit une description d'un protocole pré-TCP, soit trop simplifié. Un client essayant de se connecter à une prise d'écoute envoie un paquet spécial pour établir la connexion (jeu de bits SYN). Il existe une distinction claire entre un paquet créant une nouvelle socket et un utilisant une socket existante.
John M
... envoie un paquet spécial pour établir la connexion (jeu de bits SYN). Ce qui (si je comprends bien) amène la pile de protocoles à le donner à «l'écouteur» (le cas échéant), c'est pourquoi il ne peut y avoir qu'un seul port d'écoute par combinaison adresse / port / protocole. Je ne sais pas si c'est dans la spécification ou simplement dans la convention de mise en œuvre.
Peter Wone
1
Le deuxième paragraphe ne décrit pas correctement ce qui se passe au niveau de la couche TCP ou au sein d'un processus serveur. Les processus serveur n'ont pas besoin de maintenir les structures de données des sockets de quelque nature que ce soit, ni de vérifier les paires IP: ports entrantes par rapport à quoi que ce soit. C'est pour ça que les prises sont là. FTP utilise un port séparé pour les données, pas pour toutes les «communications ultérieures», et des chapeaux sont faits pour simplifier le protocole, pas pour des raisons de performances. Utiliser un nouveau port sans améliorer les performances de quelque manière que ce soit.
Marquis of Lorne
"maintient une base de données (ce qui signifie que je ne me soucie pas du type de structure de données table / liste / arbre / tableau / magie qu'il utilise)" :) J'appelle généralement cela un "tableau" (ou peut-être "graphique" ou "arbre de décision" ). "Database" me suggère une implémentation.
masterxilo