Como fechar forçosamente um socket em TIME_WAIT?

110

Eu corro um programa em particular no linux que às vezes falha. Se você abri-lo rapidamente depois disso, ele escuta no soquete 49201 em vez de 49200 como fez na primeira vez. netstat revela que 49200 está em um estado TIME_WAIT.

Existe um programa que você pode executar para forçar imediatamente esse soquete a sair do estado TIME_WAIT?

    
por Rehan Khwaja 03.09.2008 / 12:57

7 respostas

139
/etc/init.d/networking restart

Deixe-me elaborar. O Protocolo de Controle de Transmissão (TCP) é projetado para ser um protocolo de transmissão de dados bidirecional, ordenado e confiável entre dois pontos finais (programas). Nesse contexto, o termo confiável significa que ele retransmitirá os pacotes se for perdido no meio. O TCP garante a confiabilidade enviando de volta pacotes de Confirmação (ACK) para um único ou um intervalo de pacotes recebidos do par.

Isso vale para os sinais de controle, como solicitação / resposta de término. O RFC 793 define o estado TIME-WAIT como segue:

TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

Veja o seguinte diagrama de estado do TCP:

TCPéumprotocolodecomunicaçãobidirecional,portanto,quandoaconexãoéestabelecida,nãohádiferençaentreoclienteeoservidor.Alémdisso,qualquerumpodeencerrar,eambososparesprecisamconcordaremfecharparafecharcompletamenteumaconexãoTCPestabelecida.

Vamoschamaroprimeiroparachamaroquitscomooativomaispróximo,eooutro,opassivomaispróximo.QuandoofechamentomaisativoenviaFIN,oestadovaiparaFIN-WAIT-1.EntãorecebeumACKparaoFINenviadoeoestadovaiparaFIN-WAIT-2.UmavezquerecebeFINtambémdopassivomaispróximo,oativomaispróximoenviaoACKparaoFINeoestadovaiparaTIME-WAIT.CasooclosepassivonãotenharecebidooACKparaosegundoFIN,eleretransmitiráopacoteFIN.

A RFC 793 define o TIME-OUT como o dobro da duração máxima do segmento, ou 2MSL. Desde MSL, o tempo máximo que um pacote pode percorrer pela Internet é definido como 2 minutos, 2MSL é 4 minutos. Como não há nenhum ACK para um ACK, o fechamento ativo não pode fazer nada além de esperar 4 minutos se ele aderir ao protocolo TCP / IP corretamente, caso o remetente passivo não tenha recebido o ACK ao FIN (teoricamente) .

Na realidade, os pacotes ausentes são provavelmente raros e muito raros se tudo estiver acontecendo dentro da LAN ou em uma única máquina.

Para responder a pergunta na íntegra, Como forçar fechar um soquete em TIME_WAIT ?, ainda vou me ater à minha resposta original:

/etc/init.d/networking restart

Praticamente falando, eu o programaria de modo a ignorar o estado TIME-WAIT usando a opção SO_REUSEADDR como o WMR mencionou. O que exatamente o SO_REUSEADDR faz?

This socket option tells the kernel that even if this port is busy (in
the TIME_WAIT state), go ahead and reuse it anyway. If it is busy, but with another state, you will still get an address already in use error. It is useful if your server has been shut down, and then restarted right away while sockets are still active on its port. You should be aware that if any unexpected data comes in, it may confuse your server, but while this is possible, it is not likely.

    
por 03.09.2008 / 13:11
50

Eu não sei se você tem o código-fonte daquele programa em particular que você está executando, mas se assim for você pode simplesmente configurar SO_REUSEADDR via setsockopt(2) , o que lhe permite ligar o mesmo endereço local mesmo que o socket seja no estado TIME_WAIT (a menos que esse socket esteja escutando ativamente, consulte socket(7) ).

Para mais informações sobre o estado TIME_WAIT, consulte a FAQ sobre o socket do Unix .

    
por 03.09.2008 / 13:17
32

Tanto quanto sei, não há como forçar o fechamento do soquete fora da gravação de um manipulador de sinal melhor em seu programa, mas existe um arquivo / proc que controla quanto tempo leva o tempo limite. O arquivo é

/proc/sys/net/ipv4/tcp_tw_recycle

e você pode definir o tempo limite como 1 segundo ao fazer isso:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

No entanto, esta página contém um aviso sobre possíveis problemas de confiabilidade ao definir essa variável.

Existe também um arquivo relacionado

/proc/sys/net/ipv4/tcp_tw_reuse

que controla se os soquetes TIME_WAIT podem ser reutilizados (presumivelmente sem tempo limite).

A propósito, a documentação do kernel avisa para você não alterar nenhum desses valores sem 'conselhos / solicitações de especialistas técnicos'. Que eu não sou.

O programa deve ter sido escrito para tentar uma ligação à porta 49200 e, em seguida, incrementar em 1 se a porta já estiver em uso. Portanto, se você tiver controle do código-fonte, poderá alterar esse comportamento para aguardar alguns segundos e tentar novamente na mesma porta, em vez de incrementar.

    
por 03.09.2008 / 13:24
16

Na verdade, existe uma maneira de eliminar uma conexão - killcx . Eles afirmam que funciona em qualquer estado da conexão (que eu não verifiquei). Você precisa conhecer a interface onde a comunicação acontece, parece assumir eth0 por padrão.

ATUALIZAÇÃO: outra solução é o cortador que vem em alguns repositórios de distros linux.

    
por 30.10.2011 / 17:32
3

Outra opção é usar a opção SO_LINGER com um tempo limite de 0. Desta forma, quando você fecha o socket é forçosamente fechado, enviando um RST ao invés de entrar no comportamento de fechamento FIN / ACK. Isso evitará o estado TIME_WAIT e poderá ser mais apropriado para alguns usos.

    
por 10.06.2010 / 22:33
2

Uma solução alternativa seria ter algum proxy confiável ou software de encaminhamento de porta que escuta na porta 49200, então encaminhar a conexão para uma das várias instâncias de seu programa menos confiável usando portas diferentes ... HAPROXY vem à mente. / p>

A propósito, a porta na qual você está se conectando é bem alta. Você poderia tentar usar um não utilizado logo acima do intervalo 0-1024. Seu sistema é menos propenso a usar um número de porta mais baixo como uma porta efêmera.

    
por 21.08.2014 / 20:28
0

TIME_WAIT é o problema mais comum na arquitetura do servidor cliente de programação de soquete. Aguarde alguns segundos tentando periodicamente é a melhor solução para isso. Para aplicações em tempo real, eles precisam que o servidor deve se levantar imediatamente Existe a opção SO_REUSEADDR para eles.

    
por 13.10.2011 / 19:07