socket connect () vs bind ()

121

Ambos connect()y bind()sistema de llamadas 'asociado' el descriptor de archivo de socket a una dirección (típicamente un / combinación de puerto IP). Sus prototipos son como: -

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

y

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

¿Cuál es la diferencia exacta entre 2 llamadas? ¿Cuándo se debe usar connect()y cuándo bind()?

Específicamente, en algunos códigos de cliente de servidor de muestra, se encontró que el cliente está usando connect()y el servidor está usando la bind()llamada. La razón no me quedó del todo clara.

Siddhartha Ghosh
fuente
19
En una frase: vincular es a la dirección local, conectar es a la dirección remota.
SHR

Respuestas:

230

Para mejorar la comprensión, averigüemos dónde entra exactamente enlazar y conectar,

Además del posicionamiento de dos llamadas, como aclaró Sourav,

bind () asocia el socket con su dirección local [es por eso que el lado del servidor se une, para que los clientes puedan usar esa dirección para conectarse al servidor]. connect () se usa para conectarse a una dirección de [servidor] remota, por eso es del lado del cliente , se utiliza conectar [leer como: conectar al servidor].

No podemos usarlos indistintamente (incluso cuando tenemos cliente / servidor en la misma máquina) debido a roles específicos y la implementación correspondiente.

Además, recomendaré correlacionar estas llamadas con el protocolo de enlace TCP / IP.

ingrese la descripción de la imagen aquí

Entonces, quién enviará SYN aquí, será connect (). Mientras que bind () se usa para definir el punto final de la comunicación.

¡¡Espero que esto ayude!!

Jain Rach
fuente
1
gracias hermano. Con el diagrama todo se puede deshacer rápidamente. ¿Puedes decir cuál es la diferencia aquí, si estamos usando udp?
apm
8
accept () <br> debe moverse debajo del bloque <br> hasta que se conecte desde el cliente
tschodt
Creo que todos los nodos de una red en una red p2p deberían usar bind, ¿estoy en lo correcto?
kapil
46

La única línea: bind() dirección propia, connect()dirección remota.

Citando de la página de manual de bind()

bind () asigna la dirección especificada por addr al socket al que hace referencia el descriptor de archivo sockfd. addrlen especifica el tamaño, en bytes, de la estructura de dirección apuntada por addr. Tradicionalmente, esta operación se denomina "asignar un nombre a un conector".

y, de lo mismo para connect()

La llamada al sistema connect () conecta el conector al que hace referencia el descriptor de archivo sockfd a la dirección especificada por addr.

Para aclarar,

  • bind()asocia el socket con su dirección local [es por eso que el lado del servidor bindes, para que los clientes puedan usar esa dirección para conectarse al servidor].
  • connect() se usa para conectarse a una dirección de [servidor] remoto, por eso se usa el lado del cliente, conectar [leer como: conectarse al servidor].
Sourav Ghosh
fuente
Entonces, digamos, si tanto el proceso del servidor como el del cliente se ejecutan en la misma máquina, ¿pueden usarse indistintamente?
Siddhartha Ghosh
1
@SiddharthaGhosh No. tal vez el cliente y el servidor estén en la misma máquina, pero aún así son procesos diferentes, ¿verdad? Tanto la API sirve para su propio propósito. Nunca lo soninterchangeable
Sourav Ghosh
¿Qué se entiende exactamente por local y remoto en este contexto?
Siddhartha Ghosh
@SiddharthaGhosh local-> el proceso en sí, remote-> el otro proceso.
Sourav Ghosh
@SouravGhosh, ¿esto significa que no puedo especificar un puerto al que enlazar en el lado del cliente?
Hengqi Chen
12

bind le dice al proceso en ejecución que reclame un puerto. es decir, debe vincularse al puerto 80 y escuchar las solicitudes entrantes. con bind, su proceso se convierte en un servidor. cuando usa connect, le dice a su proceso que se conecte a un puerto que YA está en uso. su proceso se convierte en cliente. la diferencia es importante: bind quiere un puerto que no está en uso (para poder reclamarlo y convertirse en un servidor), y connect quiere un puerto que ya está en uso (para poder conectarse a él y hablar con el servidor)

Philipp Murry
fuente
9

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

conectar():

La llamada al sistema connect () conecta un conector, identificado por su descriptor de archivo, a un host remoto especificado por la dirección de ese host en la lista de argumentos.

Ciertos tipos de sockets no tienen conexión, más comúnmente sockets de protocolo de datagramas de usuario. Para estos sockets, connect adquiere un significado especial: el destino predeterminado para enviar y recibir datos se establece en la dirección dada, lo que permite el uso de funciones como send () y recv () en sockets sin conexión.

connect () devuelve un número entero que representa el código de error: 0 representa éxito, mientras que -1 representa un error.

enlazar():

bind () asigna un socket a una dirección. Cuando se crea un socket usando socket (), solo se le da una familia de protocolos, pero no se le asigna una dirección. Esta asociación con una dirección debe realizarse con la llamada al sistema bind () antes de que el socket pueda aceptar conexiones con otros hosts. bind () toma tres argumentos:

sockfd, un descriptor que representa el socket para realizar el enlace. my_addr, un puntero a una estructura sockaddr que representa la dirección a la que enlazar. addrlen, un campo socklen_t que especifica el tamaño de la estructura sockaddr. Bind () devuelve 0 en caso de éxito y -1 si se produce un error.

Ejemplos: 1.) Uso de 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.) Ejemplo de enlace:

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 aclare la diferencia

Tenga en cuenta que el tipo de enchufe que declare dependerá de lo que necesite, esto es extremadamente importante

Kan
fuente
9

Creo que ayudaría a su comprensión si piensa en connect()y listen()como contrapartes, en lugar de connect()y bind(). La razón de esto es que puede llamar u omitir bind()antes de cualquiera de ellos, aunque rara vez es una buena idea llamarlo antes connect()o no llamarlo antes listen().

Si ayuda pensar en servidores y clientes, es listen()cuál es la seña de identidad de los primeros y de connect()los segundos. bind()se puede encontrar, o no encontrar, en cualquiera.

Si asumimos que nuestro servidor y cliente están en máquinas diferentes, será más fácil comprender las distintas funciones.

bind()actúa localmente, es decir, vincula el final de la conexión en la máquina en la que se llama, a la dirección solicitada y le asigna el puerto solicitado. Lo hace independientemente de si esa máquina será un cliente o un servidor. connect()inicia una conexión a un servidor, es decir, se conecta a la dirección solicitada y al puerto en el servidor, desde un cliente. Es casi seguro que ese servidor habrá llamado bind()antes listen(), para que pueda saber en qué dirección y puerto conectarse con el uso connect().

Si no llama bind(), un puerto y una dirección se asignarán implícitamente y se vincularán en la máquina local cuando llame a connect()(cliente) o listen()(servidor). Sin embargo, ese es un efecto secundario de ambos, no su propósito. Un puerto asignado de esta manera es efímero.

Un punto importante aquí es que el cliente no necesita estar vinculado, porque los clientes se conectan a los servidores, por lo que el servidor conocerá la dirección y el puerto del cliente aunque esté usando un puerto efímero, en lugar de vincularse a algo específico. Por otro lado, aunque el servidor podría llamar listen()sin llamar bind(), en ese escenario necesitarían descubrir su puerto efímero asignado y comunicarlo a cualquier cliente que quiera conectarse a él.

Supongo que, como mencionas connect(), estás interesado en TCP, pero esto también se traslada a UDP, donde no llamar bind()antes de la primera sendto()(UDP no tiene conexión) también hace que un puerto y una dirección se asignen y enlacen implícitamente. Una función a la que no se puede llamar sin vinculación es recvfrom(), que devolverá un error, porque sin un puerto asignado y una dirección vinculada, no hay nada que recibir (o demasiado, dependiendo de cómo interprete la ausencia de una vinculación).

pjcard
fuente
1

Demasiado largo; No leer: la diferencia es si se establece la dirección / puerto de origen (local) o de destino. En resumen, bind()configure la fuente y connect()el destino. Independientemente de TCP o UDP.

bind()

bind()establece la dirección local (fuente) del socket. Esta es la dirección donde se reciben los paquetes. Los paquetes enviados por el socket llevan esto como la dirección de origen, por lo que el otro host sabrá dónde enviar sus paquetes.

Si la recepción no es necesaria, la dirección de origen del socket es inútil. Los protocolos como TCP requieren que la recepción esté habilitada para enviar correctamente, ya que el host de destino envía una confirmación cuando han llegado uno o más paquetes (es decir, acuse de recibo).

connect()

  • TCP tiene un estado "conectado". connect()activa el código TCP para intentar establecer una conexión con el otro lado.
  • UDP no tiene estado "conectado". connect()solo establezca una dirección predeterminada a donde se envían los paquetes cuando no se especifica ninguna dirección. Cuando connect()no se usa, sendto()o sendmsg()debe usarse que contiene la dirección de destino.

Cuando connect()se llama a una función de envío y no hay ninguna dirección vinculada, Linux vincula automáticamente el socket a un puerto aleatorio. Para obtener detalles técnicos, consulte el inet_autobind()código fuente del kernel de Linux.

Notas al margen

  • listen() es solo TCP.
  • En la familia AF_INET , la dirección de origen o destino del socket ( struct sockaddr_in) está compuesta por una dirección IP (ver encabezado IP ) y un puerto TCP o UDP (ver encabezado TCP y UDP ).
Ricardo Biehl Pasquali
fuente