fechar vs soquete de desligamento?

214

Em C, entendi que, se fecharmos um soquete, isso significa que o soquete será destruído e poderá ser reutilizado posteriormente.

Que tal desligar? A descrição dizia que fecha metade de uma conexão duplex a esse soquete. Mas esse soquete será destruído como uma closechamada de sistema?

tshepang
fonte
Não pode ser reutilizado mais tarde. Ele está fechado. Acabado. Feito.
Marquês de Lorne

Respostas:

191

Isso é explicado no guia de rede de Beej. shutdowné uma maneira flexível de bloquear a comunicação em uma ou ambas as direções. Quando o segundo parâmetro for SHUT_RDWR, ele bloqueará o envio e o recebimento (como close). No entanto, closeé a maneira de realmente destruir um soquete.

Com shutdown, você ainda poderá receber dados pendentes que o parceiro já enviou (obrigado a Joey Adams por observar isso).

Matthew Flaschen
fonte
23
Lembre-se de que, mesmo que você feche () um soquete TCP, ele não será necessariamente reutilizável imediatamente, pois estará no estado TIME_WAIT enquanto o sistema operacional garante que não haja pacotes pendentes que possam ser confundidos como novas informações se você deveriam reutilizar imediatamente esse soquete para outra coisa.
alesplin
91
A grande diferença entre desligar e fechar um soquete é o comportamento quando o soquete é compartilhado por outros processos. Um shutdown () afeta todas as cópias do soquete, enquanto close () afeta apenas o descritor de arquivo em um processo.
Zan Lynx
5
Um motivo pelo qual você pode querer usar as shutdownduas direções, mas não, closeé se você fez uma FILEreferência ao soquete usando fdopen. Se você closeusar o soquete, um arquivo recém-aberto poderá receber o mesmo fd, e o uso subsequente do FILEmesmo lerá / gravará o local errado, o que pode ser muito ruim. Se você apenas shutdown, o uso subseqüente do FILEapenas dará erros até que fcloseseja chamado.
R .. GitHub Pare de ajudar o gelo
25
O comentário sobre TIME_WAIT está incorreto. Isso se aplica a portas, não a soquetes. Você não pode reutilizar soquetes.
Marquês de Lorne
48
-1. Tanto este post como o link omitem uma importante razão conceitual para querer usar shutdown: sinalizar EOF para o par e ainda poder receber dados pendentes que o par enviou.
Joey Adams
144

Nenhuma das respostas existentes informa às pessoas como shutdowne closefunciona no nível do protocolo TCP, portanto, vale a pena adicionar isso.

Uma conexão TCP padrão é finalizada pela finalização em quatro direções:

  1. Uma vez que um participante não tem mais dados para enviar, envia um pacote FIN para o outro
  2. A outra parte retorna um ACK para o FIN.
  3. Quando a outra parte também conclui a transferência de dados, envia outro pacote FIN
  4. O participante inicial retorna um ACK e finaliza a transferência.

No entanto, existe outra maneira "emergente" de fechar uma conexão TCP:

  1. Um participante envia um pacote RST e abandona a conexão
  2. O outro lado recebe um RST e depois abandona a conexão também

No meu teste com o Wireshark, com opções de soquete padrão, shutdownenvia um pacote FIN para a outra extremidade, mas é tudo o que faz. Até que a outra parte envie o pacote FIN, você ainda poderá receber dados. Quando isso acontecer, você Receiveobterá um resultado de tamanho 0. Portanto, se você é o primeiro a desligar o "send", feche o soquete assim que terminar de receber os dados.

Por outro lado, se você ligar closeenquanto a conexão ainda estiver ativa (o outro lado ainda está ativo e você também pode ter dados não enviados no buffer do sistema), um pacote RST será enviado para o outro lado. Isso é bom para erros. Por exemplo, se você acha que a outra parte forneceu dados incorretos ou se recusou a fornecer dados (ataque do DOS?), Você pode fechar o soquete imediatamente.

Minha opinião sobre as regras seria:

  1. Considere shutdownantes, closequando possível
  2. Se você terminou de receber (dados de tamanho 0 recebidos) antes de decidir encerrar, feche a conexão após o término do último envio (se houver).
  3. Se você deseja fechar a conexão normalmente, encerre a conexão (com SHUT_WR e se você não se importa em receber dados após esse ponto, com SHUT_RD também), aguarde até receber um tamanho de 0 e feche o tomada.
  4. De qualquer forma, se ocorreu algum outro erro (tempo limite, por exemplo), basta fechar o soquete.

Implementações ideais para SHUT_RD e SHUT_WR

O seguinte não foi testado, confie em seu próprio risco. No entanto, acredito que essa é uma maneira razoável e prática de fazer as coisas.

Se a pilha TCP receber um desligamento apenas com SHUT_RD, marcará essa conexão como não há mais dados esperados. Quaisquer readsolicitações pendentes e subseqüentes (independentemente do segmento em que estão) serão retornadas com resultado de tamanho zero. No entanto, a conexão ainda está ativa e utilizável - você ainda pode receber dados OOB, por exemplo. Além disso, o sistema operacional eliminará todos os dados que receber para esta conexão. Mas isso é tudo, nenhum pacote será enviado para o outro lado.

Se a pilha TCP receber um desligamento apenas com SHUT_WR, marcará essa conexão como não mais dados podem ser enviados. Todas as solicitações de gravação pendentes serão concluídas, mas as solicitações de gravação subsequentes falharão. Além disso, um pacote FIN será enviado para outro lado para informar que não temos mais dados para enviar.

Earth Engine
fonte
2
"se você fechar enquanto a conexão ainda estiver ativa" é tautológica e não causa o envio de um RST. (1) não é necessário. Em (4), os tempos limite não são necessariamente fatais para a conexão e não são invariáveis, indicando que você pode fechá-la.
Marquês de Lorne
4
@EJP Não, não é tautológico. Você pode shutdown()a conexão e, em seguida, ela não está mais viva. Você ainda tem o descritor de arquivo. Você ainda pode recv()do buffer de recebimento. E você ainda precisa ligar close()para descartar o descritor de arquivo.
Pavel Šimerda 21/01
Esta é a melhor resposta em relação ao formato do fio. Eu estaria interessado em obter mais detalhes sobre como encerrar com SHUT_RD. Não há sinalização TCP por não esperar mais dados, certo? Não existe apenas FIN para sinalizar para não enviar mais dados?
Pavel Šimerda
3
@ PavelŠimerda Sim O TCP não sinaliza por não esperar mais dados. Isso deve ser considerado nos protocolos de alto nível. Na minha opinião, isso geralmente não é necessário. Você pode fechar a porta, mas não pode impedir as pessoas de colocar presentes na frente da porta. Esta é sua decisão, não sua.
Earth Engine
1
@EJP Você tem algum argumento real? Caso contrário, não estou interessado.
Pavel Šimerda 10/11
35

Existem algumas limitações close()que podem ser evitadas se alguém as usar shutdown().

close()terminará as duas direções em uma conexão TCP. Às vezes, você deseja dizer ao outro terminal que concluiu o envio de dados, mas ainda deseja receber dados.

close()diminui a contagem de referência dos descritores (mantida na entrada da tabela de arquivos e conta o número de descritores atualmente abertos que se referem a um arquivo / soquete) e não fecha o soquete / arquivo se o descritor não for 0. Isso significa que, se você estiver bifurcando, a limpeza ocorre somente depois que a contagem de referência cai para 0. Com isso, shutdown()é possível iniciar a seqüência normal de fechamento do TCP ignorando a contagem de referência.

Os parâmetros são os seguintes:

int shutdown(int s, int how); // s is socket descriptor

int how pode ser:

SHUT_RDou 0 Recebimentos adicionais não são permitidos

SHUT_WRou 1 Envios adicionais não são permitidos

SHUT_RDWRou 2 Mais envios e recebimentos não são permitidos

Milão
fonte
8
As duas funções são para propósitos completamente diferentes. O fato de o fechamento final em um soquete iniciar uma sequência de desligamento, se ainda não tiver sido iniciado, não altera o fato de que o fechamento é para limpar as estruturas de dados do soquete e o desligamento é para iniciar uma sequência de desligamento no nível tcp.
precisa
25
Você não pode 'usar o desligamento'. Você pode usá-lo também. mas você deve fechar o soquete algum tempo.
Marquês de Lorne
15

Isso pode ser específico da plataforma, de alguma forma duvido, mas, de qualquer maneira, a melhor explicação que já vi está aqui nesta página do msdn, na qual eles explicam sobre desligamentos, opções remanescentes, fechamento de soquetes e seqüências gerais de terminação de conexão.

Em resumo, use shutdown para enviar uma sequência de desligamento no nível TCP e use close para liberar os recursos usados ​​pelas estruturas de dados do soquete em seu processo. Se você não tiver emitido uma sequência de desligamento explícita no momento em que você fechar, uma será iniciada para você.

Len Holgate
fonte
9

Eu também tive sucesso no linux usando shutdown()de um pthread para forçar outro pthread atualmente bloqueado connect()para abortar mais cedo.

Em outros sistemas operacionais (OSX pelo menos), achei que a chamada close()era suficiente para connect()falhar.

Toby
fonte
7

"shutdown () na verdade não fecha o descritor de arquivo - apenas altera sua usabilidade. Para liberar um descritor de soquete, você precisa usar close ()." 1


fonte
3

Perto

Quando você terminar de usar um soquete, você pode simplesmente fechar seu descritor de arquivo com close; Se ainda houver dados aguardando para serem transmitidos pela conexão, normalmente o close tenta concluir esta transmissão. Você pode controlar esse comportamento usando a opção de soquete SO_LINGER para especificar um período de tempo limite; consulte Opções de soquete.

Desligar

Você também pode desligar apenas a recepção ou transmissão em uma conexão chamando shutdown.

A função de desligamento desliga a conexão do soquete. Seu argumento como especifica qual ação executar: 0 Pare de receber dados para esse soquete. Se mais dados chegarem, rejeite-os. 1 Pare de tentar transmitir dados deste soquete. Descartar todos os dados que aguardam para serem enviados. Pare de procurar confirmação de dados já enviados; não o retransmita se estiver perdido. 2 Pare a recepção e a transmissão.

O valor de retorno é 0 em caso de sucesso e -1 em caso de falha.

Neha Agrawal
fonte
2

no meu teste.

close enviará pacotes fin e destruirá o fd imediatamente quando o soquete não for compartilhado com outros processos

shutdown SHUT_RD , o processo ainda pode recuperar os dados do soquete, mas recvretornará 0 se o buffer TCP estiver vazio. Depois que o par enviar mais dados, recvos dados retornarão novamente.

shutdown SHUT_WR enviará pacotes fin para indicar que os envios adicionais não são permitidos. o par pode recuperar dados, mas recuperará 0 se seu buffer TCP estiver vazio

shutdown SHUT_RDWR (igual a usar SHUT_RD e SHUT_WR ) enviará o primeiro pacote se os pares enviarem mais dados.

simpx
fonte
Eu apenas tentei e close()enviei o RST no Linux em vez do FIN.
Pavel Šimerda
Você também gostaria de afirmar que, depois de desligar o lado da leitura, o programa receberá mais dados?
Pavel Šimerda 21/01
@ PavelŠimerda Eu acho que pode depender do estado tcp para enviar RST ou FIN? Não tenho certeza.
simpx
1. 'Depois que os colegas enviam mais dados, recv()eles retornam dados novamente' não está correto. 2. O comportamento se o par enviar mais dados depois SHUT_RDdepende da plataforma.
Marquês de Lorne #
@EJP Eu tentei, desculpe-me por esquecer de qual plataforma eu faço esse teste, centos ou Mac. e é isso que eu tenho
simpx