Quelle est la différence entre read () et recv (), et entre send () et write ()?
198
Quelle est la différence entre read()et recv(), et entre send()et write()dans la programmation de socket en termes de performances, de vitesse et d'autres comportements?
Pensez à écrire comme mis en œuvre comme ceci: #define write(...) send(##__VA_ARGS__, 0).
prudencenow1
Réponses:
128
La différence est que recv()/ send()ne fonctionne que sur les descripteurs de socket et vous permet de spécifier certaines options pour l'opération réelle. Ces fonctions sont légèrement plus spécialisées (par exemple, vous pouvez définir un indicateur pour ignorer SIGPIPE, ou pour envoyer des messages hors bande ...).
Les fonctions read()/ write()sont les fonctions de descripteur de fichiers universelles fonctionnant sur tous les descripteurs.
C'est incorrect, il y a une autre différence en cas de datagrammes de longueur 0 - Si un datagramme de longueur nulle est en attente, read (2) et recv () avec un argument drapeaux de zéro fournissent un comportement différent. Dans ce cas, read (2) n'a aucun effet (le datagramme reste en attente), tandis que recv () consomme le datagramme en attente.
Abhinav Gauniyal
2
@AbhinavGauniyal Comment cela fournirait-il un comportement différent ? S'il y a un datagramme de 0 octet, les deux, recvet readne fourniront aucune donnée à l'appelant mais également aucune erreur. Pour l'appelant, le comportement est le même. L'appelant peut même ne rien savoir sur les datagrammes (il peut ne pas savoir qu'il s'agit d'un socket et non d'un fichier, il peut ne pas savoir qu'il s'agit d'un socket de datagramme et non d'un socket de flux). Le fait que le datagramme reste en attente est une connaissance implicite du fonctionnement des piles IP dans les noyaux et non visible par l'appelant. Du point de vue de l'appelant, ils offriront toujours un comportement égal.
Mecki
2
@Mecki ce n'est pas une connaissance implicite pour tout le monde, prenez-moi par exemple :)
Abhinav Gauniyal
1
@Mecki qu'indique une lecture réussie non bloquante de 0 octet? Le datagramme reste-t-il toujours en attente? C'est exactement cela, et seulement cela, qui m'inquiète: le comportement qu'un datagramme peut rester en attente même s'il est lu avec succès. Je ne sais pas si la situation peut survenir, c'est pourquoi j'aimerais garder cela à l'esprit.
sehe
2
@sehe Si vous êtes inquiet, pourquoi ne l'utilisez-vous pas recv? La raison pour laquelle recvet sendoù a été introduit en premier lieu était le fait que tous les concepts de datagrammes ne pouvaient pas être mappés au monde des flux. readet writetraiter tout comme un flux de données, que ce soit un canal, un fichier, un périphérique (par exemple un port série) ou une socket. Pourtant, un socket n'est un vrai flux que s'il utilise TCP. S'il utilise UDP, c'est plus comme un périphérique bloc. Mais si les deux côtés l'utilisent comme un flux, cela fonctionnera comme un flux et vous ne pouvez même pas envoyer un paquet UDP vide à l'aide d' writeappels, donc cette situation ne se produira pas.
read () est équivalent à recv () avec un paramètre flags de 0. D'autres valeurs pour le paramètre flags changent le comportement de recv (). De même, write () est équivalent à send () avec des drapeaux == 0.
Je viens de remarquer récemment que lorsque je l'utilisais write()sur un socket dans Windows, cela fonctionne presque (le FD transmis à write()n'est pas le même que celui transmis à send(); j'avais l'habitude de _open_osfhandle()faire passer le FD à write()). Cependant, cela n'a pas fonctionné lorsque j'ai essayé d'envoyer des données binaires qui comprenaient le caractère 10. write()quelque part inséré le caractère 13 avant cela. Le changer en send()avec un paramètre drapeaux de 0 a résolu ce problème. read()pourrait avoir le problème inverse si 13-10 sont consécutifs dans les données binaires, mais je ne l'ai pas testé. Mais cela semble être une autre différence possible entre send()et write().
"Performance et vitesse"? N'est-ce pas ce genre de ... synonymes, ici?
Quoi qu'il en soit, l' recv()appel prend des drapeaux qui read()ne le font pas, ce qui le rend plus puissant, ou du moins plus pratique. Voilà une différence. Je ne pense pas qu'il y ait une différence de performance significative, mais je n'ai pas testé pour cela.
Peut-être que ne pas avoir à traiter avec des drapeaux peut être perçu comme plus pratique.
semaj
2
Sous Linux, je remarque également que:
Interruption des appels système et des fonctions de bibliothèque par les gestionnaires de signaux
Si un gestionnaire de signaux est appelé alors qu'un appel système ou un appel de fonction de bibliothèque est bloqué, alors:
l'appel est automatiquement redémarré après le retour du gestionnaire de signal; ou
l'appel échoue avec l'erreur EINTR.
... Les détails varient selon les systèmes UNIX; ci-dessous, les détails pour Linux.
Si un appel bloqué à l'une des interfaces suivantes est interrompu par un gestionnaire de signal, l'appel est automatiquement redémarré après le retour du gestionnaire de signal si l'indicateur SA_RESTART a été utilisé; sinon l'appel échoue avec l'erreur EINTR:
read (2), readv (2), write (2), writev (2) et ioctl (2) sur les périphériques "lents".
.....
Les interfaces suivantes ne sont jamais redémarrées après avoir été interrompues par un gestionnaire de signaux, quelle que soit l'utilisation de SA_RESTART; ils échouent toujours avec l'erreur EINTR lorsqu'ils sont interrompus par un gestionnaire de signaux:
Interfaces de socket "Input", lorsqu'un timeout (SO_RCVTIMEO) a été défini sur le socket à l'aide de setsockopt (2): accept (2), recv (2),
recvfrom (2), recvmmsg (2) (également avec une valeur non NULL) argument timeout) et recvmsg (2).
Interfaces de socket "Sortie", lorsqu'un délai d'expiration (SO_RCVTIMEO) a été défini sur le socket à l'aide de setsockopt (2): connect (2), send (2), sendto (2) et sendmsg (2).
Vérifiez man 7 signalpour plus de détails.
Une utilisation simple serait d'utiliser un signal pour éviter de recvfrombloquer indéfiniment.
#define write(...) send(##__VA_ARGS__, 0)
.Réponses:
La différence est que
recv()
/send()
ne fonctionne que sur les descripteurs de socket et vous permet de spécifier certaines options pour l'opération réelle. Ces fonctions sont légèrement plus spécialisées (par exemple, vous pouvez définir un indicateur pour ignorerSIGPIPE
, ou pour envoyer des messages hors bande ...).Les fonctions
read()
/write()
sont les fonctions de descripteur de fichiers universelles fonctionnant sur tous les descripteurs.la source
recv
etread
ne fourniront aucune donnée à l'appelant mais également aucune erreur. Pour l'appelant, le comportement est le même. L'appelant peut même ne rien savoir sur les datagrammes (il peut ne pas savoir qu'il s'agit d'un socket et non d'un fichier, il peut ne pas savoir qu'il s'agit d'un socket de datagramme et non d'un socket de flux). Le fait que le datagramme reste en attente est une connaissance implicite du fonctionnement des piles IP dans les noyaux et non visible par l'appelant. Du point de vue de l'appelant, ils offriront toujours un comportement égal.recv
? La raison pour laquellerecv
etsend
où a été introduit en premier lieu était le fait que tous les concepts de datagrammes ne pouvaient pas être mappés au monde des flux.read
etwrite
traiter tout comme un flux de données, que ce soit un canal, un fichier, un périphérique (par exemple un port série) ou une socket. Pourtant, un socket n'est un vrai flux que s'il utilise TCP. S'il utilise UDP, c'est plus comme un périphérique bloc. Mais si les deux côtés l'utilisent comme un flux, cela fonctionnera comme un flux et vous ne pouvez même pas envoyer un paquet UDP vide à l'aide d'write
appels, donc cette situation ne se produira pas.Par le premier hit sur Google
la source
recv
ne peut être utilisé sur une prise, et produira une erreur si vous essayez de l' utiliser, disons,STDIN_FILENO
.read()
etwrite()
sont plus génériques, ils fonctionnent avec n'importe quel descripteur de fichier. Cependant, ils ne fonctionneront pas sous Windows.Vous pouvez transmettre des options supplémentaires à
send()
etrecv()
, vous devrez donc peut-être les utiliser dans certains cas.la source
Je viens de remarquer récemment que lorsque je l'utilisais
write()
sur un socket dans Windows, cela fonctionne presque (le FD transmis àwrite()
n'est pas le même que celui transmis àsend()
; j'avais l'habitude de_open_osfhandle()
faire passer le FD àwrite()
). Cependant, cela n'a pas fonctionné lorsque j'ai essayé d'envoyer des données binaires qui comprenaient le caractère 10.write()
quelque part inséré le caractère 13 avant cela. Le changer ensend()
avec un paramètre drapeaux de 0 a résolu ce problème.read()
pourrait avoir le problème inverse si 13-10 sont consécutifs dans les données binaires, mais je ne l'ai pas testé. Mais cela semble être une autre différence possible entresend()
etwrite()
.la source
Une autre chose sur Linux est:
send
ne permet pas de fonctionner sur un fd non socket. Ainsi, par exemple pour écrire sur le port USB,write
est nécessaire.la source
"Performance et vitesse"? N'est-ce pas ce genre de ... synonymes, ici?
Quoi qu'il en soit, l'
recv()
appel prend des drapeaux quiread()
ne le font pas, ce qui le rend plus puissant, ou du moins plus pratique. Voilà une différence. Je ne pense pas qu'il y ait une différence de performance significative, mais je n'ai pas testé pour cela.la source
Sous Linux, je remarque également que:
Vérifiez
man 7 signal
pour plus de détails.Une utilisation simple serait d'utiliser un signal pour éviter de
recvfrom
bloquer indéfiniment.Un exemple d' APUE :
la source