Acho que entendo o significado formal da opção. Em algum código legado que estou lidando agora, a opção é usada. O cliente reclama do RST como resposta ao FIN de seu lado na conexão próxima de seu lado.
Não tenho certeza se posso removê-lo com segurança, pois não entendo quando ele deve ser usado.
Você pode dar um exemplo de quando a opção seria necessária?
sockets
networking
tcp
setsockopt
so-linger
dimba
fonte
fonte
Respostas:
O motivo típico para definir um
SO_LINGER
tempo limite de zero é evitar um grande número de conexões paradas noTIME_WAIT
estado, ocupando todos os recursos disponíveis em um servidor.Quando uma conexão TCP é fechada de forma limpa, o fim que iniciou o fechamento ("fechamento ativo") termina com a conexão parada
TIME_WAIT
por vários minutos. Portanto, se o seu protocolo for aquele em que o servidor inicia o fechamento da conexão e envolve um grande número de conexões de curta duração, ele pode ser suscetível a esse problema.Esta não é uma boa idéia, porém -
TIME_WAIT
existe por uma razão (para garantir que pacotes perdidos de conexões antigas não interfiram com as novas). É uma ideia melhor redesenhar seu protocolo para um em que o cliente inicie o fechamento da conexão, se possível.fonte
TIME_WAIT
o cliente ficará sentado diante do cliente sem causar danos. Lembre-se do que diz em "Programação de rede UNIX" terceira edição (Stevens et al), página 203: "O estado TIME_WAIT é seu amigo e está lá para nos ajudar. Em vez de tentar evitar o estado, devemos entendê-lo (Seção 2.7) . "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:
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
close()
FIN
para BFIN_WAIT_1
estadoFIN
ACK
para ACLOSE_WAIT
estadoACK
FIN_WAIT_2
estadoclose()
FIN
para ALAST_ACK
estadoFIN
ACK
para BTIME_WAIT
estadoACK
CLOSED
estado - ou seja, é removido das tabelas de soqueteTEMPO DE ESPERA
Portanto, o par que inicia a terminação - ou seja, as chamadas
close()
primeiro - terminará noTIME_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 chamadaclose()
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á umRST
(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 chamarclose()
- a partir de agora chamado de fechamento abortivo - em um aplicativo de servidor.No entanto, certas situações justificam isso de qualquer maneira:
CLOSE_WAIT
ou terminar noTIME_WAIT
estado.TIME_WAIT
(ao chamarclose()
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.CLOSE_WAIT
tentar entregar dados a um terminal travado porta, mas redefiniria corretamente a porta travada se conseguisse umRST
para descartar os dados pendentes. "Eu recomendaria este longo artigo, que acredito dar uma resposta muito boa à sua pergunta.
fonte
TIME_WAIT
é um amigo somente quando não começa a causar problemas: stackoverflow.com/questions/1803566/…Quando o linger está ativado, mas o tempo limite é zero, a pilha TCP não espera que os dados pendentes sejam enviados antes de fechar a conexão. Os dados podem ser perdidos devido a isso, mas ao definir linger dessa maneira, você está aceitando isso e pedindo que a conexão seja redefinida imediatamente em vez de encerrada normalmente. Isso faz com que um RST seja enviado em vez do FIN usual.
Agradecimentos ao EJP pelo comentário, veja aqui mais detalhes.
fonte
Se você pode remover o linger em seu código com segurança ou não depende do tipo de seu aplicativo: é um "cliente" (abrindo conexões TCP e fechando-as ativamente primeiro) ou é um "servidor" (ouvindo um TCP aberto e fechá-lo após o outro lado iniciar o fechamento)?
Se seu aplicativo tem o sabor de um "cliente" (fechando primeiro) E você inicia e fecha um grande número de conexões a diferentes servidores (por exemplo, quando seu aplicativo é um aplicativo de monitoramento supervisionando a acessibilidade de um grande número de servidores diferentes) seu aplicativo tem o problema de que todas as suas conexões de cliente estão travadas no estado TIME_WAIT. Em seguida, eu recomendaria reduzir o tempo limite para um valor menor do que o padrão para ainda desligar normalmente, mas liberar os recursos de conexões do cliente mais cedo. Eu não definiria o tempo limite como 0, pois 0 não encerra normalmente com FIN, mas aborta com RST.
Se a sua aplicação tem o sabor de um "cliente" e precisa buscar uma grande quantidade de pequenos arquivos do mesmo servidor, você não deve iniciar uma nova conexão TCP por arquivo e acabar em uma grande quantidade de conexões de cliente em TIME_WAIT, mas mantenha a conexão aberta e busque todos os dados na mesma conexão. A opção Linger pode e deve ser removida.
Se a sua aplicação for um “servidor” (fechar segundo como reação ao fechamento do par), em close () sua conexão é desligada normalmente e os recursos são liberados porque você não entra no estado TIME_WAIT. Linger não deve ser usado. Mas se o seu aplicativo de servidor possui um processo de supervisão que detecta conexões abertas inativas que ficam ociosas por um longo tempo ("longo" deve ser definido), você pode desligar essa conexão inativa do seu lado - veja isso como um tipo de tratamento de erro - com um desligamento abortivo. Isso é feito definindo o tempo limite de linger para 0. close () irá então enviar um RST para o cliente, dizendo a ele que você está bravo :-)
fonte
Em servidores, você pode querer enviar em
RST
vez deFIN
ao desconectar clientes com comportamento inadequado. Isso pulaFIN-WAIT
seguido porTIME-WAIT
estados de soquete no servidor, o que evita o esgotamento dos recursos do servidor e, portanto, protege contra esse tipo de ataque de negação de serviço.fonte
Gosto da observação de Maxim de que os ataques DOS podem exaurir os recursos do servidor. Isso também acontece sem um adversário realmente malicioso.
Alguns servidores têm que lidar com o 'ataque DOS não intencional' que ocorre quando o aplicativo cliente tem um bug com vazamento de conexão, onde eles continuam criando uma nova conexão para cada novo comando que enviam ao seu servidor. E então, talvez, eventualmente, fechando suas conexões se atingirem a pressão do GC, ou talvez as conexões acabem se esgotando.
Outro cenário é quando 'todos os clientes têm o mesmo endereço TCP'. Então, as conexões do cliente são distinguíveis apenas por números de porta (se conectarem a um único servidor). E se os clientes começarem a abrir / fechar conexões rapidamente por qualquer motivo, eles podem esgotar o espaço de tupla (endereço do cliente + porta, IP do servidor + porta).
Portanto, acho que os servidores podem ser mais aconselhados a mudar para a estratégia Linger-Zero quando virem um grande número de soquetes no estado TIME_WAIT - embora isso não corrija o comportamento do cliente, pode reduzir o impacto.
fonte
O soquete de escuta em um servidor pode usar linger com tempo 0 para ter acesso à ligação de volta ao soquete imediatamente e para redefinir quaisquer clientes cujas conexões ainda não tenham terminado de conectar. TIME_WAIT é algo que só é interessante quando você tem uma rede de múltiplos caminhos e pode acabar com pacotes mal ordenados ou de outra forma está lidando com pedidos / tempos de chegada de pacotes de rede estranhos.
fonte