Eu corro um programa específico no linux que às vezes trava. 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?
Deixe-me elaborar. O Transmission Control Protocol (TCP) 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 se perder no meio. O TCP garante a confiabilidade enviando de volta os pacotes de confirmação (ACK) para um único ou um intervalo de pacotes recebidos do peer.
Isso vale para os sinais de controle, como solicitação/resposta de término. A RFC 793 define o estado TIME-WAIT como segue:
Veja o seguinte diagrama de estado TCP:
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 encerrar e ambos os pares precisam concordar em fechar para fechar completamente uma conexão TCP estabelecida.
Vamos chamar o primeiro para chamar as saídas como o ativo mais próximo, e o outro para o passivo mais próximo. Quando o ativo mais próximo envia FIN, o estado vai para FIN-WAIT-1. Em seguida, ele recebe um ACK para o FIN enviado e o estado vai para FIN-WAIT-2. Uma vez que recebe FIN também do mais próximo passivo, o mais próximo ativo envia o ACK para o FIN e o estado vai para TIME-WAIT. Caso o passivo mais próximo não tenha recebido o ACK para o segundo FIN, ele retransmitirá o pacote FIN.
O RFC 793 define o TIME-OUT para ser duas vezes o Maximum Segment Lifetime, ou 2MSL. Como o MSL, o tempo máximo que um pacote pode percorrer a Internet, é definido como 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 ao protocolo TCP/IP corretamente, caso o remetente passivo não tenha recebido o ACK em seu FIN (teoricamente) .
Na realidade, os pacotes ausentes são provavelmente raros, e muito raros se tudo estiver acontecendo na LAN ou em uma única máquina.
Para responder à pergunta literalmente, Como fechar um soquete à força em TIME_WAIT?, ainda vou manter minha resposta original:
Praticamente falando, eu o programaria para que ele ignorasse o estado TIME-WAIT usando a opção SO_REUSEADDR como WMR mencionado. O que exatamente SO_REUSEDDR faz?
Eu não sei se você tem o código-fonte desse programa específico que você está executando, mas se tiver, você pode definir SO_REUSEADDR via
setsockopt(2)
que permite vincular no mesmo endereço local, mesmo que o soquete esteja no estado TIME_WAIT (a menos que socket está escutando ativamente, vejasocket(7)
).Para obter mais informações sobre o estado TIME_WAIT, consulte o FAQ do soquete Unix .
Até onde eu sei, não há como forçar o fechamento do soquete fora de escrever um manipulador de sinal melhor em seu programa, mas existe um arquivo /proc que controla quanto tempo leva o tempo limite. O arquivo é
e você pode definir o tempo limite para 1 segundo fazendo isso:
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
que controla se os soquetes TIME_WAIT podem ser reutilizados (presumivelmente sem nenhum tempo limite).
Aliás, a documentação do kernel avisa para não alterar nenhum desses valores sem 'conselhos/pedidos de especialistas técnicos'. O que eu não sou.
O programa deve ter sido escrito para tentar uma ligação à porta 49200 e então incrementar em 1 se a porta já estiver em uso. Portanto, se você tiver o controle do código-fonte, poderá alterar esse comportamento para aguardar alguns segundos e tentar novamente na mesma porta, em vez de incrementar.
Na verdade, existe uma maneira de matar uma conexão - killcx . Eles afirmam que funciona em qualquer estado da conexão (que não verifiquei). Você precisa conhecer a interface onde a comunicação acontece, porém, parece assumir eth0 por padrão.
ATUALIZAÇÃO: outra solução é o cortador que vem em alguns repositórios de distribuições linux.
Outra opção é usar a opção SO_LINGER com timeout 0. Desta forma, ao fechar 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 pode ser mais apropriado para alguns usos.
Uma solução alternativa seria ter algum proxy confiável ou software de encaminhamento de porta que escute na porta 49200, então encaminhe a conexão para uma das várias instâncias de seu programa menos confiável usando portas diferentes... HAPROXY vem à mente.
Aliás, a porta de sua conexão é bastante alta. Você pode tentar usar um não utilizado logo acima do intervalo 0-1024. É menos provável que seu sistema use um número de porta menor como uma porta efêmera.
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 aplicativos em tempo real, eles precisam do servidor deve se levantar imediatamente. Existe a opção SO_REUSEADDR para eles.