Como fechar um soquete em TIME_WAIT?

113

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

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

Rehan Khwaja
fonte
11
Se você está aqui devido a "muitos TIME_WAITservidores" , basta pular as três primeiras respostas que evitam a pergunta em vez de respondê-la.
Pacerier

Respostas:

148
/etc/init.d/networking restart

Deixe-me elaborar. O TCP (Transmission Control Protocol) foi 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 ele se perder no meio. O TCP garante a confiabilidade enviando de volta os pacotes de confirmação (ACK) para um único ou vários pacotes recebidos do par.

O mesmo ocorre com os sinais de controle, como solicitação / resposta de término. O RFC 793 define o estado TIME-WAIT da seguinte maneira:

TIME-WAIT - representa a espera de tempo suficiente para garantir que o TCP remoto receba o reconhecimento de sua solicitação de encerramento de conexão.

Consulte o seguinte diagrama de estado TCP: texto alternativo

O TCP é um protocolo de comunicação bidirecional; portanto, quando a conexão é estabelecida, não há diferença entre o cliente e o servidor. Além disso, qualquer um pode chamar encerra e ambos os pares precisam concordar em fechar para fechar completamente uma conexão TCP estabelecida.

Vamos chamar o primeiro a chamar as quits como o ativo mais próximo e o outro o passivo mais próximo. Quando o ativo mais próximo envia FIN, o estado passa para FIN-WAIT-1. Em seguida, ele recebe um ACK para o FIN enviado e o estado passa para FIN-WAIT-2. Depois de receber o FIN também do passivo mais próximo, o ativo mais próximo envia o ACK ao FIN e o estado passa para TIME-WAIT. Caso o passivo mais próximo não tenha recebido o ACK do segundo FIN, ele retransmitirá o pacote FIN.

O RFC 793 define o TEMPO LIMITE para ser o dobro da Vida útil máxima do segmento, ou 2MSL. Como o MSL, o tempo máximo que um pacote pode percorrer na Internet é definido para 2 minutos, 2MSL é de 4 minutos. Como não há ACK para um ACK, o ativo mais próximo não pode fazer nada além de esperar 4 minutos se aderir corretamente ao protocolo TCP / IP, apenas no caso de o remetente passivo não ter recebido o ACK em sua FIN (teoricamente) .

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

Para responder à pergunta na íntegra, Como forçosamente fechar um soquete em TIME_WAIT ?, eu ainda ficar com a minha resposta original:

/etc/init.d/networking restart

Na prática, eu o programaria para que ele ignore o estado TIME-WAIT usando a opção SO_REUSEADDR como o WMR mencionado. O que exatamente SO_REUSEADDR faz?

Esta opção de soquete informa ao kernel que, mesmo que essa porta esteja ocupada (no
estado TIME_WAIT), vá em frente e reutilize-a assim mesmo. Se estiver ocupado, mas com outro estado, você ainda receberá um erro de endereço já em uso. É útil se o servidor tiver sido desligado e reiniciado imediatamente enquanto os soquetes ainda estiverem ativos em sua porta. Você deve estar ciente de que, se houver dados inesperados, isso poderá confundir seu servidor, mas, embora isso seja possível, não é provável.

Eugene Yokota
fonte
8
Ótima resposta, mas não a resposta correta para sua pergunta. Reiniciar a rede funcionaria, mas também reiniciaria, portanto, isso não pode estar certo.
Chris Huang-Leaver
3
@ Chris Huang-Leaver, a pergunta é "Existe um programa que você possa executar para forçar imediatamente esse soquete a sair do estado TIME_WAIT?" se a reinicialização puder ser considerada executando um programa, também seria a resposta certa. Por que você acha que isso não pode estar certo?
Eugene Yokota
8
O WMR tem a resposta mais útil (que é o que faço quando encontro esse tipo de problema). Reiniciar a rede é muito drástico para ser uma solução e pode demorar mais do que simplesmente esperar o tempo limite. A resposta correta para sua pergunta é 'Não', mas SO não permite que você digite duas respostas com letras :-)
Chris Huang- Leaver
6
tudo bem, da próxima vez que algum processo travar no SIGTERM, eu apenas esmagarei meu computador em vez de corrigi-lo.
Longpoke
A generalização disso é "reiniciar serviços de rede". A localização específica /etc/init.d/networkingé específica da plataforma (Debian?), Portanto a linha de comando precisa será diferente (às vezes radicalmente) para outros sistemas. Concordo com outros comentaristas que isso parece um exagero grave e obviamente perturbador para quaisquer serviços de rede não relacionados.
tripleee 26/06
51

Não sei se você tem o código-fonte desse programa específico em execução, mas se sim, basta definir SO_REUSEADDR através do setsockopt(2)qual permite vincular o mesmo endereço local, mesmo que o soquete esteja no estado TIME_WAIT (a menos que tomada está ouvindo ativamente, consulte socket(7)).

Para mais informações sobre o estado TIME_WAIT, consulte as perguntas freqüentes sobre o soquete Unix .

WMR
fonte
mas eu não recebi o erro já vinculado. Quando executo o programa novamente, ele escuta no post (123456). Também vejo que o sistema está mostrando TIME_WAIT para essa porta, mas ainda assim consigo conectar. porque?
precisa saber é o seguinte
2
Mesmo com SO_REUSEADDR, ainda é possível obter o erro "Endereço já em uso". Para detalhes, consulte hea-www.harvard.edu/~fine/Tech/addrinuse.html .
Jingguo Yao 27/06
O @WMR SO_REUSEADDRnão "fecha" um soquete. Apenas permite reutilizar aqueles que já estão abertos. Portanto, a pergunta ainda é "Como fechar um soquete à força TIME_WAIT?"
Pacerier
Esta é a resposta correta, mas a pergunta não estava totalmente correta. Pelo menos resolvi bem o meu problema (não é como reiniciar toda a rede quebrando todas as outras conexões também).
V-Mark
SO_REUSEADDRdeixará bind()prosseguir; mas se você quiser ouvir esse soquete, listen()retornará EADDRINUSEo mesmo. Em outras palavras, essa resposta pode ajudar o software cliente a usar portas efêmeras, mas não resolve o problema do software para servidor.
Will
33

Até onde eu sei, não há como forçar o fechamento do soquete fora da gravação de um manipulador de sinal melhor no 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 para 1 segundo fazendo o seguinte:

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.

Há 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).

Aliás, a documentação do kernel avisa que você não deve alterar nenhum desses valores sem 'conselhos / solicitações de especialistas técnicos'. O que eu não sou.

O programa deve ter sido gravado para tentar uma ligação à porta 49200 e depois 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.

Leigh Caldwell
fonte
acho que os segundos dois exemplos deveriam ser s / rw / tw / eu editaria, mas falta rep suficiente.
11
Retirado da documentação do kernel: Cuidado. Tcp_tw_recycle e tcp_tw_reuse podem causar problemas. Você não deve ativar sem entender a topologia de rede entre os nós que estão usando ou usados ​​pelo nó em que o parâmetro está ativado. As conexões que passam por nós que estão cientes dos estados de conexão TCP, como firewall, NAT ou balanceador de carga, podem começar a descartar quadros por causa da configuração. O problema ficará visível quando houver um número suficientemente grande de conexões.
Configurá-lo para 1funcionar em conexões futuras, mas e as atuais que já estão abertas?
Pacerier
18

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

UPDATE: outra solução é o cutter, que vem nos repositórios de algumas distribuições Linux.

akostadinov
fonte
3

Outra opção é usar a opção SO_LINGER com um tempo limite de 0. Dessa forma, quando você fecha o soquete, é forçado a fechar, enviando um RST em vez de entrar no comportamento de fechamento FIN / ACK. Isso evitará o estado TIME_WAIT e poderá ser mais apropriado para alguns usos.


fonte
2
Ele também perde todos os dados de saída que ainda estão em trânsito e pode causar um erro na outra extremidade. Não recomendado.
user207421
@EJP Falhar cedo é quase sempre a ligação certa. O trabalho em rede não é confiável, e os combates que atrasarão as coisas. Um aplicativo com falha não pode assumir que nenhum dado foi produzido com segurança.
Tobu
11
Na verdade, eu recomendaria isso em qualquer dia em que o outro ponto de extremidade seja um gateway de barramento industrial de buggy incorporado que implemente seu próprio transporte confiável da camada de aplicativos sobre TCP, onde o transporte evita que a conexão se feche a menos que receba RST e, assim, preencha o limite de conexão nesse gateway. Lá. Dei a você um exemplo muito específico e real que, infelizmente, exige o recurso a hacks como esse.
andyn
O @Tobu Networking não é confiável, mas o TCP tenta ser, e piorar isso não significa tornar nada melhor, e deixar o TCP fazer seu trabalho não constitui 'lutar' com nada.
user207421
2

Uma solução alternativa seria ter algum software confiável de proxy ou encaminhamento de porta que escute na porta 49200 e, em seguida, encaminhe a conexão para uma das várias instâncias do seu programa menos confiável usando portas diferentes ... O HAPROXY vem à mente.

Aliás, a porta em que você está se conectando é bastante alta. Você pode tentar usar um não usado logo acima do intervalo de 0 a 1024. É menos provável que seu sistema use um número de porta menor como uma porta efêmera.

andrew pate
fonte
0

TIME_WAIT é o problema mais comum na arquitetura de servidor cliente de programação de soquete. Aguarde alguns segundos, tentando periodicamente é a melhor solução para isso. Para aplicativos em tempo real, eles precisam que o servidor seja levantado imediatamente. Existe a opção SO_REUSEADDR para eles.


fonte