Когда клиентский сокет должен быть привязан, чтобы получать UDP-сообщения от сервера?


Я видел два примера, которые иллюстрируют, как клиентский сокет может получать сообщения от сервера.

Пример 1:

Код сервера http://man7.org/tlpi/code/online/book/sockets/ud_ucase_sv.c.html

Клиентский код http://man7.org/tlpi/code/online/book/sockets/ud_ucase_cl.c.html

Клиентская программа создает сокет и привязывает сокет к адресу, чтобы сервер мог отправить свой ответ.
if (bind(sfd, (struct sockaddr *) &claddr, sizeof(struct sockaddr_un)) == -1)
    errExit("bind"); // snippet from ud_ucase_cl.c

Пример 2:

Сервер код http://man7.org/tlpi/code/online/book/sockets/i6d_ucase_sv.c.html

Клиентский код http://man7.org/tlpi/code/online/book/sockets/i6d_ucase_cl.c.html

В Примере 2 клиентский код не связывает свой сокет с адресом.

Вопрос :

необходимо ли клиентскому коду связывать сокет с адресом для получения сообщения от сервера?

Почему в первом примере мы должны привязать клиента сокет с адресом, почему мы не должны во втором примере?

3   2  

3 ответа:

Разница в том, что socket family - первый пример использования AF_UNIX, в то время как второй делает AF_INET6. Согласно Stevens UNP вам нужно явно bind указать путь к клиентскому сокету Unix, чтобы сервер имел путь, на который он может отправить свой ответ:

... отправка дейтаграммы в несвязанный сокет дейтаграммы домена Unix не связывает неявно путь к сокету. Поэтому, если мы опустим этот шаг, вызов сервера recvfrom ... возвращает значение null имя пути ...

Это не требуется для сокетов INET{4,6}, поскольку они "автоматически привязаны" к эфемерному порту.

Для клиента (TCP) или отправителя (UDP) вызов bind() необязателен; это способ указать интерфейс. Предположим, что у вас есть два интерфейса, которые оба маршрутизируются к месту назначения:

eth0: 10.1.1.100/24
eth1: 10.2.2.100/24

route: 10.1.1.0/24 via 10.2.2.254  # router for eth1
       0.0.0.0     via 10.1.1.254  # general router
Теперь, если вы просто говорите connect() 12.34.56.78, Вы не знаете, какой локальный интерфейс обеспечивает локальную сторону соединения. Сначала вызывая bind(), Вы делаете это конкретным.

То же самое верно и для UDP-трафика: без bind()ing ваш sendto() будет использовать случайный адрес источника и порт, но с bind() Вы делаете источник конкретным.

Если вы не привязали клиентский сокет AF_INET/AF_INET6 перед подключением / отправкой чего-либо, стек TCP/IP автоматически свяжет его с эфемерным портом на исходящем адресе.

В отличие от этого, доменные сокеты UNIX (AF_UNIX) не привязываются автоматически при отправке, поэтому вы можете отправлять сообщения через SOCK_DGRAM, но не можете получать ответы.