gniazdo connect () vs bind ()

121

Zarówno wywołania systemowe, jak connect()i bind()wywołania systemowe „kojarzą” deskryptor pliku gniazda z adresem (zwykle jest to kombinacja ip / port). Ich prototypy to: -

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

i

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

Jaka jest dokładna różnica między 2 połączeniami? Kiedy stosować connect()i kiedy bind()?

W szczególności w niektórych przykładowych kodach klienta serwera stwierdzono, że klient używa, connect()a serwer używa bind()wywołania. Rozum nie był dla mnie do końca jasny.

Siddhartha Ghosh
źródło
19
Jednym zdaniem: bind to adres lokalny, connect to adres zdalny.
SHR

Odpowiedzi:

230

Aby lepiej zrozumieć, dowiedzmy się, gdzie dokładnie pojawia się łączenie i łączenie,

Oprócz rozmieszczenia dwóch połączeń, jak wyjaśnił Sourav,

bind () kojarzy gniazdo z jego adresem lokalnym [dlatego wiąże się po stronie serwera, aby klienci mogli używać tego adresu do łączenia się z serwerem.] connect () służy do łączenia się ze zdalnym adresem [serwera], dlatego jest po stronie klienta , connect [czytaj jako: połącz z serwerem] jest używane.

Nie możemy ich używać zamiennie (nawet jeśli mamy klienta / serwer na tej samej maszynie) ze względu na określone role i odpowiednią implementację.

Będę dalej zalecał skorelowanie tych połączeń uzgadniania TCP / IP.

wprowadź opis obrazu tutaj

Więc kto wyśle ​​tutaj SYN, będzie to connect (). Podczas gdy bind () służy do definiowania punktu końcowego komunikacji.

Mam nadzieję że to pomoże!!

Jain Rach
źródło
1
dzięki stary. Dzięki diagramowi wszystko szybko się ustabilizuje. Czy możesz powiedzieć, jaka jest różnica tutaj, jeśli używamy UDP?
apm
8
accept () <br> powinna zostać przeniesiona poniżej bloku do czasu połączenia z klientem
tschodt
Myślę, że wszystkie węzły w sieci w sieci p2p powinny używać bind, czy mam rację?
kapil
46

Jedna linia: bind() na własny adres, connect()na zdalny adres.

Cytując ze strony podręcznika man bind()

bind () przypisuje adres określony przez addr do gniazda, do którego odwołuje się deskryptor pliku sockfd. addrlen określa rozmiar w bajtach struktury adresu wskazywanej przez addr. Tradycyjnie operacja ta nosi nazwę „przypisywania nazwy do gniazda”.

iz tego samego dla connect()

Wywołanie systemowe connect () łączy gniazdo, do którego odwołuje się deskryptor pliku sockfd, z adresem określonym przez addr.

W celu wyjaśnienia,

  • bind()kojarzy gniazdo z jego adresem lokalnym [dlatego po stronie serwera binds, aby klienci mogli używać tego adresu do łączenia się z serwerem].
  • connect() służy do łączenia się ze zdalnym adresem [serwera], dlatego jest po stronie klienta, używane jest połączenie [czytaj jako: połącz z serwerem].
Sourav Ghosh
źródło
Więc powiedzmy, jeśli zarówno proces serwera, jak i klienta działają na tej samej maszynie, czy mogą być używane zamiennie?
Siddhartha Ghosh
1
@SiddharthaGhosh Nie. Może klient i serwer znajdują się na tej samej maszynie, ale nadal są to różne procesy, prawda? Obydwa API obsługuje swoje własne zadanie. Nigdy nimi nie sąinterchangeable
Sourav Ghosh
Co dokładnie oznacza w tym kontekście określenie lokalny i zdalny?
Siddhartha Ghosh
@SiddharthaGhosh local-> sam proces, remote-> inny proces.
Sourav Ghosh
@SouravGhosh, więc oznacza to, że nie mogę określić portu do powiązania po stronie klienta?
Hengqi Chen
12

bind mówi działającemu procesowi, aby zajął port. tj. powinien połączyć się z portem 80 i nasłuchiwać przychodzących żądań. z bind, twój proces staje się serwerem. kiedy używasz connect, mówisz swojemu procesowi, aby połączył się z portem, który jest JUŻ używany. Twój proces staje się klientem. różnica jest ważna: bind chce portu, który nie jest używany (aby mógł go przejąć i stać się serwerem), a connect chce portu, który jest już używany (aby mógł się z nim połączyć i rozmawiać z serwerem)

Philipp Murry
źródło
9

Z Wikipedii http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

połączyć():

Wywołanie systemowe connect () łączy gniazdo, zidentyfikowane przez jego deskryptor pliku, ze zdalnym hostem określonym przez adres tego hosta na liście argumentów.

Niektóre typy gniazd są bezpołączeniowe, najczęściej są to gniazda protokołu datagramów użytkownika. Dla tych gniazd connect nabiera specjalnego znaczenia: domyślny cel wysyłania i odbierania danych zostaje ustawiony na podany adres, co pozwala na użycie funkcji takich jak send () i recv () na gniazdach bezpołączeniowych.

connect () zwraca liczbę całkowitą reprezentującą kod błędu: 0 oznacza sukces, a -1 oznacza błąd.

wiązać():

bind () przypisuje gniazdo do adresu. Kiedy gniazdo jest tworzone za pomocą funkcji socket (), otrzymuje tylko rodzinę protokołów, ale nie otrzymuje adresu. To powiązanie z adresem musi zostać wykonane za pomocą wywołania systemowego bind (), zanim gniazdo będzie mogło zaakceptować połączenia z innymi hostami. bind () przyjmuje trzy argumenty:

sockfd, deskryptor reprezentujący gniazdo do wykonania wiązania. my_addr, wskaźnik do struktury sockaddr reprezentującej adres do powiązania. addrlen, pole socklen_t określające rozmiar struktury sockaddr. Bind () zwraca 0 w przypadku sukcesu i -1, jeśli wystąpi błąd.

Przykłady: 1.) Korzystanie z 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.) Przykład wiązania:

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

Mam nadzieję, że to wyjaśnia różnicę

Należy pamiętać, że deklarowany typ gniazda będzie zależał od tego, czego potrzebujesz, jest to niezwykle ważne

Chan
źródło
9

Myślę, że pomogłoby ci to w zrozumieniu, gdybyś myślał o connect()i listen()jako o odpowiednikach, a nie o connect()i bind(). Powodem tego jest to, że możesz wywołać lub pominąć bind()wcześniej, chociaż rzadko jest to dobry pomysł, aby wywołać to wcześniej connect()lub nie dzwonić wcześniej listen().

Jeśli pomaga myśleć kategoriami serwerów i klientów, to jest listen()to znak rozpoznawczy tego pierwszego i connect()drugiego. bind()można znaleźć - lub nie znaleźć - na obu.

Jeśli założymy, że nasz serwer i klient znajdują się na różnych komputerach, łatwiej będzie zrozumieć różne funkcje.

bind()działa lokalnie, co oznacza, że ​​wiąże koniec połączenia na maszynie, na której jest wywoływany, z żądanym adresem i przypisuje żądany port do użytkownika. Robi to niezależnie od tego, czy ta maszyna będzie klientem, czy serwerem. connect()inicjuje połączenie z serwerem, co oznacza, że ​​łączy się z żądanym adresem i portem na serwerze od klienta. Ten serwer prawie na pewno zadzwonił bind()wcześniej listen(), abyś mógł wiedzieć, na którym adresie i porcie się z nim połączyć za pomocą connect().

Jeśli nie zadzwonisz bind(), port i adres zostaną niejawnie przypisane i powiązane na komputerze lokalnym podczas wywoływania connect()(klienta) lub listen()(serwera). Jest to jednak efekt uboczny obu, a nie ich cel. Tak wyznaczony port jest efemeryczny.

Ważną kwestią jest tutaj to, że klient nie musi być związany, ponieważ klienci łączą się z serwerami, więc serwer będzie znał adres i port klienta, nawet jeśli używasz portu efemerycznego, zamiast wiązania się z czymś konkretnym. Z drugiej strony, chociaż serwer mógłby dzwonić listen()bez wywoływania bind(), w tym scenariuszu musiałby znaleźć przypisany mu port efemeryczny i przekazać go każdemu klientowi, z którym chce się połączyć.

Zakładam, jak wspomniałeś, connect()że interesuje Cię TCP, ale przenosi się to również na UDP, gdzie nie wywołanie bind()przed pierwszym sendto()(UDP jest bez połączenia) powoduje również niejawne przypisanie i powiązanie portu i adresu. Jedną z funkcji, której nie można wywołać bez powiązania, jest ta recvfrom(), która zwróci błąd, ponieważ bez przypisanego portu i związanego adresu nie ma z czego odbierać (lub za dużo, w zależności od tego, jak interpretujesz brak wiązania).

pjcard
źródło
1

Za długo; Don't Read: Różnica polega na tym, czy ustawiany jest adres źródłowy (lokalny) czy docelowy / port. Krótko mówiąc, bind()ustaw źródło i connect()ustaw miejsce docelowe. Niezależnie od protokołu TCP lub UDP.

bind()

bind()ustaw lokalny (źródłowy) adres gniazda. To jest adres, pod którym odbierane są pakiety. Pakiety wysyłane przez gniazdo zawierają ten adres jako adres źródłowy, więc drugi host będzie wiedział, gdzie odesłać swoje pakiety.

Jeśli odbiór nie jest potrzebny, adres źródłowy gniazda jest bezużyteczny. Protokoły takie jak TCP wymagają włączonego odbierania w celu prawidłowego wysyłania, ponieważ host docelowy odsyła potwierdzenie, gdy przybył jeden lub więcej pakietów (tj. Potwierdzenie).

connect()

  • TCP ma stan „połączony”. connect()uruchamia kod TCP, aby spróbować nawiązać połączenie z drugą stroną.
  • UDP nie ma stanu „połączony”. connect()ustawia tylko domyślny adres, na który są wysyłane pakiety, jeśli nie określono żadnego adresu. Kiedy connect()nie jest używany sendto()lub sendmsg()musi być używany, zawierający adres docelowy.

Kiedy connect()wywoływana jest funkcja wysyłania lub, a żaden adres nie jest powiązany, Linux automatycznie wiąże gniazdo z losowym portem. Aby uzyskać szczegółowe informacje techniczne, inet_autobind()zapoznaj się z kodem źródłowym jądra Linux.

Dodatkowe uwagi

  • listen() to tylko TCP.
  • W rodzinie AF_INET adres źródłowy lub docelowy gniazda ( struct sockaddr_in) składa się z adresu IP (patrz nagłówek IP ) oraz portu TCP lub UDP (patrz nagłówek TCP i UDP ).
Ricardo Biehl Pasquali
źródło