2014-11-19 2 views
66

И connect(), и bind() системные вызовы «связывают» дескриптор файла сокета с адресом (обычно это комбинация ip/port). Их прототипы, как: -socket connect() vs bind()

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(). Разум не был полностью ясен для меня.

+8

В одном предложении: привязка к локальному адресу, подключение к удаленному адресу. – SHR

ответ

126

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

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

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

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

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

enter image description here

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

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

+0

спасибо брату. Со схемой все может быстро разрядиться. Можете ли вы сказать, в чем разница, если мы используем udp? – apm

+6

accept()
следует перемещать ниже
блок до момента подключения от клиента – tschodt

24

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

Цитируя человека странице bind()

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

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

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

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

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

Итак, скажем, если оба сервера и клиентский процесс работают на одном компьютере, можно ли их использовать взаимозаменяемо? –

+0

@SiddharthaGhosh Нет. Возможно, клиент и сервер находятся на одной машине, но все же они ** являются ** разными процессами, правильно? Оба API обслуживают свои собственные манеры. Они никогда не являются «взаимозаменяемыми» –

+0

Что конкретно означает местный и удаленный в этом контексте? –

4

Материал из Википедии http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

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

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

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

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

связывания():

связывания() назначает сокет к адресу. Когда сокет создается с помощью 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.) Bind Пример:

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

Я надеюсь, что проясняет разницу

EDIT: Пожалуйста, обратите внимание, что гнездо тип, который вы заявляете, будет зависеть от того, что вам нужно, это чрезвычайно важно.

4

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

1

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