close vs socket de desligamento?

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

Como sobre o desligamento? A descrição disse que fecha metade de uma conexão duplex para esse soquete. Mas essa tomada será destruída como close chamada de sistema close ?

Isso é explicado no guia de rede da 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 é o caminho para realmente destruir um socket.

Com o shutdown , você ainda poderá receber os dados pendentes que o peer já enviou (graças a Joey Adams por notar isso).

Nenhuma das respostas existentes diz às pessoas como o shutdown e o close funcionam no nível do protocolo TCP, portanto, vale a pena adicionar isso.

Uma conexão TCP padrão é finalizada pela finalização de 4 vias:

  1. Quando um participante não tem mais dados para enviar, ele envia um pacote FIN para o outro
  2. A outra parte retorna um ACK para o FIN.
  3. Quando a outra parte também finalizou a transferência de dados, ela 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, o shutdown envia um pacote FIN para a outra extremidade, mas é tudo o que ele faz. Até que a outra parte envie o pacote FIN, você ainda poderá receber dados. Quando isso acontecer, sua Receive receberá um resultado de tamanho 0. Então, se você é o primeiro a desligar o “envio”, feche o soquete quando terminar de receber os dados.

Por outro lado, se você chamar close enquanto a conexão ainda está ativa (o outro lado ainda está ativo e você pode ter dados unsent no buffer do sistema também), 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?), É possível fechar o soquete imediatamente.

Minha opinião sobre as regras seria:

  1. Considere shutdown antes de close quando possível
  2. Se você terminou de receber (dados de tamanho 0 recebidos) antes de decidir encerrar, feche a conexão após o último envio (se houver) concluído.
  3. Se você deseja fechar a conexão normalmente, desligue a conexão (com SHUT_WR, e se você não se importa em receber dados após este ponto, com SHUT_RD também), e aguarde até receber um tamanho de dados 0 e feche o soquete.
  4. Em qualquer caso, se algum outro erro ocorreu (tempo limite, por exemplo), simplesmente feche 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 esta é uma maneira razoável e prática de fazer as coisas.

Se a pilha TCP receber um desligamento somente com SHUT_RD, ela deverá marcar essa conexão como nenhum outro dado esperado. Quaisquer solicitações de read pendentes e subsequentes (independentemente do thread em que estejam) 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 descartará todos os dados que receber para essa conexão. Mas isso é tudo, nenhum pacote será enviado para o outro lado.

Se a pilha TCP receber um desligamento apenas com SHUT_WR, ela deverá marcar essa conexão, pois nenhum outro dado poderá ser enviado. Todas as solicitações de gravação pendentes serão concluídas, mas as solicitações de gravação subseqüentes falharão. Além disso, um pacote FIN será enviado para outro lado para informá-los que não temos mais dados para enviar.

Existem algumas limitações com o close() que podem ser evitadas se você usar o shutdown() .

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

close() decrementa a contagem de referências dos descritores (mantida na input da tabela de arquivos e conta o número de descritores atualmente abertos que estão se referindo a um arquivo / socket) e não fecha o socket / arquivo se o descritor não for 0. Isto significa que se você forking, a limpeza ocorre somente depois que a contagem de referência cai para 0. Com o shutdown() é possível iniciar uma sequência de fechamento TCP normal, 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_RD ou 0 SHUT_RD adicionais não são permitidos

SHUT_WR ou 1 SHUT_WR adicionais não são permitidos

SHUT_RDWR ou 2 SHUT_RDWR e recebimentos adicionais não são permitidos

Isso pode ser específico da plataforma, eu duvido de alguma forma, mas de qualquer forma, a melhor explicação que eu vi está aqui nesta página msdn onde eles explicam sobre desligamento, opções de linger, fechamento de soquete e seqüências de terminação de conexão geral.

Em resumo, use o desligamento para enviar uma sequência de desligamento no nível TCP e use próximo para liberar os resources usados ​​pelas estruturas de dados do soquete em seu processo. Se você não tiver emitido uma seqüência explícita de desligamento até a hora em que você ligar, será iniciado um para você.

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

Em outros sistemas operacionais (OSX, pelo menos), eu encontrei chamando close() foi o suficiente para obter connect() falhar.

“O shutdown () na verdade não fecha o descritor de arquivo – ele apenas altera sua usabilidade. Para liberar um descritor de socket, você precisa usar o close ().” 1

Fechar

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

Desligar

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

A function de desligamento desliga a conexão do soquete. Seu argumento como especifica qual ação executar: 0 Parar de receber dados para este soquete. Se mais dados chegarem, rejeite-o. 1 Pare de tentar transmitir dados deste soquete. Descarte os dados que estão aguardando para serem enviados. Pare de procurar o reconhecimento de dados já enviados; não retransmiti-lo se estiver perdido. 2 Pare a recepção e a transmissão.

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

no meu teste.

close enviará o pacote de aleta e destruirá o fd imediatamente quando o soquete não for compartilhado com outros processos

shutdown SHUT_RD , o processo ainda pode receber dados do soquete, mas a recv retornará 0 se o buffer TCP estiver vazio.Após o peer enviar mais dados, a recv retornará os dados novamente.

shutdown SHUT_WR enviará pacote de aleta para indicar que as Envios adicionais não são permitidos. o par pode recv dados, mas ele irá receber 0 se o seu buffer TCP está vazio

shutdown SHUT_RDWR (igual para usar SHUT_RD e SHUT_WR ) enviará o primeiro pacote se o peer enviar mais dados.