TCP: dois soquetes diferentes podem compartilhar uma porta?

124

Esta pode ser uma questão muito básica, mas me confunde.

Dois soquetes conectados diferentes podem compartilhar uma porta? Estou escrevendo um servidor de aplicativos que deve ser capaz de lidar com mais de 100k conexões simultâneas e sabemos que o número de portas disponíveis em um sistema é de cerca de 60k (16 bits). Um soquete conectado é atribuído a uma nova porta (dedicada), então isso significa que o número de conexões simultâneas é limitado pelo número de portas, a menos que vários soquetes possam compartilhar a mesma porta. Então, a pergunta.

Obrigado pela ajuda antecipadamente!

KJ
fonte

Respostas:

175

Um soquete de servidor escuta em uma única porta. Todas as conexões de cliente estabelecidas nesse servidor são associadas à mesma porta de escuta no lado do servidor da conexão. Uma conexão estabelecida é identificada exclusivamente pela combinação de pares IP / Porta do lado do cliente e do lado do servidor. Várias conexões no mesmo servidor podem compartilhar o mesmo par IP / porta do lado do servidor , desde que estejam associadas a diferentes pares IP / porta do lado do cliente , e o servidor seria capaz de lidar com tantos clientes quanto os recursos de sistema disponíveis permitirem para.

No lado do cliente , é uma prática comum que novas conexões de saída usem uma porta aleatória do lado do cliente , caso em que é possível esgotar as portas disponíveis se você fizer muitas conexões em um curto período de tempo.

Remy Lebeau
fonte
2
Obrigado pela resposta, Remy! Sua resposta é tudo o que eu estava curioso. ;)
KJ
2
As conexões @Remy são discriminadas não apenas pela porta / IP de origem / destino, mas também por um protocolo (TCP, UDP etc.), se não me engano.
Ondrej Peterka de
1
@OndraPeterka: sim, mas nem todas as plataformas restringem isso. Por exemplo, o Windows felizmente permite soquetes de servidor IPv4 e IPv6 separados para ouvir no mesmo IP local: Porta sem pular obstáculos, mas os sistemas * Nix (incluindo Linux e Android) não.
Remy Lebeau
6
@ user2268997: Você não pode usar um único soquete para se conectar a vários servidores. Você deve criar um soquete separado para cada conexão.
Remy Lebeau
3
@FernandoGonzalezSanchez: Um único cliente pode ter vários soquetes TCP vinculados ao mesmo par IP / Porta local, desde que estejam conectados a pares IP / Porta remotos diferentes. Isso não é específico do Windows, faz parte do funcionamento do TCP em geral.
Remy Lebeau
182

Escuta TCP / HTTP nas portas: como muitos usuários podem compartilhar a mesma porta

Então, o que acontece quando um servidor escuta conexões de entrada em uma porta TCP? Por exemplo, digamos que você tenha um servidor web na porta 80. Vamos supor que seu computador tenha o endereço IP público 24.14.181.229 e a pessoa que tenta se conectar a você tem o endereço IP 10.1.2.3. Essa pessoa pode se conectar a você abrindo um soquete TCP para 24.14.181.229:80. Simples o suficiente.

Intuitivamente (e erroneamente), a maioria das pessoas presume que seja algo assim:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

Isso é intuitivo, pois do ponto de vista do cliente, ele possui um endereço IP, e se conecta a um servidor em IP: PORT. Já que o cliente se conecta à porta 80, a porta dele também deve ser 80? É uma coisa sensata de se pensar, mas na verdade não é o que acontece. Se isso fosse correto, poderíamos servir apenas um usuário por endereço IP estrangeiro. Depois que um computador remoto se conecta, ele monopoliza a conexão da porta 80 para a porta 80 e ninguém mais consegue se conectar.

Três coisas devem ser entendidas:

1.) Em um servidor, um processo está escutando em uma porta. Assim que consegue uma conexão, ele passa para outro segmento. A comunicação nunca monopoliza a porta de escuta.

2.) As conexões são identificadas exclusivamente pelo sistema operacional pela seguinte 5-tupla: (local-IP, local-porta, remoto-IP, porta remota, protocolo). Se qualquer elemento da tupla for diferente, então esta é uma conexão completamente independente.

3.) Quando um cliente se conecta a um servidor, ele escolhe uma porta de origem de alta ordem aleatória e não utilizada . Dessa forma, um único cliente pode ter até ~ 64k conexões com o servidor para a mesma porta de destino.

Então, isso é realmente o que é criado quando um cliente se conecta a um servidor:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

Olhando para o que realmente acontece

Primeiro, vamos usar o netstat para ver o que está acontecendo neste computador. Usaremos a porta 500 em vez de 80 (porque um monte de coisas está acontecendo na porta 80, pois é uma porta comum, mas funcionalmente não faz diferença).

    netstat -atnp | grep -i ":500 "

Como esperado, a saída está em branco. Agora vamos iniciar um servidor web:

    sudo python3 -m http.server 500

Agora, aqui está o resultado da execução de netstat novamente:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

Portanto, agora existe um processo que está ouvindo ativamente (Estado: LISTEN) na porta 500. O endereço local é 0.0.0.0, que é o código para "ouvir todos os endereços IP". Um erro fácil de cometer é escutar apenas na porta 127.0.0.1, que só aceita conexões do computador atual. Portanto, esta não é uma conexão, isso significa apenas que um processo solicitou o bind () ao IP da porta, e esse processo é responsável por lidar com todas as conexões para essa porta. Isso sugere a limitação de que só pode haver um processo por computador ouvindo em uma porta (há maneiras de contornar isso usando a multiplexação, mas este é um tópico muito mais complicado). Se um servidor web estiver escutando na porta 80, ele não pode compartilhar essa porta com outros servidores web.

Agora, vamos conectar um usuário à nossa máquina:

    quicknet -m tcp -t localhost:500 -p Test payload.

Este é um script simples ( https://github.com/grokit/quickweb ) que abre um soquete TCP, envia a carga ("Carga de teste." Neste caso), espera alguns segundos e se desconecta. Executar netstat novamente enquanto isso está acontecendo exibe o seguinte:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

Se você se conectar a outro cliente e executar o netstat novamente, verá o seguinte:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... ou seja, o cliente usou outra porta aleatória para a conexão. Portanto, nunca há confusão entre os endereços IP.

Nada
fonte
11
Esta é a melhor resposta que já vi no SO.
Trabalhos de
1
@ N0thing "Desta forma, um único cliente pode ter até ~ 64k conexões com o servidor para a mesma porta de destino." Portanto, na prática, se um cliente não se conectar ao mesmo servidor e porta, duas ou várias vezes ao mesmo tempo, um cliente pode ter até mais do que ~ 64K conexões. Isso é verdade. Se sim, isso significa que a partir de uma única porta do lado do cliente pode haver conexão com vários processos de servidor diferentes (de forma que a conexão de soquete seja diferente). Portanto, ao todo, vários sockets de cliente podem residir na mesma porta na máquina cliente? Por favor, leia meu comentário para a resposta de "Remey Lebeau". Obrigado: D
Prem KTiw
6
@premktiw: Sim, vários sockets de cliente podem ser associados ao mesmo par de IP / porta local ao mesmo tempo, se eles estiverem conectados a pares de IP / porta de servidor diferentes, de forma que as tuplas de pares locais + remotos sejam exclusivas. E sim, é possível que um cliente tenha mais de 64 mil conexões simultâneas no total. A partir de uma única porta, ele pode ser conectado a um número potencialmente infinito de servidores (limitado por recursos de sistema operacional disponíveis, portas de roteador disponíveis etc.), desde que os pares de portas / IP do servidor sejam exclusivos.
Remy Lebeau
1
@RemyLebeau Satisfeito. Muito obrigado: D
Prem KTiw
1
@bibstha Como o firewall lida com portas aleatórias, quando todas as conexões de entrada são rejeitadas?
PatrykG
35

Um soquete conectado é atribuído a uma nova porta (dedicada)

Essa é uma intuição comum, mas está incorreta. Um soquete conectado não está atribuído a uma porta nova / dedicada. A única restrição real que a pilha TCP deve satisfazer é que a tupla de (local_address, local_port, remote_address, remote_port) deve ser única para cada conexão de socket. Assim, o servidor pode ter muitos sockets TCP usando a mesma porta local, desde que cada um dos sockets na porta esteja conectado a um local remoto diferente.

Consulte o parágrafo "Par de soquetes" em: http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false

Jeremy Friesner
fonte
1
Obrigado pela resposta perfeita, Jeremy!
KJ
6
O que você diz é inteiramente verdadeiro no lado do servidor. No entanto, a estrutura da API BSD Sockets significa que as portas de saída do lado do cliente devem ser exclusivas na prática, porque a bind()operação precede a connect()operação, mesmo implicitamente.
Marquês de Lorne
1
@EJP Olá, pensei que bind()só fosse usado antes no lado do servidor. accept()?Então, o lado do cliente também vinculará a porta específica?
GMsoF
5
@GMsoF: bind()pode ser usado no lado do cliente antes connect().
Remy Lebeau
10

Teoricamente sim. Pratique, não. A maioria dos kernels (incl. Linux) não permite um segundo bind()para uma porta já alocada. Não foi um remendo muito grande tornar isso permitido.

Conceitualmente, devemos diferenciar entre soquete e porta . Sockets são terminais de comunicação bidirecionais, ou seja, "coisas" onde podemos enviar e receber bytes. É algo conceitual, não existe tal campo em um cabeçalho de pacote denominado "socket".

A porta é um identificador capaz de identificar um soquete. No caso do TCP, uma porta é um inteiro de 16 bits, mas também existem outros protocolos (por exemplo, em soquetes unix, uma "porta" é essencialmente uma string).

O principal problema é o seguinte: se um pacote de entrada chega, o kernel pode identificar seu soquete pelo número da porta de destino. É a forma mais comum, mas não é a única possibilidade:

  • Os soquetes podem ser identificados pelo IP de destino dos pacotes de entrada. É o caso, por exemplo, se tivermos um servidor usando dois IPs simultaneamente. Então podemos rodar, por exemplo, diferentes servidores web nas mesmas portas, mas em diferentes IPs.
  • Os soquetes podem ser identificados por sua porta de origem e ip também. Esse é o caso em muitas configurações de balanceamento de carga.

Como você está trabalhando em um servidor de aplicativos, ele poderá fazer isso.

peterh - Reintegrar Monica
fonte
2
Ele não perguntou sobre fazer um segundo bind().
Marquês de Lorne
1
@ user207421 Você já viu um sistema operacional em que os soquetes de escuta não são configurados bind()? Eu posso imaginar, sim é bem possível, mas o fato é que tanto o WinSock quanto a API Posix usam a bind()chamada para isso, mesmo a parametrização deles é praticamente a mesma. Mesmo que uma API não tenha essa chamada, de alguma forma você precisa dizê-la de onde deseja ler os bytes de entrada .
peterh - Reintegrar Monica
1
@ user207421 Claro que 100k ou mais conexões TCP podem ser tratadas com as mesmas portas, chamadas de listen()/ accept()API podem criar os sockets de uma maneira que o kernel os diferencie por suas portas de entrada. A questão do OP pode ser interpretada na forma como ele a pede essencialmente. Acho que é bastante realista, mas não é isso que sua pergunta significa literalmente.
peterh - Restabelecer Monica
1

Não. Não é possível compartilhar a mesma porta em um determinado instante. Mas você pode fazer sua aplicação de forma que ela faça o acesso à porta em instantes diferentes.

SAKEER T
fonte