socket connect () vs bind ()


121

Обидві connect()і bind()системні виклики «асоціювати» сокет дескриптор файлу на адресу (зазвичай це IP / порт комбінації). Їхні прототипи такі:

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

і

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

Яка різниця між двома дзвінками? Коли слід використовувати connect()і коли bind()?

Зокрема, у деяких прикладних кодах клієнтського сервера виявлено, що клієнт використовує, connect()а сервер використовує bind()виклик. Розум мені був не зовсім зрозумілий.


19
В одному реченні: прив’язати до локальної адреси, підключити - до віддаленої адреси.
SHR

Відповіді:


230

Щоб покращити взаєморозуміння, давайте дізнаємося, де саме пов’язано та з'єднується зображення,

Крім позиціонування двох дзвінків, як уточнив Сурав,

bind () пов'язує сокет зі своєю локальною адресою [саме тому сторона сервера пов'язує, щоб клієнти могли використовувати цю адресу для підключення до сервера.] , використовується підключення [читати як: підключитися до сервера].

Ми не можемо користуватися ними взаємозамінно (навіть коли у нас клієнт / сервер на одній машині) через конкретні ролі та відповідну реалізацію.

Я надалі рекомендую співвіднести ці дзвінки TCP / IP рукостискання.

введіть тут опис зображення

Отже, хто посилатиме сюди SYN, це буде connect (). Хоча bind () використовується для визначення кінцевої точки зв'язку.

Сподіваюся, це допомагає !!


1
дякую брате. З діаграмою все може швидко відкрутити. Чи можете ви сказати, яка тут різниця? Якщо ми використовуємо udp?
APM

8
accept () <br> слід перемістити внизу блоку до з'єднання з клієнтом
tschodt

Я думаю, що всі вузли в мережі в мережі p2p повинні використовувати bind, чи я правильно?
капіл

46

Один лайнер: bind() власна адреса, connect()віддалена адреса.

Цитуючи зі сторінки man у bind()

bind () призначає адресу, вказану addr, в сокет, на який посилається дескриптор файлу sockfd. addrlen вказує розмір, в байтах, структури адреси, на яку вказує addr. Традиційно цю операцію називають "присвоєнням імені сокету".

і, з того ж за connect()

Системний виклик connect () з'єднує сокет, на який посилається дескриптор файлу sockfd, до адреси, вказаної аддр.

Щоб уточнити,

  • bind()асоціює сокет з його локальною адресою [ось чому сторона сервера binds, щоб клієнти могли використовувати цю адресу для підключення до сервера.]
  • connect() використовується для підключення до віддаленої [серверної] адреси, тому на стороні клієнта використовується підключення [читати як: підключити до сервера].

Так, скажімо, якщо і сервер, і клієнтський процес працюють на одній машині, чи можна їх використовувати взаємозамінно?
Сіддхартха Гош

1
@SiddharthaGhosh Номер може бути , клієнт і сервер знаходяться на одній машині, але всі вони мають інший процес, НЕ так? Обидва API обслуговують свою власну задачу. Вони ніколиinterchangeable
Sourav Ghosh

Що саме мається на увазі під місцевим та віддаленим у цьому контексті?
Сіддхартха Гош

@SiddharthaGhosh local-> сам процес, remote-> інший процес.
Sourav Ghosh

@SouravGhosh, це означає, що я не можу вказати порт, на який потрібно зв’язуватись на стороні клієнта?
Hengqi Chen

12

bind повідомляє запущеному процесу заявки на порт. тобто він повинен прив'язуватися до порту 80 і слухати вхідні запити. із прив’язкою ваш процес стає сервером. коли ви використовуєте підключення, ви повідомляєте своєму процесу підключитися до порту, який ВИНАГО використовується. ваш процес стає клієнтом. різниця важлива: bind хоче порт, який не використовується (щоб він міг вимагати його і став сервером), а підключення хоче порт, який вже використовується (щоб він міг підключитися до нього і поговорити з сервером)


9

З Вікіпедії http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

connect ():

Системний виклик connect () з'єднує сокет, ідентифікований його дескриптором файлу, до віддаленого хоста, вказаного адресою цього хосту у списку аргументів.

Окремі типи розеток - без з'єднання, найчастіше розетки протоколу дейтаграми користувача. Для цих розеток підключення набуває особливого значення: ціль за замовчуванням для надсилання та прийому даних встановлюється за вказаною адресою, що дозволяє використовувати такі функції, як send () та recv () у роз'ємах без підключення.

connect () повертає ціле число, що представляє код помилки: 0 являє собою успіх, а -1 - помилка.

bind ():

bind () призначає сокет для адреси. Коли сокет створюється за допомогою socket (), йому надається лише сім'я протоколів, але не призначена адреса. Ця асоціація з адресою повинна бути виконана з викликом системи bind (), перш ніж сокет може приймати з'єднання з іншими хостами. bind () бере три аргументи:

sockfd, дескриптор, що представляє сокет для виконання прив'язки. my_addr, вказівник на структуру sockaddr, що представляє адресу, до якої потрібно пов'язувати. addrlen, поле socklen_t із зазначенням розміру структури sockaddr. Bind () повертає 0 на успіх і -1, якщо сталася помилка.

Приклади: 1.) Використання підключення

#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.) Приклад зв’язку:

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

Я сподіваюся, що це з'ясовує різницю

Зверніть увагу, що тип розетки, яку ви заявляєте, залежатиме від того, що вам потрібно, це надзвичайно важливо


9

Я думаю, що це допоможе вашому розумінню, якщо ви ставитесь до них connect()і listen()як до них, а не до connect()та bind(). Причиною цього є те, що ви можете зателефонувати або опустити його bind()раніше, хоча рідко заздалегідь зателефонувати connect(), або не зателефонувати раніше listen().

Якщо це допомагає мислити з точки зору серверів та клієнтів, саме це listen()є відмітною ознакою першого та connect()другого. bind()можна знайти - або не знайти - на будь-якому.

Якщо припустити, що наш сервер і клієнт знаходяться на різних машинах, зрозуміти різні функції стає простіше.

bind()діє локально, що означає, що він прив'язує кінець з'єднання на машині, на якій він викликається, до запитуваної адреси та призначає вам запитуваний порт. Це робиться незалежно від того, буде ця машина клієнтом чи сервером. connect()ініціює підключення до сервера, тобто, він підключається до запитуваної адреси та порту на сервері від клієнта. Цей сервер майже напевно зателефонував bind()до listen()того, щоб ви могли знати, за якою адресою та портом підключитися до нього за допомогою connect().

Якщо ви не зателефонували bind(), порт і адреса будуть неявно призначені та прив’язані до локальної машини для вас, коли ви телефонуєте або connect()(клієнт), або listen()(сервер). Однак це побічний ефект обох, а не їх призначення. Порт, призначений таким чином, є ефемерним.

Важливим моментом тут є те, що клієнту не потрібно прив’язуватися, оскільки клієнти підключаються до серверів, і тому сервер буде знати адресу та порт клієнта, навіть якщо ви використовуєте ефемерний порт, а не прив’язуючи до чогось конкретного. З іншого боку, хоча сервер може дзвонити listen()без виклику bind(), у цьому сценарії їм потрібно буде виявити призначений їм ефемерний порт і повідомити це будь-якому клієнту, який він хоче підключитися до нього.

Я вважаю, що ви згадуєте, що connect()вас цікавить TCP, але це також переходить до UDP, де не дзвінок bind()перед першим sendto()(UDP відсутній підключенням) також призводить до того, що порт і адреса неявно призначаються і прив'язуються. Одна функція, яку ви не можете зателефонувати без прив'язки, - recvfrom()це поверне помилку, оскільки без призначеного порту та прив’язаної адреси нічого не можна отримати (або занадто багато, залежно від того, як ви інтерпретуєте відсутність прив'язки).


1

Надто довго; Не читайте. Різниця полягає в тому, чи встановлено джерело (локальний) або адресу / порт призначення. Словом, bind()встановіть джерело і connect()встановіть призначення. Незалежно від TCP або UDP.

bind()

bind()встановити локальну (джерельну) адресу сокета. Це адреса, за якою приймаються пакети. Пакети, надіслані сокетом, несуть це як адресу джерела, тому інший хост буде знати, куди повернути свої пакети.

Якщо отримання не потрібне, адреса джерела сокета марна. Для правильної відправки такі протоколи, як TCP, потребують прийому, оскільки хост-адресат повертає підтвердження, коли один або більше пакетів надійшли (тобто підтвердження).

connect()

  • TCP має "підключений" стан. connect()запускає код TCP, щоб спробувати встановити з'єднання з іншою стороною.
  • UDP не має "підключеного" стану. connect()встановіть лише адресу за замовчуванням, куди надсилаються пакети, коли не вказана адреса. Коли connect()він не використовується sendto()або sendmsg()повинен бути використаний, що містить адресу призначення.

Коли connect()або викликається функція надсилання, і жодна адреса не зв'язана, Linux автоматично прив'язує сокет до випадкового порту. Щоб отримати технічні деталі, ознайомтеся з inet_autobind()вихідним кодом ядра Linux.

Бічні нотатки

  • listen() є лише TCP.
  • У сімействі AF_INET джерело або адреса призначення сокета ( struct sockaddr_in) складається з IP-адреси (див. IP-заголовок ) та порту TCP або UDP (див. Заголовок TCP та UDP ).
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.