Meu entendimento da conexão TCP é que uma PORTA de origem permanece exclusiva para uma conexão, não importa o destino, portanto o número de conexões da porta local 12345, por exemplo, nunca pode exceder 1.
Li recentemente que uma conexão TCP é identificada por <src IP, src PORT, dst IP, dst PORT>
O TCP permite o compartilhamento da porta de origem entre vários processos, mas cada processo requer uma porta livre para vincular sua conexão
então fui validar esse "compartilhamento de porta entre processos": isso deve significar que a mesma porta de origem pode ser usada para conectar-se a destinos diferentes.
No entanto, ao experimentar isso, tentei estes dois comandos:
nc -v -p 12345 google.com 80
que funciona bem (-v para verboso e -p para especificar a porta de origem como 12345, para meu propósito de aprendizado)
Agora, executando simultaneamente este comando em um shell diferente
nc -v -p 12345 github.com 80
falha com esta mensagem de erro:
nc: falha na conexão x à porta 80 do github.com (tcp): endereço já em uso
o motivo pelo qual especifiquei a mesma porta de origem com -p é para validar que uma porta de origem pode ser compartilhada. Na prática, não há necessidade disso, no cenário do mundo real eu nem me preocuparia com a porta de origem. De acordo com isso, é verdade que uma porta de origem será usada apenas uma vez?
Não há razão para que dois processos não possam usar a mesma porta de origem, desde que ambos não se conectem ao mesmo destino (host, porta). Em muitos sistemas UNIX, configure a
SO_REUSEPORT
opção para permitir que os processos compartilhem um número de porta; no Windows, definaSO_REUSEADDR
. Por exemplo, comsocat
:socat stdio tcp:google.com:80,bind=:12345,reuseport
socat stdio tcp:bing.com:80,bind=:12345,reuseport
Esses dois processos podem ser executados simultaneamente e ambos terão a porta de origem 12345 (como você pode confirmar em
netstat
).Observe, no entanto, que você quase certamente terá problemas se os soquetes não estiverem fechados corretamente em ambas as extremidades, pois os soquetes TCP não fechados entrarão em um
TIME_WAIT
estado que fará com que as quatro tuplas (srcaddr, srcport, dstaddr, dstport) sejam vinculadas até que o período de espera expire. Portanto, ao usar uma única porta de origem para múltiplas conexões, você não poderá se reconectar exatamente ao mesmo servidor e porta, a menos que a conexão anterior tenha sido totalmente encerrada ou oTIME_WAIT
período tenha expirado.Qualquer porta só é alocada para um único processo em qualquer momento. Esse processo pode criar qualquer número de conexões com ele, com a restrição de que o endereço IP de destino ou número da porta varie entre as conexões.
Por exemplo, o TCP 10.0.0.10:49152 pode conectar-se apenas uma vez ao TCP 10.0.0.2:80, mas pode ao mesmo tempo conectar-se ao TCP 10.0.0.3:80 ou ao TCP 10.0.0.2:8080.
Embora o próprio protocolo TCP permita combinações arbitrárias de portas e endereços locais e remotos, a maioria das implementações Unix simplifica o gerenciamento de portas. A razão é que o processo de configuração de soquetes é dividido em etapas separadas.
Primeiro você define a porta local com
bind()
. Esta etapa é necessária ao criar um soquete TCP de escuta (você deve especificar em qual porta ele está escutando), é opcional antes de fazer uma conexão de saídaconnect()
(uma porta local arbitrária será atribuída). Como ainda não sabemos o endereço remoto ou a porta, não é possível dizer se isso é totalmente exclusivo. Portanto, simplesmente verifica se a porta solicitada está disponível. Se o soquete tiver aSO_REUSEADDR
opção definida, ele ignorará os soquetes conectados ao verificar se o endereço local está em uso, mas ainda falhará se houver um soquete de escuta na porta.Então, para uma conexão de saída, você chama
connect()
, especificando o endereço remoto. Você só pode chamarconnect()
uma vez em um soquete e, como verificamos a porta local durantebind()
, isso nunca pode produzir um endereço/porta local/remoto duplicado.Para uma conexão de entrada, você chama
accept()
o soquete de escuta. Novamente, como verificamos se a porta local do soquete de escuta não está em uso quando a vinculamos, nunca poderá haver uma combinação duplicada.Atrasar as verificações das portas até que tenhamos as informações remotas complicaria o tratamento de erros. O design atual apenas verifica a duplicação em um só lugar:
bind()
.Após algumas investigações, descobri que o compartilhamento da porta de origem só é permitido para múltiplas conexões de saída do mesmo processo. O sistema operacional precisa saber para qual processo encaminhar o fluxo de conexão.
Selecionarei esta resposta em dois dias como política do site.