Страницы

Поиск по вопросам

суббота, 1 февраля 2020 г.

Непонятки с функцией connect и accept

#c #сеть


Ребят, у меня тут назрело два вопроса:




Функция connect (int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);)
принимает во второй аргумент адрес сервера, тогда зачем мне указывать какое семейство
адресов у сокета этого сервера в приложении клиенте (addr.sin_family=AF_INET;)? Почему
нельзя просто айпишник и порт указать?





В строке socket2=accept(socket1,(struct sockaddr*)&client,(socklen_t*)&len_client);
во втором аргументе указана структура, в которую записывается адрес сокета клиента.
Вопрос, откуда он берет этот адрес? Я же не передаю его никак в функцию connect.

    


Ответы

Ответ 1



зачем мне указывать какое семейство адресов у сокета этого сервера(addr.sin_family=AF_INET;)? Почему нельзя просто айпишник и порт указать? Потому, что один и тот же порт, зачастую, может работать как с UDP, так и с TCP. Например: 25 порт - SMTP: UDP/TCP Может принимать пакеты на UDP порт и соединения на TCP порт. откуда он берет этот адрес? Согласно "RFC 791 — Протокол IP" в заголовке любого пакета IP должны присутствовать как адрес получателя (т.е. алрес Вашего сервера) так и адрес отправителя - клиента, пытающегося работать с Вашим сервером.

Ответ 2



Интересный вопрос (особенно первая часть -- "...зачем мне указывать какое семейство адресов у сокета этого сервера?", побудившая меня перечитать manpages) В самом деле, если мы посмотрим на вызов socket(), то увидим, что в аргументе domain задаются те же константы (AF_INET и т.п.), что и в поле sockadr.sin_family в вызове connect(). На первый взгляд кажется, что и в самом деле это поле избыточно при вызове connect(). Однако, оно используется по крайней мере для достижения двух целей. Во-первых, это проверка целостности (не противоречивости) данных. При вызове connect() происходит проверка, что семейство адресов, задаваемое в поле .sin_family адреса сервера соответствует допустимым протоколам сокета (address_families), которые были заданы в аргументе domain при его создании. А второй возможный ответ кроется вот в этом абзаце из man connect: Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change their association. Connectionless sockets may dissolve the association by connecting to an address with the sa_family member of sockaddr set to AF_UNSPEC (supported on Linux since kernel 2.2). более конкретно, в словах: "Connectionless sockets may dissolve the association......with the sa_family member of sockaddr set to AF_UNSPEC" Т.е. для сокетов типа SOCK_STREAM (а именно с ними мы в основном используем connect) поле .sin_family (если отбросить проверки непротиворечивости) избыточно, но оно нужно (очевидно, не часто) для использования connect с SOCK_DGRAM сокетами (обычно мы вообще не используем connect(), работая с ними, а задаем адрес получателя непосредственно в вызове sendto). Что касается второй части вопроса об адресе клиента в серверном вызове accept() --"откуда он берет этот адрес? Я же не передаю его никак в функцию connect", то ответ следующий: accept() срабатывает на стороне сервера в ответ на connect() у клиента. ОС на стороне сервера записывает во второй аргумент accept() адрес клиента. Само значение своего адреса клиент либо задает сам, вызывая bind перед вызовом connect(), либо (если bind() не использовали) оно формируется ОС в ходе вызова connect() из первичного адреса сетевого интерфейса, который алгоритмы роутинга выбирают для данного соединения и свободного динамического номера порта (см. тут (обычно где-то от 65535 до 49000)). Далее этот адрес передается во всех IP-пакетах (включая первый пакет с запросом соединения), которыми обмениваются клиент с сервером.

Комментариев нет:

Отправить комментарий