socket servidor library ejemplos cliente c sockets network-programming

servidor - socket connect() vs bind()



socket en c++ cliente servidor (6)

Tanto el sistema connect() como el bind() llaman ''asocian'' el descriptor del archivo de socket a una dirección (típicamente una combinación de ip / puerto). 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 llamada bind() . La razón no estaba completamente clara para mí.


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, aunque rara vez es una buena idea llamarlo antes de connect() , o no llamarlo antes de listen() .

Si ayuda pensar en términos de servidores y clientes, es listen() que es el sello distintivo del primero, y connect() el último. bind() se puede encontrar, o no se encuentra, en cualquiera de los dos.

Si asumimos que nuestro servidor y cliente están en máquinas diferentes, se hace más fácil entender las diversas 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 y puerto solicitados en el servidor, desde un cliente. Es casi seguro que ese servidor habrá llamado a bind() antes de listen() , para que pueda saber en qué dirección y puerto conectarse con el uso de connect() .

Si no llama a 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() (client) o listen() (server). 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 sabrá la dirección y el puerto del cliente a pesar de que esté utilizando un puerto efímero, en lugar de vincularse a algo específico. Por otro lado, aunque el servidor podría llamar a listen() sin llamar a bind() , en ese escenario tendrían que descubrir su puerto efímero asignado y comunicarlo a cualquier cliente al que quiera conectarse.

Supongo que, al mencionar connect() , está interesado en TCP, pero esto también se transfiere a UDP, donde no llamar a bind() antes del primer sendto() (UDP no tiene conexión) también hace que un puerto y una dirección sean implícitamente asignado y vinculado. Una función que no puede llamar sin enlace es recvfrom() , que devolverá un error, porque sin un puerto asignado y una dirección enlazada, no hay nada de qué recibir (o demasiado, dependiendo de cómo interprete la ausencia de un enlace).


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

conectar():

La llamada al sistema connect () conecta un socket, 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 objetivo 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 el éxito, mientras que -1 representa un error.

enlazar():

bind () asigna un socket a una dirección. Cuando se crea un socket utilizando socket (), solo se le asigna 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 a 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 para 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.) Usar 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 eso aclare la diferencia

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


Demasiado largo; No leer: la diferencia es si se está configurando la dirección / puerto de origen (local) o de destino. En resumen, bind() establece la fuente y connect() establece 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 no se necesita recibir, 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, confirmación).

connect()

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

Cuando connect() o se llama a una función de envío, y no se vincula ninguna dirección, Linux vincula automáticamente el socket a un puerto aleatorio. Para detalles técnicos, eche un vistazo a inet_autobind() en el 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 ).

Para mejorar la comprensión, descubramos dónde exactamente se vinculan y conectan las imágenes,

Además del posicionamiento de dos llamadas, como lo 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 remota [del servidor], por eso es el 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 TCP / IP apretón de manos.

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!!


bind le dice al proceso en ejecución que reclame un puerto. es decir, debería unirse al puerto 80 y escuchar las solicitudes de ingreso. 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 un cliente. la diferencia es importante: bind quiere un puerto que no esté en uso (para que pueda reclamarlo y convertirse en un servidor), y connect quiere un puerto que ya esté en uso (para que pueda conectarse y hablar con el servidor)


El único revestimiento: bind() a la propia dirección, connect() a la dirección remota.

Citando de la página man 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 llama "asignar un nombre a un socket".

y, de lo mismo para connect()

La llamada al sistema connect () conecta el socket referido por 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 servidor bind s, para que los clientes puedan usar esa dirección para conectarse al servidor].
  • connect() se usa para conectarse a una dirección remota [del servidor], por eso es del lado del cliente, se usa connect [leer como: connect to server].