socket connect () vs bind ()

121

O sistema connect()e as bind()chamadas 'associam' o descritor do arquivo de soquete a um endereço (normalmente uma combinação de ip / porta). Seus protótipos são como: -

int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);

e

int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);

Qual é a diferença exata entre 2 chamadas? Quando alguém deve usar connect()e quando bind()?

Especificamente, em alguns códigos de cliente de servidor de amostra, constatou que o cliente está usando connect()e o servidor está usando a bind()chamada. A razão não estava totalmente clara para mim.

Siddhartha Ghosh
fonte
19
Em uma frase: bind é o endereço local, connect é o endereço remoto.
SHR

Respostas:

230

Para melhorar a compreensão, vamos descobrir onde exatamente ligar e conectar entra em cena,

Após o posicionamento de duas chamadas, conforme esclarecido por Sourav,

bind () associa o soquete ao seu endereço local [é por isso que o lado do servidor se liga, para que os clientes possam usar esse endereço para se conectar ao servidor.] connect () é usado para se conectar a um endereço [servidor] remoto, é por isso que é do lado do cliente , conectar [ler como: conectar ao servidor] é usado.

Não podemos usá-los de forma intercambiável (mesmo quando temos cliente / servidor na mesma máquina) devido a funções específicas e a implementação correspondente.

Recomendarei ainda correlacionar essas chamadas de handshake TCP / IP.

insira a descrição da imagem aqui

Então, quem enviará o SYN aqui, será o connect (). Enquanto bind () é usado para definir o ponto final da comunicação.

Espero que isto ajude!!

Jain Rach
fonte
1
obrigado mano. Com o diagrama, tudo pode se desdobrar rapidamente. Você pode dizer qual é a diferença aqui, se estamos usando o udp?
apm
8
accept () <br> deve ser movido abaixo <br> bloco até que a conexão do cliente
tschodt
Eu acho que todos os nós em uma rede em uma rede p2p devem usar bind, estou correto?
kapil 23/02
46

O liner: bind() para o próprio endereço, connect()para o endereço remoto.

Citando na página de manual de bind()

bind () atribui o endereço especificado por addr ao soquete referido pelo descritor de arquivo sockfd. addrlen especifica o tamanho, em bytes, da estrutura de endereço apontada por addr. Tradicionalmente, essa operação é chamada "atribuindo um nome a um soquete".

e, do mesmo para connect()

A chamada do sistema connect () conecta o soquete referido pelo descritor de arquivo sockfd ao endereço especificado por addr.

Esclarecer,

  • bind()associa o soquete ao seu endereço local [é por isso que o lado do servidor binds, para que os clientes possam usar esse endereço para se conectar ao servidor.]
  • connect() é usado para conectar-se a um endereço [servidor] remoto, é por isso que é usado o lado do cliente, a conexão [leia como: conectar ao servidor].
Sourav Ghosh
fonte
Então, digamos, se o processo do servidor e do cliente for executado na mesma máquina, eles poderão ser usados ​​de forma intercambiável?
Siddhartha Ghosh
1
@SiddharthaGhosh Não. Talvez o cliente e o servidor estejam na mesma máquina, mas ainda são processos diferentes, certo? A API atende a seu próprio objetivo. Eles nunca sãointerchangeable
Sourav Ghosh
O que exatamente se entende por local e remoto neste contexto?
Siddhartha Ghosh
@SiddharthaGhosh local-> o próprio processo, remote-> o outro processo.
Sourav Ghosh
@SouravGhosh, então isso significa que não posso especificar uma porta à qual ligar no lado do cliente?
Hengqi Chen
12

bind informa ao processo em execução para reivindicar uma porta. ou seja, ele deve se ligar à porta 80 e ouvir solicitações de entrada. com o bind, seu processo se torna um servidor. quando você usa o connect, você diz ao seu processo para conectar-se a uma porta que JÁ ESTÁ em uso. seu processo se torna um cliente. a diferença é importante: bind quer uma porta que não esteja em uso (para que possa reivindicá-la e se torne um servidor) e connect quer uma porta que já esteja em uso (para que possa se conectar a ela e conversar com o servidor)

Philipp Murry
fonte
9

Da Wikipedia: http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

conectar():

A chamada do sistema connect () conecta um soquete, identificado por seu descritor de arquivo, a um host remoto especificado pelo endereço desse host na lista de argumentos.

Certos tipos de soquetes são sem conexão, mais comumente soquetes de protocolo de datagrama do usuário. Para esses soquetes, a conexão assume um significado especial: o destino padrão para enviar e receber dados é definido no endereço fornecido, permitindo o uso de funções como send () e recv () em soquetes sem conexão.

connect () retorna um número inteiro que representa o código de erro: 0 representa sucesso, enquanto -1 representa um erro.

ligar():

bind () atribui um soquete a um endereço. Quando um soquete é criado usando socket (), ele recebe apenas uma família de protocolos, mas não recebe um endereço. Essa associação com um endereço deve ser realizada com a chamada do sistema bind () antes que o soquete possa aceitar conexões com outros hosts. bind () usa três argumentos:

sockfd, um descritor que representa o soquete para executar a ligação. my_addr, um ponteiro para uma estrutura sockaddr representando o endereço ao qual vincular. addrlen, um campo socklen_t que especifica o tamanho da estrutura sockaddr. Bind () retorna 0 em caso de sucesso e -1 se ocorrer um erro.

Exemplos: 1.) Usando o Connect

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(){
  int clientSocket;
  char buffer[1024];
  struct sockaddr_in serverAddr;
  socklen_t addr_size;

  /*---- Create the socket. The three arguments are: ----*/
  /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
  clientSocket = socket(PF_INET, SOCK_STREAM, 0);

  /*---- Configure settings of the server address struct ----*/
  /* Address family = Internet */
  serverAddr.sin_family = AF_INET;
  /* Set port number, using htons function to use proper byte order */
  serverAddr.sin_port = htons(7891);
  /* Set the IP address to desired host to connect to */
  serverAddr.sin_addr.s_addr = inet_addr("192.168.1.17");
  /* Set all bits of the padding field to 0 */
  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

  /*---- Connect the socket to the server using the address struct ----*/
  addr_size = sizeof serverAddr;
  connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);

  /*---- Read the message from the server into the buffer ----*/
  recv(clientSocket, buffer, 1024, 0);

  /*---- Print the received message ----*/
  printf("Data received: %s",buffer);   

  return 0;
}

2.) Exemplo de ligação:

int main()
{
    struct sockaddr_in source, destination = {};  //two sockets declared as previously
    int sock = 0;
    int datalen = 0;
    int pkt = 0;

    uint8_t *send_buffer, *recv_buffer;

    struct sockaddr_storage fromAddr;   // same as the previous entity struct sockaddr_storage serverStorage;
    unsigned int addrlen;  //in the previous example socklen_t addr_size;
    struct timeval tv;
    tv.tv_sec = 3;  /* 3 Seconds Time-out */
    tv.tv_usec = 0;

    /* creating the socket */         
    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
        printf("Failed to create socket\n");

    /*set the socket options*/
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));

    /*Inititalize source to zero*/
    memset(&source, 0, sizeof(source));       //source is an instance of sockaddr_in. Initialization to zero
    /*Inititalize destinaton to zero*/
    memset(&destination, 0, sizeof(destination));


    /*---- Configure settings of the source address struct, WHERE THE PACKET IS COMING FROM ----*/
    /* Address family = Internet */
    source.sin_family = AF_INET;    
    /* Set IP address to localhost */   
    source.sin_addr.s_addr = INADDR_ANY;  //INADDR_ANY = 0.0.0.0
    /* Set port number, using htons function to use proper byte order */
    source.sin_port = htons(7005); 
    /* Set all bits of the padding field to 0 */
    memset(source.sin_zero, '\0', sizeof source.sin_zero); //optional


    /*bind socket to the source WHERE THE PACKET IS COMING FROM*/
    if (bind(sock, (struct sockaddr *) &source, sizeof(source)) < 0) 
        printf("Failed to bind socket");

    /* setting the destination, i.e our OWN IP ADDRESS AND PORT */
    destination.sin_family = AF_INET;                 
    destination.sin_addr.s_addr = inet_addr("127.0.0.1");  
    destination.sin_port = htons(7005); 

    //Creating a Buffer;
    send_buffer=(uint8_t *) malloc(350);
    recv_buffer=(uint8_t *) malloc(250);

    addrlen=sizeof(fromAddr);

    memset((void *) recv_buffer, 0, 250);
    memset((void *) send_buffer, 0, 350);

    sendto(sock, send_buffer, 20, 0,(struct sockaddr *) &destination, sizeof(destination));

    pkt=recvfrom(sock, recv_buffer, 98,0,(struct sockaddr *)&destination, &addrlen);
    if(pkt > 0)
        printf("%u bytes received\n", pkt);
    }

Espero que esclareça a diferença

Observe que o tipo de soquete que você declara dependerá do que você precisa, isso é extremamente importante

Khan
fonte
9

Eu acho que ajudaria sua compreensão se você pensar em connect()e listen()como contrapartes, em vez de connect()e bind(). A razão para isso é que você pode ligar ou omitir bind()antes, embora raramente seja uma boa ideia chamá-lo antes connect()ou não listen().

Se isso ajuda a pensar em termos de servidores e clientes, é listen()essa a marca registrada do primeiro e connect()do segundo. bind()pode ser encontrado - ou não encontrado - em ambos.

Se assumirmos que nosso servidor e cliente estão em máquinas diferentes, fica mais fácil entender as várias funções.

bind()atua localmente, ou seja, vincula o final da conexão na máquina em que é chamado, ao endereço solicitado e atribui a porta solicitada a você. Isso é feito independentemente da máquina ser um cliente ou servidor. connect()inicia uma conexão com um servidor, ou seja, se conecta ao endereço e porta solicitados no servidor, a partir de um cliente. Esse servidor quase certamente já ligou bind()antes listen(), para que você possa saber em qual endereço e porta se conectar a ele usando connect().

Se você não ligar bind(), uma porta e um endereço serão implicitamente atribuídos e vinculados à máquina local quando você ligar connect()(cliente) oulisten() (servidor). No entanto, esse é um efeito colateral de ambos, não da finalidade deles. Uma porta designada dessa maneira é efêmera.

Um ponto importante aqui é que o cliente não precisa ser vinculado, porque os clientes se conectam aos servidores e, portanto, o servidor saberá o endereço e a porta do cliente, mesmo que você esteja usando uma porta efêmera, em vez de se vincular a algo específico. Por outro lado, embora o servidor possa ligar listen()sem chamar bind(), nesse cenário eles precisariam descobrir a porta efêmera atribuída e comunicar isso a qualquer cliente que deseje se conectar a ela.

Suponho que, como você mencionou, connect()esteja interessado em TCP, mas isso também é transferido para o UDP, onde não chamar bind()antes do primeiro sendto()(o UDP é sem conexão) também faz com que uma porta e um endereço sejam atribuídos e vinculados implicitamente. Uma função que você não pode chamar sem ligação é a recvfrom()que retornará um erro, porque sem uma porta atribuída e um endereço vinculado, não há nada a receber (ou muito, dependendo de como você interpreta a ausência de uma ligação).

pjcard
fonte
1

Demasiado longo; Não leia: a diferença é se a fonte (local) ou o endereço / porta de destino está sendo definido. Em resumo, bind()defina a fonte e connect()o destino. Independentemente do TCP ou UDP.

bind()

bind()defina o endereço local (fonte) do soquete. Este é o endereço onde os pacotes são recebidos. Os pacotes enviados pelo soquete carregam isso como o endereço de origem, para que o outro host saiba para onde enviar seus pacotes.

Se a recepção não for necessária, o endereço de origem do soquete é inútil. Protocolos como o TCP exigem o recebimento ativado para enviar corretamente, pois o host de destino envia uma confirmação quando um ou mais pacotes chegarem (ou seja, reconhecimento).

connect()

  • O TCP tem um estado "conectado". connect()aciona o código TCP para tentar estabelecer uma conexão com o outro lado.
  • O UDP não possui um estado "conectado". connect()defina apenas um endereço padrão para onde os pacotes serão enviados quando nenhum endereço for especificado. Quando connect()não é usado sendto()ou sendmsg()deve ser usado contendo o endereço de destino.

Quando connect()uma função de envio ou é chamada e nenhum endereço é vinculado, o Linux vincula automaticamente o soquete a uma porta aleatória. Para detalhes técnicos, consulte o inet_autobind()código-fonte do kernel do Linux.

Notas laterais

  • listen() é apenas TCP.
  • Na família AF_INET , o endereço de origem ou destino do soquete ( struct sockaddr_in) é composto por um endereço IP (consulte o cabeçalho IP ) e uma porta TCP ou UDP (consulte o cabeçalho TCP e UDP ).
Ricardo Biehl Pasquali
fonte