Когда клиентский сокет должен быть привязан, чтобы получать 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 ответа:
Разница в том, что
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, но не можете получать ответы.