Qual é a diferença entre read () e recv () e Between send () e write ()?
198
Qual é a diferença entre read()e recv(), e entre send()e write()na programação do soquete em termos de performances, velocidade e outros comportamentos?
Pense em gravação como implementado assim: #define write(...) send(##__VA_ARGS__, 0).
precisa
Respostas:
128
A diferença é que recv()/ send()funciona apenas em descritores de soquete e permite especificar determinadas opções para a operação real. Essas funções são um pouco mais especializadas (por exemplo, você pode definir um sinalizador para ignorar SIGPIPEou enviar mensagens fora da banda ...).
Funções read()/ write()são as funções universais de descrição de arquivos que funcionam em todos os descritores.
Isso está incorreto, há outra diferença no caso de datagramas com tamanho 0 - se um datagrama com comprimento zero estiver pendente, read (2) e recv () com um argumento flags de zero fornecem um comportamento diferente. Nessa circunstância, a leitura (2) não tem efeito (o datagrama permanece pendente), enquanto recv () consome o datagrama pendente.
Abhinav Gauniyal
2
@AbhinavGauniyal Como isso proporcionaria um comportamento diferente ? Se houver um datagrama de 0 byte, ambos, recve readnão fornecerá dados ao chamador, mas também nenhum erro. Para o chamador, o comportamento é o mesmo. O chamador pode nem saber nada sobre datagramas (pode não saber que esse é um soquete e não um arquivo, pode não saber que esse é um soquete de datagrama e não um soquete de fluxo). O fato de o datagrama permanecer pendente é um conhecimento implícito sobre como as pilhas IP funcionam nos kernels e não são visíveis para o chamador. Da perspectiva do chamador, eles ainda fornecerão um comportamento igual.
Mecki
2
@Mecki isso não é conhecimento implícito para todos, me leve por exemplo :)
Abhinav Gauniyal
1
@Mecki, o que indica uma leitura bem-sucedida sem bloqueio de 0 bytes? O datagrama ainda permanece pendente? Exatamente isso, e apenas isso, está me preocupando: o comportamento que um datagrama pode ficar pendente, mesmo que seja lido com êxito. Não tenho certeza se a situação pode surgir, e é por isso que eu gostaria de ter isso em mente.
sehe
2
@sehe Se você está preocupado, por que não usa recv? A razão pela qual recve sendonde foi introduzida em primeiro lugar foi o fato de que nem todos os conceitos de datagrama puderam ser mapeados para o mundo dos fluxos. reade writetrate tudo como um fluxo de dados, seja um canal, um arquivo, um dispositivo (por exemplo, uma porta serial) ou um soquete. No entanto, um soquete é apenas um fluxo real se ele usa TCP. Se ele usa UDP, é mais como um dispositivo de bloco. Mas se os dois lados o usarem como um fluxo, ele funcionará como um fluxo e você não poderá nem enviar um pacote UDP vazio usando writechamadas, para que essa situação não ocorra.
read () é equivalente a recv () com um parâmetro flags de 0. Outros valores para o parâmetro flags alteram o comportamento de recv (). Da mesma forma, write () é equivalente a send () com sinalizadores == 0.
Acabei de notar recentemente que, quando usei write()um soquete no Windows, quase funcionou (o FD passou para write()não é o mesmo que o passou para send(); eu costumava _open_osfhandle()fazer o FD para passar write()). No entanto, não funcionou quando tentei enviar dados binários que incluíam o caractere 10. Em write()algum lugar, o caractere 13 foi inserido antes disso. Mudá-lo para send()com um parâmetro flags de 0 corrigiu esse problema. read()poderia ter o problema inverso se 13-10 forem consecutivos nos dados binários, mas eu não testei. Mas isso parece ser outra diferença possível entre send()e write().
"Desempenho e velocidade"? Esse tipo de ... não é sinônimo, aqui?
De qualquer forma, a recv()chamada recebe sinalizadores que read()não o fazem, o que a torna mais poderosa ou pelo menos mais conveniente. Essa é uma diferença. Não acho que exista uma diferença significativa de desempenho, mas ainda não testei.
Talvez não ter que lidar com bandeiras possa ser percebido como mais conveniente.
semaj 24/11/2009
2
No Linux, também noto que:
Interrupção de chamadas do sistema e funções de biblioteca por manipuladores de sinal
Se um manipulador de sinal for chamado enquanto uma chamada de sistema ou chamada de função de biblioteca estiver bloqueada, então:
a chamada é reiniciada automaticamente após o retorno do manipulador de sinal; ou
a chamada falha com o erro EINTR.
... Os detalhes variam entre os sistemas UNIX; abaixo, os detalhes para Linux.
Se uma chamada bloqueada para uma das seguintes interfaces for interrompida por um manipulador de sinal, a chamada será reiniciada automaticamente depois que o manipulador de sinal retornar se o sinalizador SA_RESTART foi usado; caso contrário, a chamada falhará com o erro EINTR:
read (2), readv (2), write (2), writev (2) e ioctl (2) chama dispositivos "lentos".
.....
As seguintes interfaces nunca são reiniciadas após serem interrompidas por um manipulador de sinal, independentemente do uso de SA_RESTART; eles sempre falham com o erro EINTR quando interrompidos por um manipulador de sinal:
Interfaces de soquete "Input", quando um tempo limite (SO_RCVTIMEO) foi definido no soquete usando setsockopt (2): accept (2), recv (2),
recvfrom (2), recvmmsg (2) (também com um valor não NULL argumento de tempo limite) e recvmsg (2).
Interfaces de soquete "Output", quando um tempo limite (SO_RCVTIMEO) foi definido no soquete usando setsockopt (2): connect (2), send (2), sendto (2), sendto (2) e sendmsg (2).
Verifique man 7 signalpara mais detalhes.
Um uso simples seria usar o sinal para evitar o recvfrombloqueio indefinidamente.
#define write(...) send(##__VA_ARGS__, 0)
.Respostas:
A diferença é que
recv()
/send()
funciona apenas em descritores de soquete e permite especificar determinadas opções para a operação real. Essas funções são um pouco mais especializadas (por exemplo, você pode definir um sinalizador para ignorarSIGPIPE
ou enviar mensagens fora da banda ...).Funções
read()
/write()
são as funções universais de descrição de arquivos que funcionam em todos os descritores.fonte
recv
eread
não fornecerá dados ao chamador, mas também nenhum erro. Para o chamador, o comportamento é o mesmo. O chamador pode nem saber nada sobre datagramas (pode não saber que esse é um soquete e não um arquivo, pode não saber que esse é um soquete de datagrama e não um soquete de fluxo). O fato de o datagrama permanecer pendente é um conhecimento implícito sobre como as pilhas IP funcionam nos kernels e não são visíveis para o chamador. Da perspectiva do chamador, eles ainda fornecerão um comportamento igual.recv
? A razão pela qualrecv
esend
onde foi introduzida em primeiro lugar foi o fato de que nem todos os conceitos de datagrama puderam ser mapeados para o mundo dos fluxos.read
ewrite
trate tudo como um fluxo de dados, seja um canal, um arquivo, um dispositivo (por exemplo, uma porta serial) ou um soquete. No entanto, um soquete é apenas um fluxo real se ele usa TCP. Se ele usa UDP, é mais como um dispositivo de bloco. Mas se os dois lados o usarem como um fluxo, ele funcionará como um fluxo e você não poderá nem enviar um pacote UDP vazio usandowrite
chamadas, para que essa situação não ocorra.Pela primeira vez no Google
fonte
recv
só pode ser usado em um soquete e produzirá um erro se você tentar usá-lo, digamosSTDIN_FILENO
,.read()
ewrite()
são mais genéricos, eles funcionam com qualquer descritor de arquivo. No entanto, eles não funcionarão no Windows.Você pode passar opções adicionais para
send()
erecv()
, portanto, pode ser necessário usá-las em alguns casos.fonte
Acabei de notar recentemente que, quando usei
write()
um soquete no Windows, quase funcionou (o FD passou parawrite()
não é o mesmo que o passou parasend()
; eu costumava_open_osfhandle()
fazer o FD para passarwrite()
). No entanto, não funcionou quando tentei enviar dados binários que incluíam o caractere 10. Emwrite()
algum lugar, o caractere 13 foi inserido antes disso. Mudá-lo parasend()
com um parâmetro flags de 0 corrigiu esse problema.read()
poderia ter o problema inverso se 13-10 forem consecutivos nos dados binários, mas eu não testei. Mas isso parece ser outra diferença possível entresend()
ewrite()
.fonte
Outra coisa no linux é:
send
não permite operar em fd sem soquete. Assim, por exemplo, para escrever na porta USB,write
é necessário.fonte
"Desempenho e velocidade"? Esse tipo de ... não é sinônimo, aqui?
De qualquer forma, a
recv()
chamada recebe sinalizadores queread()
não o fazem, o que a torna mais poderosa ou pelo menos mais conveniente. Essa é uma diferença. Não acho que exista uma diferença significativa de desempenho, mas ainda não testei.fonte
No Linux, também noto que:
Verifique
man 7 signal
para mais detalhes.Um uso simples seria usar o sinal para evitar o
recvfrom
bloqueio indefinidamente.Um exemplo de APUE :
fonte