web-dev-qa-db-fra.com

Blocage des sockets: quand, exactement, "send ()" revient-il?

Quand, exactement, la fonction socket BSD send() revient-elle à l'appelant?

En mode non bloquant, il devrait revenir immédiatement, correct?

Quant au mode blocage, le page de manuel dit:

Lorsque le message ne rentre pas dans le tampon d'envoi du socket, send () se bloque normalement, sauf si le socket a été placé dans un I/non bloquant Mode O.

Des questions:

  1. Est-ce à dire que l'appel send() retournera toujours immédiatement s'il y a de la place dans le tampon d'envoi du noyau?
  2. Le comportement et les performances de l'appel send() sont-ils identiques pour TCP et UDP? Sinon, pourquoi?
41
David Citron

Est-ce à dire que l'appel send () retournera toujours immédiatement s'il y a de la place dans le tampon d'envoi du noyau?

Oui. Tant que signifie immédiatement après la mémoire que vous avez fournie, elle a été copiée dans le tampon du noyau. Ce qui, dans certains cas Edge, peut ne pas être aussi immédiat. Par exemple, si le pointeur que vous passez déclenche une erreur de page qui doit extraire le tampon d'un fichier mappé en mémoire ou du swap, cela ajouterait un retard important au retour de l'appel.

Le comportement et les performances de l'appel send () sont-ils identiques pour TCP et UDP? Sinon, pourquoi?

Pas assez. Les différences de performances possibles dépendent de l'implémentation du système d'exploitation de la pile TCP/IP. En théorie, le socket UDP pourrait être légèrement moins cher, car le système d'exploitation doit faire moins de choses avec.

EDIT: D'autre part, puisque vous pouvez envoyer beaucoup plus de données par appel système avec TCP, le coût par octet peut généralement être beaucoup plus faible avec TCP . Cela peut être atténué avec sendmmsg () dans les noyaux Linux récents.

Quant au comportement, il est presque identique.

Pour bloquer les sockets, les deux TCP et UDP se bloqueront jusqu'à ce qu'il y ait de l'espace dans le tampon du noyau. La distinction est cependant que le socket UDP attendra jusqu'à ce que tout votre tampon puisse être stocké dans le tampon du noyau, tandis que le TCP peut décider de ne copier qu'un seul octet dans le tampon du noyau (il s'agit généralement de plus d'un octet)).

Si vous essayez d'envoyer des paquets supérieurs à 64 Ko, un socket UDP échouera probablement avec [~ # ~] emsgsize [~ # ~] . En effet, UDP, étant une socket de datagramme , garantit d'envoyer votre tampon entier sous la forme d'un seul paquet IP (ou train de fragments de paquets IP) ou de ne pas l'envoyer du tout.

Les sockets non bloquants se comportent de la même manière que les versions bloquantes à la seule exception qu'au lieu de bloquer (au cas où il n'y a pas assez d'espace dans le tampon du noyau), les appels échouent avec [~ # ~] eagain [ ~ # ~] (ou [~ # ~] ewouldblock [~ # ~] ). Lorsque cela se produit, il est temps de remettre le socket dans epoll/kqueue/select (ou tout ce que vous utilisez) pour attendre qu'il redevienne accessible en écriture.

Comme d'habitude lorsque vous travaillez sur POSIX, gardez à l'esprit que votre appel peut échouer avec [~ # ~] eintr [~ # ~] (si l'appel a été interrompu par un signal). Dans ce cas, vous voudrez probablement appeler à nouveau send().

34
Arvid

S'il y a de la place dans le tampon du noyau, alors send() copie autant d'octets que possible dans le tampon et se ferme immédiatement, retournant le nombre d'octets réellement copiés (qui peut être inférieur au nombre que vous avez demandé). S'il n'y a pas de place dans la mémoire tampon du noyau, alors send() se bloque jusqu'à ce que l'une des pièces devienne disponible ou qu'un délai d'attente se produise (si une est configurée).

5
Remy Lebeau

Le send () reviendra dès que les données auront été acceptées par le noyau. En cas de blocage de socket: send () se bloquera si la mémoire tampon du noyau n'est pas suffisamment libre pour recevoir les données fournies pour envoyer ().

Sockets non bloquantes: send () ne se bloquera pas, mais échouera et retournera -1 ou il peut retourner le nombre d'octets copiés partiellement (en fonction de l'espace tampon disponible). Il définit l'errno EWOULDBLOCK ou EAGAIN. Cela signifie qu'à ce moment de send (), le tampon n'a pas pu prendre tous les octets et vous devez réessayer avec select () call pour envoyer () à nouveau les données. Ou vous pouvez mettre une boucle avec sleep () et appeler send (), mais vous devez faire attention au nombre d'octets réellement envoyés et au nombre d'octets restant à envoyer.

1
Sumit Trehan

Est-ce à dire que l'appel send () retournera toujours immédiatement s'il y a de la place dans le tampon d'envoi du noyau?

N'est-ce pas? Le moment après lequel les données "sont envoyées" peut être défini différemment. Je pense que c'est un moment où OS a accepté vos données pour la livraison sur pile. Sinon, il est assez difficile de le définir. Est-ce un moment où les données sont transmises au tampon de la carte réseau? Ou après le moment où les données sont poussées hors du tampon de la carte réseau?

Y a-t-il un problème que vous devez savoir avec certitude ou vous êtes simplement curieux?

0

Votre présomption est correcte. S'il y a de la place dans le tampon d'envoi du noyau, le noyau copiera les données dans le tampon d'envoi et send() reviendra.

0
caf