Inaddr any что это
tcp_socket = socket(PF_INET, SOCK_STREAM, 0);
raw_socket = socket(PF_INET, SOCK_RAW, protocol );
udp_socket = socket(PF_INET, SOCK_DGRAM, protocol );
ОПИСАНИЕ
Интерфейс программирования совместим с интерфейсом BSD-сокетов. Более подробную информацию смотри в socket (7).
— это IP-протокол, указанный в IP-заголовке, который будет принят или отослан. Единственные возможные значения для параметра протокол
— это 0 или IPPROTO_TCP для TCP сокетов, 0 или IPPROTO_UDP для UDP сокетов. Для SOCK_RAW можно указать зарегистрированный в IANA IP-протокол, один из тех, что описаны в RFC1700.
ФОРМАТ АДРЕСА
Заметьте, что значения адреса и порта всегда хранятся в сетевом формате. В частности, это означает, что требуется вызывать htons (3) для числа, обозначающего порт. Все функции из стандартной библиотеки, манипулирующие с адресами/портами, работают с сетевым форматом.
ОПЦИИ СОКЕТА
IP_TTL Устанавливает или получает текущее значение поля Время Жизни (time to live), которое указывается в каждом пакете, который отсылается с этого сокета.
| Флаги опции | Их значения |
| Path MTU Discovery | |
| =================== | ============ |
| IP_PMTUDISC_WANT | Использовать установки |
| маршрутизаторов. | |
| IP_PMTUDISC_DONT | Никогда не производить |
| обнаружение MTU маршрута. | |
| IP_PMTUDISC_DO | Всегда производить обнаружение |
| MTU маршрута. |
Если опция Path MTU Discovery включена, то ядро автоматически следит за MTU маршрута для каждого удаленного хоста. Если с некоторым узлом устанавливается соединение с помощью connect (2), то текущее значение MTU маршрута может быть установлено заново используя опцию сокета IP_MTU (например, после возникновения ошибки EMSGSIZE ). Значение MTU может меняться время от времени. Для сокетов без предварительного установления соединения, которые имеют несколько хостов-получателей, новое значение MTU для заданного хоста может быть получено с помощью очереди ошибок (смотри IP_RECVERR ). При каждом входящем сообщении об обновлении MTU, в очередь будет поставлена новая ошибка.
Во время процесса обнаружения MTU, пакеты, инициализирующие соединение, от датаграмных сокетов, могут быть отброшены. Приложения, использующие UDP, должны знать это и не принимать во внимание в своих методах повторной передачи данных.
Чтобы запустить процесс обнаружения MTU маршрута для сокетов, не установивших соединение, можно сначала установить большой размер датаграммы (с размером заголовка до 64K) и позволить обновлениям MTU маршрута сократить его.
SYSCTL-ЗНАЧЕНИЯ
Если этот флаг включен (его значение не равно 0), то входящие фрагменты (части IP пакетов, которые образуются, если некоторый хост, находящийся между отправителем и адресатом, решает, что пакеты слишком велики и разделяет их на кусочки) будут снова собраны (дефрагментированы) перед дальнейшей обработкой, даже если они должны быть пересланы дальше.
Включайте эту опцию только на файерволе, который является единственной связью с вашей сетью или на прозрачном прокси-сервере; никогда не включайте ее на нормальном маршрутизаторе или хосте. В противном случае, соединение может быть нарушено если фрагменты передаются по различным линиям. Дефрагментация также требует много памяти и процессорного времени.
Эта опция включается автоматически, если конфигурируется маскарадинг или прозрачный прокси-сервер. neigh/* Смотри arp (7).
IOCTLS
Ioctls для конфигурирования характерных параметров устройств, описаны в netdevice (7).
ЗАМЕЧАНИЯ
КОДЫ ОШИБОК
EADDRINUSE Попытка связать сокет с уже используемым адресом. ENOPROTOOPT и EOPNOTSUPP Передана недопустимая опция. EPERM У пользователя нет достаточных полномочий, чтобы повысить приоритет, изменить конфигурацию или послать сигнал запрашиваемому процессу или группе процессов. EADDRNOTAVAIL Был запрошен несуществующий интерфейс или запрошенный исходящий адрес не является локальным. EAGAIN Действие над неблокирующим сокетом привело бы к его блокировке. ESOCKTNOSUPPORT Сокет не сконфигурирован или запрошен неизвестный тип сокета. EISCONN Функция connect (2) вызвана для сокета, уже установившего соединение. EALREADY Операция соединения на неблокируемом сокете уже находится в процессе выполнения. ECONNABORTED Соединение закрыто во время accept (2). EPIPE Соединение неожиданно закрылось или завершено другой стороной. ENOENT SIOCGSTAMP вызван для сокета, который еще не получил ни одного пакета. EHOSTUNREACH В таблице маршрутизации нет разрешенных записей, соответствующих адресу назначения. Эта ошибка может возникнуть из-за ICMP-сообщения от удаленного маршрутизатора или из-за локальной таблицы маршрутизации. ENODEV Сетевое устройство недоступно или неспособно посылать IP пакеты. ENOPKG IP-подсистема ядра не сконфигурирована. ENOBUFS, ENOMEM Недостаточно свободной памяти. Часто это означает, что распределение памяти ограничивается не размером системной памяти, а границами буфера сокета, но это не всегда так.
Протоколами более высокого уровня могут генерироваться другие ошибки; смотри tcp (7), raw (7), udp (7) и socket (7).
ВЕРСИИ
Sysctl-значения были введены в Linux 2.2.
СОВМЕСТИМОСТЬ
ОШИБКИ РЕАЛИЗАЦИИ
Не описаны ioctls для конфигурирования специфичных для IP опций интерфейса и таблиц ARP.
Получение исходного адреса назначения в msg_name с помощью MSG_ERRQUEUE функцией recvmsg (2) не работает в некоторых ядрах 2.2.
АВТОРЫ
СМОТРИ ТАКЖЕ
RFC791, где описана изначальная спецификация IP.
RFC1122, где описаны требования к хосту для IPv4.
RFC1812, где описаны требования к маршрутизатору для IPv4.
Понимание INADDR_ANY для программирования сокетов
Я получаю 0 в качестве возвращаемого значения. Может кто-нибудь принести какое-то объяснение?
ОТВЕТЫ
Ответ 1
Для сервера обычно требуется привязка ко всем интерфейсам, а не только к локальному хосту.
Если вы еще не знакомы с этим, я настоятельно рекомендую вам ознакомиться с Beej Guide to Sockets Programming:
Поскольку люди все еще читают это, дополнительная заметка:
Когда процесс хочет получать новые входящие пакеты или соединения, он должен привязать сокет к адресу локального интерфейса, используя bind (2).
В этом случае только один IP-сокет может быть привязан к любой локальной (адрес, порт). Когда INADDR_ANY указывается в вызове bind, сокет будет привязан ко всем локальным интерфейсам.
Когда вызов (2) вызывается на несвязанный сокет, сокет автоматически привязывается к произвольному свободному порту с локальным адресом, установленным в INADDR_ANY.
Когда вызов connect (2) вызывается на несвязанный сокет, сокет автоматически привязан к произвольному свободному порту или к используемому совместно используемому порту с локальным адресом, установленным в INADDR_ANY.
Существует несколько специальных адресов: INADDR_LOOPBACK (127.0.0.1) всегда ссылается на локальный хост через устройство loopback; INADDR_ANY (0.0.0.0) означает любой адрес для привязки.
Если поле (sin_addr.s_addr) установлено в константу INADDR_ANY, как определено в netinet/in.h, вызывающий абонент запрашивает, чтобы сокет был привязан ко всем сетевым интерфейсам на хосте. Впоследствии UDP-пакеты и TCP-соединения из всех интерфейсов (которые соответствуют имени привязки) направляются в приложение. Это становится важным, когда сервер предлагает услугу для нескольких сетей. Если оставить адрес не указанным, сервер может принимать все UDP-пакеты и запросы TCP-соединения, сделанные для своего порта, независимо от сетевого интерфейса, по которому были получены запросы.
Ответ 2
Ответ 3
Чтобы связать сокет с localhost, перед вызовом функции bind, необходимо установить значение поля sin_addr.s_addr структуры sockaddr_in. Собственное значение может быть получено либо через
Ответ 4
Ответ 5
Также обратите внимание, что при IN6ADDR_ANY_INIT сокета IPv6 к IN6ADDR_ANY_INIT ваш сокет будет привязываться ко всем интерфейсам IPv6 и должен также принимать соединения от клиентов IPv4 (хотя и с адресами, сопоставленными IPv6).
Вопрос о INADDR_ANY
Константа INADDR_ANY – это так называемый адресной знак IPv4. групповой IP-адрес полезен для приложений, которые связывают Интернет доменных сокетов на многоходовых хостах. Если приложение на многопользовательском хост связывает сокет только с одним из его IP-адресов хостов, затем socket может принимать только датаграммы UDP или отправленные запросы TCP-соединения на этот IP-адрес. Однако мы обычно хотим, чтобы приложение было для многоточечного хоста, чтобы иметь возможность принимать датаграммы или запросы на соединение которые определяют любой из IP-адресов хостов и привязывают сокет к подстановочный IP-адрес делает это возможным.
Если мы привязываем сокет к определенному IP-адресу, то сокет может получать только сообщения UPD/TCP, отправленные на этот IP-адрес.
Как показано в приведенном выше коде, теперь сокет server_sockfd связан с INADDR_ANY.
Я просто чувствую смущение здесь b/c, если сокет может получить любой запрос в Интернете, как он все еще может работать хорошо. Есть тонны запросов UDP/TCP в Интернете, если сокет отвечает всем,
, как он может работать?
//обновленный код для клиентской стороны //
//обновлен для серверного кода
//обновлено для того, как запускать эти серверные/клиентские программы.
Он не получает запросы на каждый IP-адрес в Интернете (a) он получает запросы на каждый IP-адрес, который он обслуживает. Например, он может иметь несколько сетевых адаптеров, каждый из которых имеет отдельный IP-адрес или может иметь один NIC, способный управлять несколькими IP-адресами (он может иметь несколько сетевых адаптеров, каждый из которых способен обрабатывать несколько IP-адресов.
Ключевым фрагментом для просмотра является:
… Обычно мы хотим, чтобы приложение на многодомном хосте могло получать датаграммы или запросы на подключение, которые указывают любой из IP-адресов хостов (курсив мой).
В следующей таблице, где верхняя строка – это адресаты запроса, а левый столбец – адрес, который вы слушаете, показывает, будет ли вам предоставлен запрос ( Y ) или нет ( N ):
(a) Он даже не видит подавляющее большинство запросов в сети. Подавляющее большинство даже не добирается до вашего ближайшего маршрутизатора (или, возможно, даже вашего интернет-провайдера). Даже те, которые делают это до вашего ближайшего маршрутизатора, ваша конкретная машина может не видеть, предназначены ли они для другой машины на локальном сегменте (несмотря на беспорядочный режим).
Сетевое программирование для разработчиков игр. Часть 2: прием и передача пакетов данных
Прием и передача пакетов данных
Введение
Привет, меня зовут Гленн Фидлер и я приветствую вас в своей второй статье из цикла “Сетевое программирование для разработчиков игр”.
В предыдущей статье мы обсудили различные способы передачи данных между компьютерами по сети, и в конце решили использовать протокол UDP, а не TCP. UDP мы решили использовать для того, чтобы иметь возможность пересылать данные без задержек, связанных с ожиданием повторной пересылки пакетов.
А сейчас я собираюсь рассказать вам, как на практике использовать UDP для отправки и приема пакетов.
BSD сокеты
В большинстве современных ОС имеется какая-нибудь реализация сокетов, основанная на BSD сокетах (сокетах Беркли).
Сокеты BSD оперируют простыми функциями, такими, как “socket”, “bind”, “sendto” и “recvfrom”. Конечно, вы можете обращаться к этим функциями напрямую, но в таком случае ваш код будет зависим от платформы, так как их реализации в разных ОС могут немного отличаться.
Поэтому, хоть я далее и приведу первый простой пример взаимодействия с BSD сокетами, в дальнейшем мы не будем использовать их напрямую. Вместо этого, после освоения базового функционала, мы напишем несколько классов, которые абстрагируют всю работу с сокетами, чтобы в дальнейшем наш код был платформонезависимым.
Особенности разных ОС
Для начала напишем код, который будет определять текущую ОС, чтобы мы могли учесть различия в работе сокетов:
Теперь подключим заголовочные файлы, нужные для работы с сокетами. Так как набор необходимых заголовочных файлов зависит от текущей ОС, здесь мы используем код #define, написанный выше, чтобы определить, какие файлы нужно подключать.
В UNIX системах функции работы с сокетами входят в стандартные системные библиотеки, поэтому никакие сторонние библиотеки нам в этом случае не нужны. Однако в Windows для этих целей нам нужно подключить библиотеку winsock.
Вот небольшая хитрость, как можно это сделать без изменения проекта или makefile’а:
Мне нравится этот прием потому, что я ленивый. Вы, конечно, можете подключить библиотеку в проект или в makefile.
Инициализация сокетов
В большинстве unix-like операционных систем (включая macosx) не требуется никаких особых действий для инициализации функционала работы с сокетами, но в Windows нужно сначала сделать пару па — нужно вызвать функцию “WSAStartup” перед использованием любых функций работы с сокетами, а после окончания работы — вызвать “WSACleanup”.
Давайте добавим две новые функции:
Теперь мы имеем независимый от платформы код инициализации и завершения работы с сокетами. На платформах, которые не требуют инициализации, данный код просто не делает ничего.
Создаем сокет
Теперь мы можем создать UDP сокет. Это делается так:
Далее мы должны привязать сокет к определенному номеру порта (к примеру, 30000). У каждого сокета должен быть свой уникальный порт, так как, когда приходит новый пакет, номер порта определяет, какому сокету его передать. Не используйте номера портов меньшие, чем 1024 — они зарезервированы системой.
Если вам все равно, какой номер порта использовать для сокета, вы можете просто передать в функцию “0”, и тогда система сама выделит вам какой-нибудь незанятый порт.
Теперь наш сокет готов для передачи и приема пакетов данных.
Но что это за таинственная функция “htons” вызывается в коде? Это просто небольшая вспомогательная функция, которая переводит порядок следования байтов в 16-битном целом числе — из текущего (little- или big-endian) в big-endian, который используется при сетевом взаимодействии. Ее нужно вызывать каждый раз, когда вы используете целые числа при работе с сокетами напрямую.
Вы встретите функцию “htons” и ее 32-битного двойника — “htonl” в этой статье еще несколько раз, так что будьте внимательны.
Перевод сокета в неблокирующий режим
По умолчанию сокеты находится в так называемом “блокирующем режиме”. Это означает, что если вы попытаетесь прочитать из него данные с помощью “recvfrom”, функция не вернет значение, пока не сокет не получит пакет с данными, которые можно прочитать. Такое поведение нам совсем не подходит. Игры — это приложения, работающие в реальном времени, со скоростью от 30 до 60 кадров в секунду, и игра не может просто остановиться и ждать, пока не придет пакет с данными!
Решить эту проблему можно переведя сокет в “неблокирующий режим” после его создания. В этом режиме функция “recvfrom”, если отсутствуют данные для чтения из сокета, сразу возвращает определенное значение, показывающее, что нужно будет вызвать ее еще раз, когда в сокете появятся данные.
Перевести сокет в неблокирующий режим можно следующим образом:
Как вы можете видеть, в Windows нет функции “fcntl”, поэтому вместе нее мы используем “ioctlsocket”.
Отправка пакетов
UDP — это протокол без поддержки соединений, поэтому при каждой отправке пакета нам нужно указывать адрес получателя. Можно использовать один и тот же UDP сокет для отправки пакетов на разные IP адреса — на другом конце сокета не обязательно должен быть один компьютер.
Переслать пакет на определенный адрес можно следующим образом:
Обратите внимание — возвращаемое функцией “sendto” значение показывает только, был ли пакет успешно отправлен с локального компьютера. Но оно не показывает, был ли пакет принят адресатом! В UDP нет средств для определения, дошел ли пакет по назначению или нет.
В коде, приведенном выше, мы передаем структуру “sockaddr_in” в качестве адреса назначения. Как нам получить эту структуру?
Допустим, мы хотим отправить пакет по адресу 207.45.186.98:30000.
Запишем адрес в следующей форме:
И нужно сделать еще пару преобразований, чтобы привести его к форме, которую понимает “sendto”:
Как видно, сначала мы объединяем числа a, b, c, d (которые лежат в диапазоне [0, 255]) в одно целое число, в котором каждый байт — это одно из исходных чисел. Затем мы инициализируем структуру “sockaddr_in” нашими адресом назначения и портом, при этом не забыв конвертировать порядок байтов с помощью функций “htonl” и “htons”.
Отдельно стоит выделить случай, когда нужно передать пакет самому себе: при этом не нужно выяснять IP адрес локальной машины, а можно просто использовать 127.0.0.1 в качестве адреса (адрес локальной петли), и пакет будет отправлен на локальный компьютер.
Прием пакетов
После того, как мы привязали UDP сокет к порту, все UDP пакеты, приходящие на IP адрес и порт нашего сокета, будут ставиться в очередь. Поэтому для приема пакетов мы просто в цикле вызываем “recvfrom”, пока он не выдаст ошибку, означающую, что пакетов для чтения в очерели не осталось.
Так как протокол UDP не поддерживает соединения, пакеты могут приходить с множества различных компьютеров сети. Каждый раз, когда мы принимаем пакет, функция “recvfrom” выдает нам IP адрес и порт отправителя, и поэтому мы знаем, кто отправил этот пакет.
Код приема пакетов в цикле:
Пакеты, размер которых больше, чем размер буфера приема, будут просто втихую удалены из очереди. Так что, если вы используете буфер размером 256 байтов, как в примере выше, и кто-то присылает вам пакет в 300 байт, он будет отброшен. Вы не получите просто первые 256 байтов из пакета.
Но, поскольку мы пишем свой собственный протокол, для нас это не станет проблемой. Просто всегда будьте внимательны и проверяете, чтобы размер буфера приема был достаточно большим, и мог вместить самый большой пакет, который вам могут прислать.
Закрытие сокета
На большинстве unix-like систем, сокеты представляют собой файловые дескрипторы, поэтому для того, чтобы закрыть сокеты после использования, можно использовать стандартную функцию “close”. Однако, Windows, как всегда, выделяется, и в ней нам нужно использовать “closesocket”.
Так держать, Windows!
Класс сокета
Итак, мы разобрались со всеми основными операциями: создание сокета, привязка его к порту, перевод в неблокирующий режим, отправка и прием пакетов, и, в конце, закрытие сокета.
Но, как вы могли заметить, все эти операции немного отличаются от платформы к платформе, и, конечно, трудно каждый раз при работе с сокетами вспоминать особенности разных платформ и писать все эти #ifdef.
Поэтому мы сделаем класс-обертку “Socket” для всех этих операций. Также мы создадим класс “Address”, чтобы было проще работать с IP адресами. Он позволит не проводить все манипуляции с “sockaddr_in” каждый раз, когда мы захотим отправить или принять пакет.
Итак, наш класс Socket:
Использовать их для приема и передачи нужно следующим образом:
Как видите, это намного проще, чем работать с BSD сокетами напрямую. И также этот код будет одинаков для всех ОС, потому весь платформозависимый функционал находится внутри классов Socket и Address.
Заключение
Теперь у нас есть независимый от платформы инструмент для отправки и према UDP пакетов.
UDP не поддерживает соединения, и мне хотелось сделать пример, который бы четко это показал. Поэтому я написал небольшую программу, которая считывает список IP адресов из текстового файла и рассылает им пакеты, по одному в секунду. Каждый раз, когда программа принимает пакет, она выводит в консоль адрес и порт компьютера-отправителя и размер принятого пакета.
Вы можете легко настроить программу так, чтобы даже на локальной машине получить несколько узлов, обменивающихся пакетами друг с другом. Для этого просто разным экземплярам программы задайте разные порты, например:
> Node 30000
> Node 30001
> Node 30002
И т.д…
Каждый из узлов будет пересылать пакеты всем остальным узлам, образуя нечто вроде мини peer-to-peer системы.
Я разрабатывал эту программу на MacOSX, но она должна компилироваться на любой unix-like ОС и на Windows, однако если вам для этого потребуется делать какие-либо доработки, сообщите мне.
Понимание INADDR для программирования сокетов
5 ответов
для сервера вы обычно хотите привязаться ко всем интерфейсам, а не только к «localhost».
если вы еще не знакомы с ним, я призываю вас проверить руководство Beej по программированию сокетов:
поскольку люди все еще читают это, дополнительное Примечание:
когда процесс хочет получать новые входящие пакеты или соединения, он должен привязать сокет к локальному адресу интерфейса, используя bind (2).
в этом случае только один IP-сокет может быть привязан к любому заданному локальному (адрес, порт) пара. Когда INADDR_ANY указан в вызове bind, сокет будет привязан ко всем локальным интерфейсам.
, когда слушать(2) вызывается на несвязанном сокете, сокет автоматически привязывается к случайному бесплатно порт с локального адреса к INADDR_ANY.
, когда подключиться(2) вызывается на несвязанном сокете, сокет автоматически привязывается к случайному Свободному порту или к совместному порту с локальным адресом, установленным в INADDR_ANY.
существует несколько специальных адресов: INADDR_LOOPBACK (127.0.0.1) всегда ссылается на локальный хост через устройство loopback; INADDR_ANY (0.0.0.0) означает любой адрес обязательный.
если (sin_addr.s_addr) поле имеет значение константа INADDR_ANY, как определенными в netinet/в.ч, абонент просит сокет привязан ко всем сетевым интерфейсам на хосте. Впоследствии, UDP пакеты и TCP-соединения от всех интерфейсов (которые соответствуют связанному имени) направляются в приложение. Это становится важным, когда сервер предлагает услугу нескольким сетям. Оставив адрес не указано, сервер может принимать все UDP-пакеты и TCP-соединение запросы на порт, независимо от сетевого интерфейса какие запросы поступили.




