socket connect () vs bind ()


121

Entrambi connect()e bind()chiamate di sistema 'associato' il file di socket descrittore ad un indirizzo (in genere una combinazione IP / porta). I loro prototipi sono come: -

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

e

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

Qual è la differenza esatta tra 2 chiamate? Quando si dovrebbe usare connect()e quando bind()?

In particolare, in alcuni codici client del server di esempio, è stato rilevato che il client utilizza connect()e il server utilizza la bind()chiamata. La ragione non mi era del tutto chiara.


19
In una frase: il collegamento è all'indirizzo locale, il collegamento è all'indirizzo remoto.
SHR

Risposte:


230

Per una migliore comprensione, scopriamo dove esattamente bind e connect entra in scena,

Oltre al posizionamento di due bandi, come chiarito da Sourav,

bind () associa il socket con il suo indirizzo locale [ecco perché si collega lato server, in modo che i client possano usare quell'indirizzo per connettersi al server.] connect () è usato per connettersi a un indirizzo [server] remoto, ecco perché è lato client , viene utilizzato il collegamento [leggi come: il collegamento al server].

Non possiamo usarli in modo intercambiabile (anche quando abbiamo client / server sulla stessa macchina) a causa di ruoli specifici e implementazione corrispondente.

Raccomanderò inoltre di correlare queste chiamate handshake TCP / IP.

inserisci qui la descrizione dell'immagine

Quindi, chi invierà SYN qui, sarà connect (). Mentre bind () viene utilizzato per definire il punto finale della comunicazione.

Spero che questo ti aiuti!!


1
grazie fratello. Con il diagramma tutto può essere risolto velocemente. Puoi dire qual è la differenza qui, se stiamo usando udp?
apm

8
accept () <br> dovrebbe essere spostato sotto il blocco <br> fino alla connessione dal client
tschodt

penso che tutti i nodi in una rete in una rete p2p dovrebbero usare bind, ho ragione?
kapil

46

L'unica linea: bind() al proprio indirizzo, connect()all'indirizzo remoto.

Citando dalla pagina man di bind()

bind () assegna l'indirizzo specificato da addr al socket a cui fa riferimento il descrittore di file sockfd. addrlen specifica la dimensione, in byte, della struttura degli indirizzi puntata da addr. Tradizionalmente, questa operazione è chiamata "assegnazione di un nome a un socket".

e, dallo stesso per connect()

La chiamata di sistema connect () collega il socket a cui fa riferimento il descrittore di file sockfd all'indirizzo specificato da addr.

Chiarire,

  • bind()associa il socket al suo indirizzo locale [ecco perché lato server binds, in modo che i client possano utilizzare quell'indirizzo per connettersi al server.]
  • connect() viene utilizzato per connettersi a un indirizzo [server] remoto, ecco perché è lato client, viene utilizzato connect [leggi come: connettersi al server].

Quindi, ad esempio, se sia il server che il processo client vengono eseguiti sulla stessa macchina, possono essere utilizzati in modo intercambiabile?
Siddhartha Ghosh

1
@SiddharthaGhosh No. Forse client e il server sono in stessa macchina, ma ancora sono processo diverso, giusto? Entrambe le API servono il proprio scopo. Non sono maiinterchangeable
Sourav Ghosh

Cosa si intende esattamente per locale e remoto in questo contesto?
Siddhartha Ghosh

@SiddharthaGhosh local-> il processo stesso, remote-> l'altro processo.
Sourav Ghosh

@SouravGhosh quindi questo significa che non posso specificare una porta a cui collegarmi sul lato client?
Hengqi Chen,

12

bind dice al processo in esecuzione di richiedere una porta. cioè, dovrebbe collegarsi alla porta 80 e ascoltare le richieste in entrata. con bind, il tuo processo diventa un server. quando si utilizza connect, si comunica al processo di connettersi a una porta GIÀ in uso. il tuo processo diventa un cliente. la differenza è importante: bind vuole una porta che non è in uso (in modo che possa richiederla e diventare un server), e connect vuole una porta che è già in uso (in modo che possa connettersi ad essa e parlare con il server)


9

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

Collegare():

La chiamata di sistema connect () collega un socket, identificato dal suo descrittore di file, a un host remoto specificato dall'indirizzo di quell'host nell'elenco degli argomenti.

Alcuni tipi di socket sono senza connessione, più comunemente socket di protocollo datagramma utente. Per questi socket, connect assume un significato speciale: il target predefinito per l'invio e la ricezione di dati viene impostato all'indirizzo dato, consentendo l'uso di funzioni come send () e recv () su socket senza connessione.

connect () restituisce un numero intero che rappresenta il codice di errore: 0 rappresenta il successo, mentre -1 rappresenta un errore.

bind ():

bind () assegna un socket a un indirizzo. Quando un socket viene creato usando socket (), gli viene data solo una famiglia di protocolli, ma non gli viene assegnato un indirizzo. Questa associazione con un indirizzo deve essere eseguita con la chiamata di sistema bind () prima che il socket possa accettare connessioni ad altri host. bind () accetta tre argomenti:

sockfd, un descrittore che rappresenta il socket su cui eseguire il bind. my_addr, un puntatore a una struttura sockaddr che rappresenta l'indirizzo a cui collegarsi. addrlen, un campo socklen_t che specifica la dimensione della struttura sockaddr. Bind () restituisce 0 in caso di successo e -1 se si verifica un errore.

Esempi: 1.) Utilizzo di 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.) Esempio di binding:

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

Spero che questo chiarisca la differenza

Tieni presente che il tipo di socket che dichiari dipenderà da ciò che richiedi, questo è estremamente importante


9

Penso che aiuterebbe la tua comprensione se pensassi a connect()e listen()come controparti, piuttosto che a connect()e bind(). Il motivo è che puoi chiamare o omettere bind()prima di entrambi, anche se raramente è una buona idea chiamarlo prima connect()o non chiamarlo prima listen().

Se aiuta a pensare in termini di server e client, è listen()qual è il segno distintivo del primo e connect()del secondo. bind()può essere trovato - o non trovato - su entrambi.

Se assumiamo che il nostro server e client siano su macchine diverse, diventa più facile comprendere le varie funzioni.

bind()agisce localmente, vale a dire lega la fine della connessione sulla macchina su cui viene chiamata, all'indirizzo richiesto e ti assegna la porta richiesta. Lo fa indipendentemente dal fatto che quella macchina sarà un client o un server. connect()avvia una connessione a un server, vale a dire che si connette all'indirizzo e alla porta richiesti sul server, da un client. Quel server quasi sicuramente avrà chiamato bind()prima listen(), in modo che tu possa sapere su quale indirizzo e porta connetterti ad esso usando connect().

Se non chiami bind(), una porta e un indirizzo verranno assegnati implicitamente e vincolati sulla macchina locale per te quando chiami connect()(client) o listen()(server). Tuttavia, questo è un effetto collaterale di entrambi, non il loro scopo. Una porta assegnata in questo modo è effimera.

Un punto importante qui è che il client non ha bisogno di essere associato, perché i client si connettono ai server, e quindi il server conoscerà l'indirizzo e la porta del client anche se stai utilizzando una porta temporanea, piuttosto che collegarsi a qualcosa di specifico. D'altra parte, sebbene il server possa chiamare listen()senza chiamare bind(), in quello scenario dovrebbero scoprire la porta temporanea assegnata e comunicarla a qualsiasi client che desidera connettersi ad essa.

Presumo che come dici connect()tu sia interessato a TCP, ma questo si trasferisce anche a UDP, dove non chiamare bind()prima del primo sendto()(UDP è senza connessione) fa sì che una porta e un indirizzo vengano assegnati e associati implicitamente. Una funzione che non è possibile chiamare senza associazione è recvfrom(), che restituirà un errore, perché senza una porta assegnata e un indirizzo associato, non c'è nulla da ricevere (o troppo, a seconda di come si interpreta l'assenza di un'associazione).


1

Troppo lungo; Non leggere: la differenza è se viene impostata la porta / indirizzo di origine (locale) o di destinazione. In breve, bind()imposta la sorgente e connect()imposta la destinazione. Indipendentemente da TCP o UDP.

bind()

bind()imposta l'indirizzo locale (sorgente) del socket. Questo è l'indirizzo in cui vengono ricevuti i pacchetti. I pacchetti inviati dal socket portano questo come indirizzo di origine, quindi l'altro host saprà dove rimandare i suoi pacchetti.

Se la ricezione non è necessaria, l'indirizzo sorgente del socket è inutile. I protocolli come TCP richiedono la ricezione abilitata per poter inviare correttamente, poiché l'host di destinazione invia una conferma quando uno o più pacchetti sono arrivati ​​(cioè riconoscimento).

connect()

  • TCP ha uno stato "connesso". connect()attiva il codice TCP per tentare di stabilire una connessione con l'altro lato.
  • UDP non ha uno stato "connesso". connect()impostare solo un indirizzo predefinito a cui vengono inviati i pacchetti quando non viene specificato alcun indirizzo. Quando connect()non viene utilizzato sendto()o sendmsg()deve essere utilizzato contenente l'indirizzo di destinazione.

Quando connect()viene chiamata o una funzione di invio e nessun indirizzo è associato, Linux collega automaticamente il socket a una porta casuale. Per i dettagli tecnici, dai un'occhiata al inet_autobind()codice sorgente del kernel Linux.

Note a margine

  • listen() è solo TCP.
  • Nella famiglia AF_INET , l'indirizzo di origine o di destinazione del socket ( struct sockaddr_in) è composto da un indirizzo IP (vedere intestazione IP ) e una porta TCP o UDP (vedere intestazione TCP e UDP ).
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.