сокет connect () против 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);

В чем точная разница между 2 звонками? Когда следует использовать connect()и когда bind()?

В частности, в некоторых примерах клиентских кодов сервера обнаружено, что клиент использует, connect()а сервер использует bind()вызов. Причина была мне не совсем ясна.

Сиддхартха Гош
источник
19
Одним предложением: привязка к локальному адресу, подключение к удаленному адресу.
SHR

Ответы:

231

Чтобы лучше понять, давайте выясним, где именно происходит связывание и соединение,

В дополнение к позиционированию двух звонков, как пояснил Сурав,

bind () связывает сокет с его локальным адресом [вот почему выполняется привязка на стороне сервера, чтобы клиенты могли использовать этот адрес для подключения к серверу.] connect () используется для подключения к удаленному адресу [сервера], поэтому на стороне клиента , используется подключение [читается как: подключение к серверу].

Мы не можем использовать их взаимозаменяемо (даже если у нас есть клиент / сервер на одном компьютере) из-за определенных ролей и соответствующей реализации.

Далее я порекомендую соотнести эти вызовы с рукопожатием TCP / IP.

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

Итак, кто отправит сюда SYN, это будет connect (). В то время как bind () используется для определения конечной точки связи.

Надеюсь это поможет!!

Джейн Рах
источник
1
Спасибо брат. С диаграммой все может быстро разобраться. Можете ли вы сказать, в чем разница, если мы используем udp?
apm
8
accept () <br> должен быть перемещен ниже <br> блока до тех пор, пока не будет установлено соединение от клиента
tschodt
Я думаю, что все узлы в сети в сети p2p должны использовать привязку, я прав?
капил
46

Один лайнер: bind() на собственный адрес, connect()на удаленный адрес.

Цитата из справочной страницы bind()

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

и из того же для connect()

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

Чтобы уточнить,

  • bind()связывает сокет с его локальным адресом [поэтому на стороне сервера binds, чтобы клиенты могли использовать этот адрес для подключения к серверу.]
  • connect() используется для подключения к удаленному [серверу] адресу, поэтому на стороне клиента используется подключение [читается как: подключение к серверу].
Сурав Гош
источник
Итак, скажем, если и сервер, и клиентский процесс работают на одной машине, могут ли они использоваться взаимозаменяемо?
Сиддхартха Гош
1
@SiddharthaGhosh Нет. Возможно, клиент и сервер находятся на одном компьютере, но все же это разные процессы, верно? Оба API обслуживают собственное приложение. Их никогда не бываетinterchangeable
Сурав Гош
Что в данном контексте подразумевается под локальным и удаленным?
Сиддхартха Гош
@SiddharthaGhosh local-> сам процесс, remote-> другой процесс.
Sourav Ghosh
@SouravGhosh, значит, я не могу указать порт для привязки на стороне клиента?
Hengqi Chen
12

bind сообщает запущенному процессу требовать порт. т.е. он должен привязаться к порту 80 и прослушивать входящие запросы. с bind ваш процесс становится сервером. когда вы используете connect, вы говорите своему процессу подключиться к порту, который УЖЕ используется. ваш процесс становится клиентом. разница важна: bind хочет порт, который не используется (чтобы он мог потребовать его и стать сервером), а connect хочет порт, который уже используется (чтобы он мог подключиться к нему и поговорить с сервером)

Филипп Марри
источник
9

Из Википедии http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

подключения ():

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

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

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

Bind ():

bind () назначает сокет адресу. Когда сокет создается с помощью socket (), ему предоставляется только семейство протоколов, но не назначается адрес. Это сопоставление с адресом должно быть выполнено с помощью системного вызова bind (), прежде чем сокет сможет принимать соединения с другими хостами. bind () принимает три аргумента:

sockfd, дескриптор, представляющий сокет для выполнения привязки. my_addr, указатель на структуру sockaddr, представляющую адрес для привязки. addrlen, поле socklen_t, определяющее размер структуры sockaddr. Bind () возвращает 0 в случае успеха и -1 в случае ошибки.

Примеры: 1.) Использование 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.) Пример привязки:

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(), которая вернет ошибку, потому что без назначенного порта и привязанного адреса нет ничего (или слишком много, в зависимости от того, как вы интерпретируете отсутствие привязки).

pjcard
источник
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 ).
Рикардо Биль Паскуали
источник