socket connect () vs bind ()

121

Les deux connect()et les bind()appels système «associent» le descripteur de fichier de socket à une adresse (généralement une combinaison ip / port). Leurs prototypes sont comme: -

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

et

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

Quelle est la différence exacte entre 2 appels? Quand faut-il utiliser connect()et quand bind()?

Plus précisément, dans certains exemples de codes de client de serveur, trouvé que le client utilise connect()et que le serveur utilise l' bind()appel. La raison n'était pas tout à fait claire pour moi.

Siddhartha Ghosh
la source
19
En une phrase: la liaison est à l'adresse locale, la connexion est à l'adresse distante.
SHR

Réponses:

230

Pour mieux comprendre, découvrons où exactement lier et connecter entre en scène,

Suite au positionnement de deux appels, comme précisé par Sourav,

bind () associe le socket à son adresse locale [c'est pourquoi le côté serveur se lie, afin que les clients puissent utiliser cette adresse pour se connecter au serveur.] connect () est utilisé pour se connecter à une adresse [serveur] distante, c'est pourquoi c'est côté client , connect [lire comme: se connecter au serveur] est utilisé.

Nous ne pouvons pas les utiliser de manière interchangeable (même lorsque nous avons client / serveur sur la même machine) en raison de rôles spécifiques et de l'implémentation correspondante.

Je recommanderai en outre de corréler ces appels TCP / IP handshake.

entrez la description de l'image ici

Alors, qui enverra SYN ici, ce sera connect (). Alors que bind () est utilisé pour définir le point final de communication.

J'espère que cela t'aides!!

Jain Rach
la source
1
Merci mec. Avec le diagramme, tout peut résister rapidement. Pouvez-vous dire quelle est la différence ici, si nous utilisons udp?
apm le
8
accept () <br> devrait être déplacé sous le bloc <br> jusqu'à la connexion du client
tschodt
Je pense que tous les nœuds d'un réseau dans un réseau p2p devraient utiliser bind, ai-je raison?
kapil le
46

The one liner: bind() à sa propre adresse,connect() à une adresse distante.

Citant la page de manuel de bind()

bind () affecte l'adresse spécifiée par addr au socket référencé par le descripteur de fichier sockfd. addrlen spécifie la taille, en octets, de la structure d'adresse pointée par addr. Traditionnellement, cette opération est appelée "attribuer un nom à une socket".

et, de même pour connect()

L'appel système connect () connecte la socket référencée par le descripteur de fichier sockfd à l'adresse spécifiée par addr.

Clarifier,

  • bind()associe le socket à son adresse locale [c'est pourquoi côté serveur bind, afin que les clients puissent utiliser cette adresse pour se connecter au serveur.]
  • connect() est utilisé pour se connecter à une adresse [serveur] distante, c'est pourquoi, côté client, se connecter [lire comme: se connecter au serveur] est utilisé.
Sourav Ghosh
la source
Donc, disons, si les processus serveur et client s'exécutent sur la même machine, peuvent-ils être utilisés de manière interchangeable?
Siddhartha Ghosh
1
@SiddharthaGhosh Non. Peut-être que le client et le serveur sont sur la même machine, mais ce sont toujours des processus différents, non? L'API sert toutes deux ses propres fins. Ils ne sont jamaisinterchangeable
Sourav Ghosh
Qu'entend-on exactement par local et distant dans ce contexte?
Siddhartha Ghosh
@SiddharthaGhosh local-> le processus lui-même, remote-> l'autre processus.
Sourav Ghosh
@SouravGhosh donc cela signifie que je ne peux pas spécifier un port auquel se connecter côté client?
Hengqi Chen
12

bind dit au processus en cours de réclamer un port. c'est-à-dire qu'il doit se lier au port 80 et écouter les demandes entrantes. avec bind, votre processus devient un serveur. lorsque vous utilisez connect, vous dites à votre processus de se connecter à un port qui est DÉJÀ utilisé. votre processus devient un client. la différence est importante: bind veut un port qui n'est pas utilisé (pour qu'il puisse le revendiquer et devenir un serveur), et connect veut un port qui est déjà utilisé (pour qu'il puisse s'y connecter et parler au serveur)

Philipp Murry
la source
9

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

relier():

L'appel système connect () connecte un socket, identifié par son descripteur de fichier, à un hôte distant spécifié par l'adresse de cet hôte dans la liste d'arguments.

Certains types de sockets sont sans connexion, le plus souvent des sockets de protocole de datagramme utilisateur. Pour ces sockets, connect prend une signification particulière: la cible par défaut pour l'envoi et la réception de données est définie à l'adresse donnée, permettant l'utilisation de fonctions telles que send () et recv () sur les sockets sans connexion.

connect () renvoie un entier représentant le code d'erreur: 0 représente le succès, tandis que -1 représente une erreur.

lier():

bind () assigne une socket à une adresse. Lorsqu'un socket est créé à l'aide de socket (), il ne reçoit qu'une famille de protocoles, mais pas d'adresse. Cette association avec une adresse doit être effectuée avec l'appel système bind () avant que le socket puisse accepter des connexions à d'autres hôtes. bind () prend trois arguments:

sockfd, un descripteur représentant le socket sur lequel effectuer la liaison. my_addr, un pointeur vers une structure sockaddr représentant l'adresse à laquelle se lier. addrlen, un champ socklen_t spécifiant la taille de la structure sockaddr. Bind () renvoie 0 en cas de succès et -1 si une erreur se produit.

Exemples: 1.) Utilisation 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.) Exemple de liaison:

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);
    }

J'espère que cela clarifie la différence

Veuillez noter que le type de socket que vous déclarez dépendra de ce dont vous avez besoin, c'est extrêmement important

Khan
la source
9

Je pense que cela vous aiderait à comprendre si vous pensez à connect()et en listen()tant que contreparties, plutôt qu'à connect()et bind(). La raison en est que vous pouvez appeler ou omettre bind()avant l'un ou l' autre, bien que ce soit rarement une bonne idée de l'appeler avant connect(), ou de ne pas l'appeler avantlisten() .

Si cela aide à penser en termes de serveurs et de clients, c'est listen()ce qui caractérise les premiers et connect()les seconds.bind()peut être trouvé - ou introuvable - sur l'un ou l'autre.

Si nous supposons que notre serveur et notre client sont sur des machines différentes, il devient plus facile de comprendre les différentes fonctions.

bind()agit localement, c'est-à-dire qu'il lie la fin de la connexion sur la machine sur laquelle il est appelé, à l'adresse demandée et vous attribue le port demandé. Il le fait indépendamment du fait que cette machine soit un client ou un serveur. connect()initie une connexion à un serveur, c'est-à-dire qu'il se connecte à l'adresse et au port demandés sur le serveur, à partir d'un client. Ce serveur aura presque certainement appelé bind()avant listen(), afin que vous puissiez savoir sur quelle adresse et quel port vous connecter en utilisantconnect() .

Si vous n'appelez pas bind(), un port et une adresse seront implicitement attribués et liés sur la machine locale pour vous lorsque vous appelez connect()(client) ou listen()(serveur). Cependant, c'est un effet secondaire des deux, pas leur objectif. Un port ainsi attribué est éphémère.

Un point important ici est que le client n'a pas besoin d'être lié, car les clients se connectent aux serveurs, et ainsi le serveur connaîtra l'adresse et le port du client même si vous utilisez un port éphémère, plutôt que de se lier à quelque chose de spécifique. D'un autre côté, bien que le serveur puisse appeler listen()sans appeler bind(), dans ce scénario, il devra découvrir le port éphémère qui lui a été attribué et le communiquer à tout client qu'il souhaite s'y connecter.

Je suppose que, comme vous le mentionnez, connect()vous êtes intéressé par TCP, mais cela se répercute également sur UDP, où le fait de ne pas appeler bind()avant le premier sendto()(UDP est sans connexion) entraîne également l'attribution et la liaison implicites d'un port et d'une adresse. Une fonction que vous ne pouvez pas appeler sans liaison est recvfrom(), qui retournera une erreur, car sans port attribué et adresse liée, il n'y a rien à recevoir (ou trop, selon la façon dont vous interprétez l'absence de liaison).

pjcard
la source
1

Trop long; Ne pas lire: la différence est de savoir si l'adresse / le port source (local) ou de destination est défini. En bref, bind()définissez la source et connect()définissez la destination. Indépendamment de TCP ou UDP.

bind()

bind()définir l'adresse locale (source) du socket. C'est l'adresse à laquelle les paquets sont reçus. Les paquets envoyés par le socket portent cela comme adresse source, de sorte que l'autre hôte saura où renvoyer ses paquets.

Si la réception n'est pas nécessaire, l'adresse source du socket est inutile. Les protocoles comme TCP nécessitent la réception activée pour pouvoir envoyer correctement, car l'hôte de destination renvoie une confirmation lorsqu'un ou plusieurs paquets sont arrivés (c'est-à-dire un accusé de réception).

connect()

  • TCP a un état "connecté". connect()déclenche le code TCP pour essayer d'établir une connexion de l'autre côté.
  • UDP n'a pas d'état «connecté». connect()ne définissez qu'une adresse par défaut à laquelle les paquets sont envoyés lorsqu'aucune adresse n'est spécifiée. Quand connect()n'est pas utilisé sendto()ou sendmsg()doit être utilisé avec l'adresse de destination.

Lorsqu'une connect()fonction d'envoi est appelée et qu'aucune adresse n'est liée, Linux lie automatiquement le socket à un port aléatoire. Pour plus de détails techniques, consultez le inet_autobind()code source du noyau Linux.

Notes d'accompagnement

  • listen() est TCP uniquement.
  • Dans la famille AF_INET , l'adresse source ou de destination de la socket ( struct sockaddr_in) est composée d'une adresse IP (voir en- tête IP ) et d'un port TCP ou UDP (voir en - tête TCP et UDP ).
Ricardo Biehl Pasquali
la source