Para minha sugestão, por favor leia a última seção: “Quando usar SO_LINGER com tempo limite 0” .
Antes de entrarmos nisso, uma pequena palestra sobre:
- Terminação TCP normal
TIME_WAIT
FIN
, ACK
eRST
Terminação TCP normal
A sequência normal de terminação do TCP é semelhante a esta (simplificada):
Temos dois pares: A e B
- A chama
close()
- A envia
FIN
para B
- A entra no
FIN_WAIT_1
estado
- B recebe
FIN
- B envia
ACK
para A
- B entra no
CLOSE_WAIT
estado
- A recebe
ACK
- A entra no
FIN_WAIT_2
estado
- B chama
close()
- B envia
FIN
para A
- B entra no
LAST_ACK
estado
- A recebe
FIN
- A envia
ACK
para B
- A entra no
TIME_WAIT
estado
- B recebe
ACK
- B vai para o
CLOSED
estado - ou seja, é removido das tabelas de soquete
TEMPO DE ESPERA
Portanto, o par que inicia a terminação - ou seja, as chamadas close()
primeiro - terminará no TIME_WAIT
estado.
Para entender por que o TIME_WAIT
estado é nosso amigo, leia a seção 2.7 em "Programação de rede UNIX" terceira edição de Stevens et al (página 43).
No entanto, pode ser um problema com muitos soquetes em TIME_WAIT
estado em um servidor, pois pode impedir que novas conexões sejam aceitas.
Para contornar esse problema, tenho visto muitos sugerindo definir a opção de soquete SO_LINGER com tempo limite 0 antes de chamar close()
. No entanto, essa é uma solução ruim, pois faz com que a conexão TCP seja encerrada com um erro.
Em vez disso, projete o protocolo do aplicativo de forma que o encerramento da conexão seja sempre iniciado no lado do cliente. Se o cliente sempre souber quando leu todos os dados restantes, ele pode iniciar a sequência de encerramento. Por exemplo, um navegador sabe pelo Content-Length
cabeçalho HTTP quando leu todos os dados e pode iniciar o fechamento. (Eu sei que no HTTP 1.1 ele vai mantê-lo aberto por um tempo para uma possível reutilização, e então fechá-lo.)
Se o servidor precisar fechar a conexão, projete o protocolo do aplicativo de forma que o servidor peça ao cliente para ligar close()
.
Quando usar SO_LINGER com tempo limite 0
Mais uma vez, de acordo com a terceira edição da página 202-203 de "Programação de rede UNIX", definir SO_LINGER
com o tempo limite 0 antes da chamada close()
fará com que a sequência de encerramento normal não seja iniciada.
Em vez disso, o par configurando esta opção e chamando close()
enviará um RST
(reset de conexão) que indica uma condição de erro e é assim que será percebida na outra extremidade. Normalmente, você verá erros como "Conexão redefinida por par".
Portanto, na situação normal, é realmente uma má ideia definir SO_LINGER
com o tempo limite 0 antes de chamar close()
- a partir de agora chamado de fechamento abortivo - em um aplicativo de servidor.
No entanto, certas situações justificam isso de qualquer maneira:
- Se o cliente de seu aplicativo de servidor se comportar mal (tempo limite, retornar dados inválidos, etc.), um fechamento abortivo faz sentido para evitar ficar preso
CLOSE_WAIT
ou terminar no TIME_WAIT
estado.
- Se você deve reiniciar seu aplicativo de servidor que atualmente tem milhares de conexões de cliente, você pode considerar definir esta opção de soquete para evitar milhares de soquetes de servidor
TIME_WAIT
(ao chamar close()
do lado do servidor), pois isso pode impedir que o servidor obtenha portas disponíveis para novas conexões de cliente após ser reiniciado.
- Na página 202 do livro mencionado, ele diz especificamente: "Existem certas circunstâncias que justificam o uso deste recurso para enviar um fechamento abortivo. Um exemplo é um servidor de terminal RS-232, que pode travar para sempre ao
CLOSE_WAIT
tentar entregar dados a um terminal travado porta, mas redefiniria corretamente a porta travada se conseguisse um RST
para descartar os dados pendentes. "
Eu recomendaria este longo artigo, que acredito dar uma resposta muito boa à sua pergunta.