класс Socket
Класс Socket предоставляет доступ к реализациям сокетов базовой операционной системы. Его можно использовать для предоставления более специфичных для операционной системы функций, чем классы сокетов, специфичные для протокола.
Константы, определённые в Socket::Constants, также определены в Socket. Например, можно использовать Socket::AF_INET, а также Socket::Constants::AF_INET. Список констант см. в Socket::Constants.
Что такое сокет?
Сокеты — это конечные точки двунаправленного канала связи. Сокеты могут взаимодействовать внутри одного процесса, между процессами на одной машине или между разными машинами. Существует множество типов сокетов: например, TCPSocket, UDPSocket или UNIXSocket.
У сокетов есть своя терминология:
домен: семейство протоколов:
тип: тип связи между двумя конечными точками, обычно
протокол: обычно ноль. Это значение может использоваться для идентификации варианта протокола.
имя хоста: идентификатор сетевого интерфейса:
-
строка (имя хоста, адрес IPv4 или IPv6 либо
broadcast, задающий широковещательный адрес) -
строка нулевой длины, задающая
INADDR_ANY -
целое число (интерпретируемое как двоичный адрес в порядке байтов узла).
Краткое руководство
Многие классы, такие как TCPSocket, UDPSocket или UNIXSocket, упрощают работу с сокетами по сравнению с соответствующим интерфейсом программирования на C.
Создадим интернет-сокет с использованием протокола IPv4 в стиле C:
require 'socket' s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM s.connect Socket.pack_sockaddr_in(80, 'example.com')
Можно также использовать класс TCPSocket:
s = TCPSocket.new 'example.com', 80
Простой сервер может выглядеть так:
require 'socket'
server = TCPServer.new 2000 # Server bound to port 2000
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
Простой клиент может выглядеть так:
require 'socket' s = TCPSocket.new 'localhost', 2000 while line = s.gets # Read lines from socket puts line # and print them end s.close # close socket when done
Exception Обработка
Реализация Socket в Ruby генерирует исключения на основе ошибок, возникающих в зависящей от системы реализации. Поэтому методы описаны таким образом, чтобы отделить исключения систем на базе Unix от исключений систем на базе Windows. Если нужны дополнительные сведения о конкретном исключении, обратитесь к страницам руководства Unix или справочнику WinSock для Windows.
Вспомогательные методы
Хотя обычно сокеты создаются с помощью Socket.new, для большинства случаев предусмотрено несколько методов создания сокетов.
- Клиентский TCP-сокет
- Серверный TCP-сокет
- Клиентский UNIX-сокет
- Серверный UNIX-сокет
Авторы документации
-
Zach Dennis
-
Sam Roberts
-
Programming Ruby издательства The Pragmatic Bookshelf.
Значительная часть материала в этой документации использована с разрешения из книги Programming Ruby издательства The Pragmatic Bookshelf.
Константы
- AF_ALG
-
Интерфейс к криптографическому API ядра
- AF_APPLETALK
-
Протокол AppleTalk
- AF_ATM
-
Режим асинхронной передачи
- AF_AX25
-
Протокол AX.25
- AF_BLUETOOTH
-
Низкоуровневый протокол сокетов Bluetooth
- AF_CAN
-
Протокол автомобильной шины Controller Area Network
- AF_CCITT
-
Протоколы CCITT (ныне ITU-T)
- AF_CHAOS
-
Протоколы MIT CHAOS
- AF_CNT
-
Технология компьютерных сетей
- AF_COIP
-
IP с установлением соединения
- AF_DATAKIT
-
Протокол Datakit
- AF_DEC
-
Протокол DECnet
- AF_DECnet
-
Протокол DECnet
- AF_DLI
-
Протокол интерфейса прямой передачи данных DEC
- AF_E164
-
Рекомендация CCITT (ITU-T) E.164
- AF_ECMA
-
Протоколы Европейской ассоциации производителей компьютеров
- AF_HYLINK
-
Протокол NSC Hyperchannel
- AF_IB
-
Нативная адресация InfiniBand
- AF_IMPLINK
-
Протокол ARPANET IMP
- AF_INET
-
Протокол IPv4
- AF_INET6
-
Протокол IPv6
- AF_IPX
-
Протокол IPX
- AF_ISDN
-
Цифровая сеть с интеграцией служб
- AF_ISO
-
Протоколы взаимодействия открытых систем ISO
- AF_KCM
-
Интерфейс KCM (мультиплексор соединений ядра)
- AF_KEY
-
Протокол управления ключами, первоначально разработанный для использования с IPsec
- AF_LAT
-
Протокол Local Area Transport
- AF_LINK
-
Интерфейс канального уровня
- AF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- AF_LOCAL
-
Внутренние протоколы хоста
- AF_MAX
-
Максимальное семейство адресов для этой платформы
- AF_MPLS
-
Многопротокольная коммутация по меткам
- AF_NATM
-
Прямой доступ к ATM
- AF_NDRV
-
Прямой доступ к сетевому драйверу
- AF_NETBIOS
-
NetBIOS
- AF_NETGRAPH
-
Сокеты Netgraph
- AF_NETLINK
-
Устройство интерфейса ядра с пользовательским пространством
- AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы взаимодействия открытых систем ISO
- AF_PACKET
-
Прямой доступ к канальному уровню
- AF_PPP
-
Протокол точка-точка
- AF_PPPOX
-
Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)
- AF_PUP
-
Протокол PARC Universal Packet
- AF_RDS
-
Протокол Reliable Datagram Sockets (RDS)
- AF_ROUTE
-
Внутренний протокол маршрутизации
- AF_SIP
-
Простой интернет-протокол
- AF_SNA
-
Протокол IBM SNA
- AF_SYSTEM
-
Сообщения о событиях ядра
- AF_TIPC
-
Протокол TIPC («сокеты домена кластера»)
- AF_UNIX
-
Сокеты UNIX
- AF_UNSPEC
-
Неуказанный протокол, любое поддерживаемое семейство адресов
- AF_VSOCK
-
Протокол VSOCK (первоначально «VMWare VSockets») для связи гипервизора с гостевой системой
- AF_XDP
-
Интерфейс XDP (express data path)
- AI_ADDRCONFIG
-
Принимать только при наличии назначенного адреса
- AI_ALL
-
Разрешить все адреса
- AI_CANONNAME
-
Заполнить каноническое имя
- AI_DEFAULT
-
Флаги по умолчанию для getaddrinfo
- AI_MASK
-
Допустимая маска флагов для getaddrinfo (не для использования приложениями)
- AI_NUMERICHOST
-
Запретить разрешение имени хоста
- AI_NUMERICSERV
-
Запретить разрешение имени службы
- AI_PASSIVE
-
Получить адрес для использования с bind()
- AI_V4MAPPED
-
Принимать IPv4-адреса, отображённые в IPv6
- AI_V4MAPPED_CFG
-
Принимать IPv4-адреса, отображённые в IPv6, если ядро это поддерживает
- EAI_ADDRFAMILY
-
Семейство адресов для имени хоста не поддерживается
- EAI_AGAIN
-
Временный сбой разрешения имени
- EAI_BADFLAGS
-
Недопустимые флаги
- EAI_BADHINTS
-
Недопустимое значение hints
- EAI_FAIL
-
Неустранимый сбой разрешения имени
- EAI_FAMILY
-
Семейство адресов не поддерживается
- EAI_MAX
-
Максимальный код ошибки getaddrinfo
- EAI_MEMORY
-
Ошибка выделения памяти
- EAI_NODATA
-
С именем хоста не связан ни один адрес
- EAI_NONAME
-
Имя хоста или службы не указано либо неизвестно
- EAI_OVERFLOW
-
Переполнение буфера аргумента
- EAI_PROTOCOL
-
Разрешённый протокол неизвестен
- EAI_SERVICE
-
Служба не поддерживается для данного типа сокета
- EAI_SOCKTYPE
-
Тип сокета не поддерживается
- EAI_SYSTEM
-
Системная ошибка, возвращённая в errno
- IFF_802_1Q_VLAN
-
Устройство VLAN 802.1Q
- IFF_ALLMULTI
-
принимать все пакеты многоадресной рассылки
- IFF_ALTPHYS
-
использовать альтернативное физическое соединение
- IFF_AUTOMEDIA
-
автоматический выбор активного типа среды
- IFF_BONDING
-
ведущее или ведомое устройство bonding
- IFF_BRIDGE_PORT
-
устройство используется как порт моста
- IFF_BROADCAST
-
широковещательный адрес действителен
- IFF_CANTCHANGE
-
флаги нельзя изменить
- IFF_CANTCONFIG
-
нельзя настроить с помощью ioctl(2)
- IFF_DEBUG
-
включить отладку
- IFF_DISABLE_NETPOLL
-
отключить netpoll во время выполнения
- IFF_DONT_BRIDGE
-
запретить создание моста для этого Ethernet-устройства
- IFF_DORMANT
-
драйвер сообщает о состоянии ожидания
- IFF_DRV_OACTIVE
-
аппаратная очередь передачи заполнена
- IFF_DRV_RUNNING
-
ресурсы выделены
- IFF_DYING
-
интерфейс отключается
- IFF_DYNAMIC
-
коммутируемое устройство с изменяющимися адресами
- IFF_EBRIDGE
-
устройство Ethernet-моста
- IFF_ECHO
-
отправлять эхо-копии переданных пакетов
- IFF_ISATAP
-
интерфейс ISATAP (RFC4214)
- IFF_LINK0
-
бит 0, определяемый канальным уровнем
- IFF_LINK1
-
бит 1, определяемый канальным уровнем
- IFF_LINK2
-
бит 2, определяемый канальным уровнем
- IFF_LIVE_ADDR_CHANGE
-
изменение аппаратного адреса во время работы
- IFF_LOOPBACK
-
петлевой сетевой интерфейс
- IFF_LOWER_UP
-
драйвер сообщает об активности физического уровня (L1)
- IFF_MACVLAN_PORT
-
устройство используется как порт macvlan
- IFF_MASTER
-
ведущее устройство балансировщика нагрузки
- IFF_MASTER_8023AD
-
ведущее устройство bonding, 802.3ad.
- IFF_MASTER_ALB
-
ведущее устройство bonding, balance-alb.
- IFF_MASTER_ARPMON
-
ведущее устройство bonding, используется мониторинг ARP
- IFF_MONITOR
-
режим мониторинга, запрошенный пользователем
- IFF_MULTICAST
-
поддерживает многоадресную рассылку
- IFF_NOARP
-
без протокола разрешения адресов
- IFF_NOTRAILERS
-
не использовать трейлеры
- IFF_OACTIVE
-
идёт передача
- IFF_OVS_DATAPATH
-
устройство используется как порт datapath Open vSwitch
- IFF_POINTOPOINT
-
соединение точка-точка
- IFF_PORTSEL
-
можно задать тип среды передачи
- IFF_PPROMISC
-
неразборчивый режим, запрошенный пользователем
- IFF_PROMISC
-
принимать все пакеты
- IFF_RENAMING
-
имя интерфейса изменяется
- IFF_ROUTE
-
маршрут установлен
- IFF_RUNNING
-
ресурсы выделены
- IFF_SIMPLEX
-
нельзя принимать собственные передачи
- IFF_SLAVE
-
ведомое устройство балансировщика нагрузки
- IFF_SLAVE_INACTIVE
-
ведомое устройство bonding не является текущим активным
- IFF_SLAVE_NEEDARP
-
для проверки требуются запросы ARP
- IFF_SMART
-
интерфейс управляет собственными маршрутами
- IFF_STATICARP
-
статический ARP
- IFF_SUPP_NOFCS
-
отправка пользовательского FCS
- IFF_TEAM_PORT
-
используется как порт team
- IFF_TX_SKB_SHARING
-
совместное использование skb при передаче
- IFF_UNICAST_FLT
-
фильтрация одноадресного трафика
- IFF_UP
-
интерфейс активен
- IFF_VOLATILE
-
изменчивые флаги
- IFF_WAN_HDLC
-
устройство WAN HDLC
- IFF_XMIT_DST_RELEASE
-
dev_hard_start_xmit() может освобождать skb->dst
- IFNAMSIZ
-
Максимальная длина имени интерфейса
- IF_NAMESIZE
-
Максимальная длина имени интерфейса
- INADDR_ALLHOSTS_GROUP
-
Группа многоадресной рассылки для всех систем этой подсети
- INADDR_ANY
-
Сокет, привязанный к
INADDR_ANY, получает пакеты со всех интерфейсов и отправляет их с IP-адреса по умолчанию - INADDR_BROADCAST
-
Широковещательный адрес сети
- INADDR_LOOPBACK
-
Адрес петлевого интерфейса
- INADDR_MAX_LOCAL_GROUP
-
Последняя группа локальной сети для многоадресной рассылки
- INADDR_NONE
-
Битовая маска для сопоставления с любым недопустимым IP-адресом
- INADDR_UNSPEC_GROUP
-
Зарезервированная группа многоадресной рассылки
- INET6_ADDRSTRLEN
-
Максимальная длина строкового представления адреса IPv6
- INET_ADDRSTRLEN
-
Максимальная длина строкового представления адреса IPv4
- IPPORT_RESERVED
-
Минимальный адрес по умолчанию для bind или connect
- IPPORT_USERRESERVED
-
Максимальный адрес по умолчанию для bind или connect
- IPPROTO_AH
-
Заголовок аутентификации IP6
- IPPROTO_BIP
- IPPROTO_DSTOPTS
-
Параметр назначения IP6
- IPPROTO_EGP
-
Протокол внешнего шлюза
- IPPROTO_EON
-
ISO CNLP
- IPPROTO_ESP
-
Инкапсулированная полезная нагрузка безопасности IP6
- IPPROTO_FRAGMENT
-
Заголовок фрагментации IP6
- IPPROTO_GGP
-
Протокол взаимодействия шлюзов
- IPPROTO_HELLO
-
Протокол маршрутизации «hello»
- IPPROTO_HOPOPTS
-
Параметры IP6 для каждого перехода
- IPPROTO_ICMP
-
Протокол управляющих сообщений
- IPPROTO_ICMPV6
-
ICMP6
- IPPROTO_IDP
-
XNS IDP
- IPPROTO_IGMP
-
Протокол управления группами
- IPPROTO_IP
-
Вспомогательный протокол для IP
- IPPROTO_IPV6
-
Заголовок IP6
- IPPROTO_MAX
-
Максимальное значение константы IPPROTO
- IPPROTO_ND
-
Сетевой дисковый протокол Sun
- IPPROTO_NONE
-
В IP6 нет следующего заголовка
- IPPROTO_PUP
-
Протокол PARC Universal Packet
- IPPROTO_RAW
-
Необработанный IP-пакет
- IPPROTO_ROUTING
-
Заголовок маршрутизации IP6
- IPPROTO_TCP
-
TCP
- IPPROTO_TP
-
Транспортный протокол ISO класса 4
- IPPROTO_UDP
-
UDP
- IPPROTO_XTP
-
Протокол передачи Xpress
- IPV6_CHECKSUM
-
Смещение контрольной суммы для необработанных сокетов
- IPV6_DONTFRAG
-
Не фрагментировать пакеты
- IPV6_DSTOPTS
-
Параметр назначения
- IPV6_HOPLIMIT
-
Ограничение числа переходов
- IPV6_HOPOPTS
-
Параметр для каждого перехода
- IPV6_JOIN_GROUP
-
Вступить в группу
- IPV6_LEAVE_GROUP
-
Покинуть группу
- IPV6_MTU_DISCOVER
-
Определение MTU пути
- IPV6_MULTICAST_HOPS
-
Число переходов многоадресной рассылки IP6
- IPV6_MULTICAST_IF
-
Интерфейс многоадресной рассылки IP6
- IPV6_MULTICAST_LOOP
-
Петлевая передача многоадресной рассылки IP6
- IPV6_NEXTHOP
-
Адрес следующего узла
- IPV6_PATHMTU
-
Получить текущее значение MTU пути
- IPV6_PKTINFO
-
Получать сведения о пакете вместе с дейтаграммой
- IPV6_RECVDSTOPTS
-
Получать все параметры IP6 для ответа
- IPV6_RECVERR
-
Включить передачу расширенных надёжных сообщений об ошибках
- IPV6_RECVHOPLIMIT
-
Получать ограничение числа переходов вместе с дейтаграммой
- IPV6_RECVHOPOPTS
-
Получать параметры для каждого перехода
- IPV6_RECVPATHMTU
-
Получать текущее значение MTU пути вместе с дейтаграммой
- IPV6_RECVPKTINFO
-
Получать IP-адрес назначения и входящий интерфейс
- IPV6_RECVRTHDR
-
Получать заголовок маршрутизации
- IPV6_RECVTCLASS
-
Получать класс трафика
- IPV6_RTHDR
-
Разрешает удаление закреплённых заголовков маршрутизации
- IPV6_RTHDRDSTOPTS
-
Разрешает удаление закреплённого заголовка параметров назначения
- IPV6_RTHDR_TYPE_0
-
Тип заголовка маршрутизации 0
- IPV6_TCLASS
-
Задать класс трафика
- IPV6_UNICAST_HOPS
-
Число переходов одноадресной передачи IP6
- IPV6_USE_MIN_MTU
-
Использовать минимальный размер MTU
- IPV6_V6ONLY
-
При привязке к любому адресу использовать только IPv6
- IPX_TYPE
- IP_ADD_MEMBERSHIP
-
Вступить в группу многоадресной рассылки
- IP_ADD_SOURCE_MEMBERSHIP
-
Вступить в группу многоадресной рассылки
- IP_BLOCK_SOURCE
-
Блокировать пакеты многоадресной рассылки IPv4 с указанным адресом источника
- IP_DEFAULT_MULTICAST_LOOP
-
Петлевая передача многоадресной рассылки по умолчанию
- IP_DEFAULT_MULTICAST_TTL
-
TTL многоадресной рассылки по умолчанию
- IP_DONTFRAG
-
Не фрагментировать пакеты
- IP_DROP_MEMBERSHIP
-
Покинуть группу многоадресной рассылки
- IP_DROP_SOURCE_MEMBERSHIP
-
Покинуть группу многоадресной рассылки
- IP_FREEBIND
-
Разрешить привязку к несуществующим IP-адресам
- IP_HDRINCL
-
Заголовок включён в данные
- IP_IPSEC_POLICY
-
Политика безопасности IPsec
- IP_MAX_MEMBERSHIPS
-
Максимальное число групп многоадресной рассылки, в которые может вступить сокет
- IP_MINTTL
-
Минимальное допустимое значение TTL для получаемых пакетов
- IP_MSFILTER
-
Фильтрация источников многоадресной рассылки
- IP_MTU
-
Максимальный размер передаваемого блока данных сокета
- IP_MTU_DISCOVER
-
Определение MTU пути
- IP_MULTICAST_IF
-
Интерфейс многоадресной рассылки IP
- IP_MULTICAST_LOOP
-
Петлевая передача многоадресной рассылки IP
- IP_MULTICAST_TTL
-
TTL многоадресной рассылки IP
- IP_ONESBCAST
-
Принудительно задавать исходящим широковещательным дейтаграммам адрес направленной широковещательной рассылки
- IP_OPTIONS
-
Параметры IP, включаемые в пакеты
- IP_PASSSEC
-
Получать контекст безопасности вместе с дейтаграммой
- IP_PKTINFO
-
Получать сведения о пакете вместе с дейтаграммами
- IP_PKTOPTIONS
-
Получать параметры пакетов вместе с дейтаграммами
- IP_PMTUDISC_DO
-
Всегда отправлять кадры с установленным DF
- IP_PMTUDISC_DONT
-
Никогда не отправлять кадры с установленным DF
- IP_PMTUDISC_WANT
-
Использовать подсказки для каждого маршрута
- IP_PORTRANGE
-
Задать диапазон портов для сокетов с неуказанными номерами портов
- IP_RECVDSTADDR
-
Получать IP-адрес назначения вместе с дейтаграммой
- IP_RECVERR
-
Включить передачу расширенных надёжных сообщений об ошибках
- IP_RECVIF
-
Получать сведения об интерфейсе вместе с дейтаграммами
- IP_RECVOPTS
-
Получать все параметры IP вместе с дейтаграммой
- IP_RECVRETOPTS
-
Получать все параметры IP для ответа
- IP_RECVSLLA
-
Получать адрес канального уровня вместе с дейтаграммами
- IP_RECVTOS
-
Получать TOS во входящих пакетах
- IP_RECVTTL
-
Получать TTL IP вместе с дейтаграммами
- IP_RETOPTS
-
Параметры IP, включаемые в дейтаграммы
- IP_ROUTER_ALERT
-
Уведомлять транзитные маршрутизаторы о необходимости более тщательно изучить содержимое IP-пакета
- IP_SENDSRCADDR
-
Адрес источника для исходящих дейтаграмм UDP
- IP_TOS
-
Тип обслуживания IP
- IP_TRANSPARENT
-
Прозрачный прокси-сервер
- IP_TTL
-
Время жизни IP
- IP_UNBLOCK_SOURCE
-
Разблокировать пакеты многоадресной рассылки IPv4 с указанным адресом источника
- IP_XFRM_POLICY
- LOCAL_CONNWAIT
-
Вызов connect блокируется до принятия соединения
- LOCAL_CREDS
-
Передавать учётные данные получателю
- LOCAL_PEERCRED
-
Получить учётные данные узла-партнёра
- MCAST_BLOCK_SOURCE
-
Блокировать пакеты многоадресной рассылки от этого источника
- MCAST_EXCLUDE
-
Исключающий фильтр источников многоадресной рассылки
- MCAST_INCLUDE
-
Включающий фильтр источников многоадресной рассылки
- MCAST_JOIN_GROUP
-
Вступить в группу многоадресной рассылки
- MCAST_JOIN_SOURCE_GROUP
-
Вступить в группу источников многоадресной рассылки
- MCAST_LEAVE_GROUP
-
Покинуть группу многоадресной рассылки
- MCAST_LEAVE_SOURCE_GROUP
-
Покинуть группу источников многоадресной рассылки
- MCAST_MSFILTER
-
Фильтрация источников многоадресной рассылки
- MCAST_UNBLOCK_SOURCE
-
Разблокировать пакеты многоадресной рассылки от этого источника
- MSG_COMPAT
-
Конец записи
- MSG_CONFIRM
-
Подтвердить действительность маршрута
- MSG_CTRUNC
-
Управляющие данные потеряны до доставки
- MSG_DONTROUTE
-
Отправить, не используя таблицы маршрутизации
- MSG_DONTWAIT
-
Это сообщение должно обрабатываться в неблокирующем режиме
- MSG_EOF
-
Данные завершают соединение
- MSG_EOR
-
Данные завершают запись
- MSG_ERRQUEUE
-
Извлечь сообщение из очереди ошибок
- MSG_FASTOPEN
-
Сократить этап процесса установления соединения
- MSG_FIN
- MSG_FLUSH
-
Начало последовательности удержания. Сбрасывает данные в so_temp
- MSG_HAVEMORE
-
Данные готовы к чтению
- MSG_HOLD
-
Удерживать фрагмент в so_temp
- MSG_MORE
-
Отправитель отправит ещё данные
- MSG_NOSIGNAL
-
Не генерировать SIGPIPE
- MSG_OOB
-
Обработать внеполосные данные
- MSG_PEEK
-
Просмотреть входящее сообщение
- MSG_PROXY
-
Ожидать полного запроса
- MSG_RCVMORE
-
В текущем пакете остались данные
- MSG_RST
- MSG_SEND
-
Отправить пакет из so_temp
- MSG_SYN
- MSG_TRUNC
-
Данные отброшены до доставки
- MSG_WAITALL
-
Ожидать полного запроса или ошибки
- NI_DGRAM
-
Указана служба дейтаграмм (выполняется поиск портов UDP)
- NI_MAXHOST
-
Максимальная длина имени хоста
- NI_MAXSERV
-
Максимальная длина имени службы
- NI_NAMEREQD
-
Требуется имя
- NI_NOFQDN
-
Для локальных хостов полное доменное имя не требуется; возвращается только локальная часть
- NI_NUMERICHOST
-
Вернуть числовой адрес
- NI_NUMERICSERV
-
Вернуть имя службы в виде строки цифр
- PF_ALG
-
Интерфейс к криптографическому API ядра
- PF_APPLETALK
-
Протокол AppleTalk
- PF_ATM
-
Режим асинхронной передачи
- PF_AX25
-
Протокол AX.25
- PF_BLUETOOTH
-
Низкоуровневый протокол сокетов Bluetooth
- PF_CAN
-
Протокол автомобильной шины Controller Area Network
- PF_CCITT
-
Протоколы CCITT (ныне ITU-T)
- PF_CHAOS
-
Протоколы MIT CHAOS
- PF_CNT
-
Технология компьютерных сетей
- PF_COIP
-
IP с установлением соединения
- PF_DATAKIT
-
Протокол Datakit
- PF_DEC
-
Протокол DECnet
- PF_DECnet
-
Протокол DECnet
- PF_DLI
-
Протокол интерфейса прямой передачи данных DEC
- PF_ECMA
-
Протоколы Европейской ассоциации производителей компьютеров
- PF_HYLINK
-
Протокол NSC Hyperchannel
- PF_IB
-
Нативная адресация InfiniBand
- PF_IMPLINK
-
Протокол ARPANET IMP
- PF_INET
-
Протокол IPv4
- PF_INET6
-
Протокол IPv6
- PF_IPX
-
Протокол IPX
- PF_ISDN
-
Цифровая сеть с интеграцией служб
- PF_ISO
-
Протоколы взаимодействия открытых систем ISO
- PF_KCM
-
Интерфейс KCM (мультиплексор соединений ядра)
- PF_KEY
-
Протокол управления ключами, первоначально разработанный для использования с IPsec
- PF_LAT
-
Протокол Local Area Transport
- PF_LINK
-
Интерфейс канального уровня
- PF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- PF_LOCAL
-
Внутренние протоколы хоста
- PF_MAX
-
Максимальное семейство адресов для этой платформы
- PF_MPLS
-
Многопротокольная коммутация по меткам
- PF_NATM
-
Прямой доступ к ATM
- PF_NDRV
-
Прямой доступ к сетевому драйверу
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Сокеты Netgraph
- PF_NETLINK
-
Устройство интерфейса ядра с пользовательским пространством
- PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы взаимодействия открытых систем ISO
- PF_PACKET
-
Прямой доступ к канальному уровню
- PF_PIP
-
Помогает идентифицировать пакеты PIP
- PF_PPP
-
Протокол точка-точка
- PF_PPPOX
-
Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)
- PF_PUP
-
Протокол PARC Universal Packet
- PF_RDS
-
Протокол Reliable Datagram Sockets (RDS)
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_RTIP
-
Помогает идентифицировать пакеты RTIP
- PF_SIP
-
Простой интернет-протокол
- PF_SNA
-
Протокол IBM SNA
- PF_SYSTEM
-
Сообщения о событиях ядра
- PF_TIPC
-
Протокол TIPC («сокеты домена кластера»)
- PF_UNIX
-
Сокеты UNIX
- PF_UNSPEC
-
Неуказанный протокол, любое поддерживаемое семейство адресов
- PF_VSOCK
-
Протокол VSOCK (первоначально «VMWare VSockets») для связи гипервизора с гостевой системой
- PF_XDP
-
Интерфейс XDP (express data path)
- PF_XTP
-
Протокол eXpress Transfer
- SCM_BINTIME
-
Временная метка (bintime)
- SCM_CREDENTIALS
-
Учётные данные отправителя
- SCM_CREDS
-
Учётные данные процесса
- SCM_RIGHTS
-
Права доступа
- SCM_TIMESTAMP
-
Временная метка (timeval)
- SCM_TIMESTAMPING
-
Временная метка (список timespec) (Linux 2.6.30)
- SCM_TIMESTAMPNS
-
Timespec (timespec)
- SCM_UCRED
-
Учётные данные пользователя
- SCM_WIFI_STATUS
-
Состояние Wi-Fi (Linux 3.3)
- SHUT_RD
-
Закрыть сторону сокета для чтения
- SHUT_RDWR
-
Закрыть обе стороны сокета
- SHUT_WR
-
Закрыть сторону сокета для записи
- SOCK_CLOEXEC
-
Установить флаг close-on-exec (FD_CLOEXEC) для нового файлового дескриптора.
- SOCK_DGRAM
-
Дейтаграммный сокет обеспечивает обмен сообщениями без установления соединения и без гарантии доставки
- SOCK_NONBLOCK
-
Установить флаг состояния файла O_NONBLOCK для описания открытого файла (см. open(2)), на которое ссылается новый файловый дескриптор.
- SOCK_PACKET
-
Доступ к пакетам на уровне устройства
- SOCK_RAW
-
Необработанный сокет обеспечивает низкоуровневый доступ для прямого взаимодействия или реализации сетевых протоколов
- SOCK_RDM
-
Надёжный дейтаграммный сокет обеспечивает надёжную доставку сообщений
- SOCK_SEQPACKET
-
Сокет последовательных пакетов обеспечивает упорядоченное, надежное двустороннее соединение для датаграмм
- SOCK_STREAM
-
Потоковый сокет обеспечивает упорядоченное, надежное двустороннее соединение для потока байтов
- SOL_ATALK
-
Параметры сокета AppleTalk
- SOL_AX25
-
Параметры сокета AX.25
- SOL_IP
-
Параметры сокета IP
- SOL_IPX
-
Параметры сокета IPX
- SOL_SOCKET
-
Параметры уровня сокета
- SOL_TCP
-
Параметры сокета TCP
- SOL_UDP
-
Параметры сокета UDP
- SOMAXCONN
-
Максимальное число запросов на подключение, которые могут находиться в очереди сокета
- SOPRI_BACKGROUND
-
Фоновый приоритет сокета
- SOPRI_INTERACTIVE
-
Интерактивный приоритет сокета
- SOPRI_NORMAL
-
Обычный приоритет сокета
- SO_ACCEPTCONN
-
Для сокета был вызван listen()
- SO_ACCEPTFILTER
-
Установлен фильтр приема соединений
- SO_ALLZONES
-
Обход ограничений зон
- SO_ATTACH_FILTER
-
Подключить фильтр приема соединений
- SO_BINDTODEVICE
-
Отправлять пакеты только через указанный интерфейс
- SO_BINTIME
-
Получать временную метку с датаграммами (bintime)
- SO_BPF_EXTENSIONS
-
Запрос поддерживаемых расширений BPF (Linux 3.14)
- SO_BROADCAST
-
Разрешить отправку широковещательных сообщений
- SO_BUSY_POLL
-
Задать порог в микросекундах для опроса с низкой задержкой (Linux 3.11)
- SO_CONNECT_TIME
-
Возвращает время в секундах, в течение которого сокет был подключен. Этот параметр применим только к протоколам с установлением соединения (Windows)
- SO_DEBUG
-
Запись отладочной информации
- SO_DETACH_FILTER
-
Отключить фильтр приема соединений
- SO_DOMAIN
-
Домен, указанный для socket() (Linux 2.6.32)
- SO_DONTROUTE
-
Использовать адреса интерфейсов
- SO_DONTTRUNC
-
Сохранять непрочитанные данные
- SO_ERROR
-
Получить и сбросить состояние ошибки
- SO_GET_FILTER
-
Получить фильтр, установленный с помощью
SO_ATTACH_FILTER(Linux 3.8) - SO_INCOMING_CPU
-
Получать идентификатор процессора, связанного с сокетом (Linux 3.19)
- SO_INCOMING_NAPI_ID
-
Получать идентификатор NAPI, связанный с очередью приема (Linux 4.12)
- SO_KEEPALIVE
-
Поддерживать соединения активными
- SO_LINGER
-
Ожидать при закрытии, если имеются данные
- SO_LOCK_FILTER
-
Блокировать фильтр, подключенный к сокету (Linux 3.9)
- SO_MAC_EXEMPT
-
Освобождение от обязательного контроля доступа для узлов без меток
- SO_MARK
-
Задать метку для маршрутизации на основе меток (Linux 2.6.25)
- SO_MAX_PACING_RATE
-
Ограничить скорость, рассчитанную транспортным уровнем. [байт в секунду] (Linux 3.13)
- SO_NKE
-
Установить расширение сетевого ядра на уровне сокета
- SO_NOFCS
-
Задать сетевое пространство имен сокета (Linux 3.4)
- SO_NOSIGPIPE
-
Не отправлять SIGPIPE при EPIPE
- SO_NO_CHECK
-
Отключить контрольные суммы
- SO_NREAD
-
Получить число байтов в первом пакете
- SO_OOBINLINE
-
Оставлять полученные внеполосные данные во встроенном потоке
- SO_PASSCRED
-
Получать сообщения
SCM_CREDENTIALS - SO_PASSSEC
-
Включить или отключить передачу контекста безопасности (Linux 2.6.18)
- SO_PEEK_OFF
-
Задать смещение просмотра (Linux 3.4)
- SO_PEERCRED
-
Учетные данные удаленного процесса, подключенного к этому сокету
- SO_PEERNAME
-
Имя подключающегося пользователя
- SO_PEERSEC
-
Получить учетные данные безопасности (Linux 2.6.2)
- SO_PRIORITY
-
Приоритет, определенный протоколом, для всех пакетов этого сокета
- SO_PROTOCOL
-
Протокол, указанный для socket() (Linux 2.6.32)
- SO_RCVBUF
-
Размер буфера приема
- SO_RCVBUFFORCE
-
Размер буфера приема без ограничения rmem_max (Linux 2.6.14)
- SO_RCVLOWAT
-
Нижний порог для приема
- SO_RCVTIMEO
-
Тайм-аут приема
- SO_RECVUCRED
-
Получать учетные данные пользователя вместе с датаграммой
- SO_REUSEADDR
-
Разрешить повторное использование локального адреса
- SO_REUSEPORT
-
Разрешить повторное использование локального адреса и порта
- SO_RTABLE
-
Задать таблицу маршрутизации для этого сокета (OpenBSD)
- SO_RXQ_OVFL
-
Включить или отключить cmsg с числом отброшенных пакетов (Linux 2.6.33)
- SO_SECURITY_AUTHENTICATION
- SO_SECURITY_ENCRYPTION_NETWORK
- SO_SECURITY_ENCRYPTION_TRANSPORT
- SO_SELECT_ERR_QUEUE
-
Позволить select() обнаруживать очередь ошибок сокета с помощью errorfds (Linux 3.10)
- SO_SETFIB
-
Задать связанную с сокетом таблицу маршрутизации (FreeBSD)
- SO_SNDBUF
-
Размер буфера отправки
- SO_SNDBUFFORCE
-
Размер буфера отправки без ограничения wmem_max (Linux 2.6.14)
- SO_SNDLOWAT
-
Нижний порог для отправки
- SO_SNDTIMEO
-
Тайм-аут отправки
- SO_TIMESTAMP
-
Получать временную метку с датаграммами (timeval)
- SO_TIMESTAMPING
-
Установка временных меток для входящих и исходящих пакетов (Linux 2.6.30)
- SO_TIMESTAMPNS
-
Получать временную метку с точностью до наносекунд вместе с датаграммами (timespec)
- SO_TYPE
-
Получить тип сокета
- SO_USELOOPBACK
-
По возможности обходить аппаратное обеспечение
- SO_USER_COOKIE
-
Задать идентификатор, главным образом для ipfw
- SO_WANTMORE
-
Подать сигнал о готовности дополнительных данных
- SO_WANTOOBFLAG
-
При приеме внеполосные данные требуются в MSG_FLAG
- SO_WIFI_STATUS
-
Включить или отключить cmsg со статусом Wi-Fi (Linux 3.3)
- TCP_CONGESTION
-
Алгоритм управления перегрузкой TCP (Linux 2.6.13, glibc 2.6)
- TCP_CONNECTION_INFO
-
Получить сведения об этом сокете (macOS)
- TCP_COOKIE_TRANSACTIONS
-
Транзакции TCP Cookie (Linux 2.6.33, glibc 2.18)
- TCP_CORK
-
Не отправлять неполные кадры (Linux 2.2, glibc 2.2)
- TCP_DEFER_ACCEPT
-
Не уведомлять слушающий сокет, пока данные не будут готовы (Linux 2.4, glibc 2.2)
- TCP_FASTOPEN
-
Сократить этап процесса установления соединения (Linux 3.7, glibc 2.18)
- TCP_INFO
-
Получить сведения об этом сокете (Linux 2.4, glibc 2.2)
- TCP_KEEPALIVE
-
Время простоя перед отправкой проверочных пакетов keepalive (macOS)
- TCP_KEEPCNT
-
Максимальное число проверочных пакетов keepalive перед разрывом соединения (Linux 2.4, glibc 2.2)
- TCP_KEEPIDLE
-
Время простоя перед отправкой проверочных пакетов keepalive (Linux 2.4, glibc 2.2)
- TCP_KEEPINTVL
-
Интервал между проверочными пакетами keepalive (Linux 2.4, glibc 2.2)
- TCP_LINGER2
-
Время жизни осиротевших сокетов в состоянии FIN_WAIT2 (Linux 2.4, glibc 2.2)
- TCP_MAXSEG
-
Задать максимальный размер сегмента
- TCP_MD5SIG
-
Использовать дайджесты MD5 (RFC2385, Linux 2.6.20, glibc 2.7)
- TCP_NODELAY
-
Не задерживать отправку для объединения пакетов
- TCP_NOOPT
-
Не использовать параметры TCP
- TCP_NOPUSH
-
Не отправлять последний блок записи
- TCP_QUEUE_SEQ
-
Номер последовательности очереди для режима восстановления (Linux 3.5, glibc 2.18)
- TCP_QUICKACK
-
Включить режим быстрой отправки подтверждений (Linux 2.4.4, glibc 2.3)
- TCP_REPAIR
-
Режим восстановления (Linux 3.5, glibc 2.18)
- TCP_REPAIR_OPTIONS
-
Параметры режима восстановления (Linux 3.5, glibc 2.18)
- TCP_REPAIR_QUEUE
-
Очередь для режима восстановления (Linux 3.5, glibc 2.18)
- TCP_SYNCNT
-
Число повторных отправок SYN до разрыва соединения (Linux 2.4, glibc 2.2)
- TCP_THIN_DUPACK
-
Обработка дублирующихся подтверждений для тонких потоков (Linux 2.6.34, glibc 2.18)
- TCP_THIN_LINEAR_TIMEOUTS
-
Линейные тайм-ауты для тонких потоков (Linux 2.6.34, glibc 2.18)
- TCP_TIMESTAMP
-
Временная метка TCP (Linux 3.9, glibc 2.18)
- TCP_USER_TIMEOUT
-
Максимальный тайм-аут до прерывания TCP-соединения (Linux 2.6.37, glibc 2.18)
- TCP_WINDOW_CLAMP
-
Ограничить размер объявляемого окна (Linux 2.4, glibc 2.2)
- UDP_CORK
-
Не отправлять неполные кадры (Linux 2.5.44, glibc 2.11)
Публичные методы класса
# File ext/socket/lib/socket.rb, line 1258
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
sockets.flatten!(1)
if sockets.empty?
raise ArgumentError, "no sockets"
end
loop {
readable, _, _ = IO.select(sockets)
readable.each {|r|
sock, addr = r.accept_nonblock(exception: false)
next if sock == :wait_readable
yield sock, addr
}
}
end Для каждого подключения, принятого через указанные сокеты, передаёт в блок сокет и адрес клиента.
Аргументы — это список сокетов. Каждый отдельный аргумент должен быть сокетом или массивом сокетов.
Этот метод последовательно вызывает блок. Это означает, что следующее подключение не будет принято, пока блок не завершит работу. Поэтому для обслуживания нескольких клиентов одновременно следует использовать механизм параллельной обработки, например потоки.
static VALUE
sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _)
{
VALUE host, port, family, socktype, protocol, flags, ret, revlookup;
struct addrinfo hints;
struct rb_addrinfo *res;
int norevlookup;
rb_scan_args(argc, argv, "25", &host, &port, &family, &socktype, &protocol, &flags, &revlookup);
MEMZERO(&hints, struct addrinfo, 1);
hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family);
if (!NIL_P(socktype)) {
hints.ai_socktype = rsock_socktype_arg(socktype);
}
if (!NIL_P(protocol)) {
hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
hints.ai_flags = NUM2INT(flags);
}
if (NIL_P(revlookup) || !rsock_revlookup_flag(revlookup, &norevlookup)) {
norevlookup = rsock_do_not_reverse_lookup;
}
res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
ret = make_addrinfo(res, norevlookup);
rb_freeaddrinfo(res);
return ret;
} Получает сведения об адресе для nodename:servname.
Обратите внимание, что Addrinfo.getaddrinfo предоставляет те же возможности в объектно-ориентированном стиле.
family должно быть семейством адресов, например :INET, :INET6 и т. д.
socktype должен быть типом сокета, например :STREAM, :DGRAM, :RAW и т. д.
protocol должен быть протоколом, определённым для этого семейства; по умолчанию используется 0 для данного семейства.
flags должно быть побитовым ИЛИ констант Socket::AI_*.
Socket.getaddrinfo("www.ruby-lang.org", "http", nil, :STREAM)
#=> [["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68", 2, 1, 6]] # PF_INET/SOCK_STREAM/IPPROTO_TCP
Socket.getaddrinfo("localhost", nil)
#=> [["AF_INET", 0, "localhost", "127.0.0.1", 2, 1, 6], # PF_INET/SOCK_STREAM/IPPROTO_TCP
# ["AF_INET", 0, "localhost", "127.0.0.1", 2, 2, 17], # PF_INET/SOCK_DGRAM/IPPROTO_UDP
# ["AF_INET", 0, "localhost", "127.0.0.1", 2, 3, 0]] # PF_INET/SOCK_RAW/IPPROTO_IP
reverse_lookup определяет формат третьего элемента и должен иметь одно из следующих значений. Если reverse_lookup не указан, используется значение по умолчанию nil.
+true+, +:hostname+: hostname is obtained from numeric address using reverse lookup, which may take a time. +false+, +:numeric+: hostname is the same as numeric address. +nil+: obey to the current +do_not_reverse_lookup+ flag.
Если предпочтителен объект Addrinfo, используйте Addrinfo.getaddrinfo.
static VALUE
sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
{
VALUE addr, family;
struct hostent *h;
char **pch;
VALUE ary, names;
int t = AF_INET;
rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead.");
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
t = AF_INET6;
}
#endif
h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
extern int h_errno;
rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
rb_ary_push(ary, rb_str_new2(h->h_name));
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
for (pch = h->h_aliases; *pch; pch++) {
rb_ary_push(names, rb_str_new2(*pch));
}
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
#endif
return ary;
} Вместо этого используйте Addrinfo#getnameinfo. Этот метод считается устаревшим по следующим причинам:
-
Необычный формат представления адреса: для представления адреса IPv4/IPv6 используется двоичная строка длиной 4/16 байт.
-
Выполнение gethostbyaddr() может занять много времени и заблокировать другие потоки. (GVL нельзя освободить, поскольку gethostbyname() не является потокобезопасной.)
-
Этот метод использует функцию gethostbyname(), уже удалённую из POSIX.
Этот метод получает сведения о хосте для address.
p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
#=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
["localhost", [], 2, "\x7F\x00\x00\x01"]
p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
#=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
struct rb_addrinfo *res =
rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil);
return rsock_make_hostent(host, res, sock_sockaddr);
} Вместо этого используйте Addrinfo.getaddrinfo. Этот метод считается устаревшим по следующим причинам:
-
Третий элемент результата — это семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Необычный формат представления адреса: для представления адреса IPv4/IPv6 используется двоичная строка длиной 4/16 байт.
-
Выполнение gethostbyname() может занять много времени и заблокировать другие потоки. (GVL нельзя освободить, поскольку gethostbyname() не является потокобезопасной.)
-
Этот метод использует функцию gethostbyname(), уже удалённую из POSIX.
Этот метод получает сведения о хосте для hostname.
p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
static VALUE
sock_gethostname(VALUE obj)
{
#if defined(NI_MAXHOST)
# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST
#elif defined(HOST_NAME_MAX)
# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX
#else
# define RUBY_MAX_HOST_NAME_LEN 1024
#endif
long len = RUBY_MAX_HOST_NAME_LEN;
VALUE name;
name = rb_str_new(0, len);
while (gethostname(RSTRING_PTR(name), len) < 0) {
int e = errno;
switch (e) {
case ENAMETOOLONG:
#ifdef __linux__
case EINVAL:
/* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
#endif
break;
default:
rb_syserr_fail(e, "gethostname(3)");
}
rb_str_modify_expand(name, len);
len += len;
}
rb_str_resize(name, strlen(RSTRING_PTR(name)));
return name;
} Возвращает имя хоста.
p Socket.gethostname #=> "hal"
Обратите внимание, что преобразование этого имени в IP-адрес с помощью gethostbyname, getaddrinfo и т. д. не гарантируется. Если вам нужен локальный IP-адрес, используйте Socket.ip_address_list.
static VALUE
socket_s_getifaddrs(VALUE self)
{
return rsock_getifaddrs();
} Возвращает массив адресов интерфейсов. Элемент массива является экземпляром Socket::Ifaddr.
Этот метод можно использовать для поиска интерфейсов с поддержкой многоадресной рассылки:
pp Socket.getifaddrs.reject {|ifaddr|
!ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
}.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
#=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
# ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
Пример результата в GNU/Linux:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
Пример результата во FreeBSD:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
static VALUE
sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
{
VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
char hbuf[1024], pbuf[1024];
int fl;
struct rb_addrinfo *res = NULL;
struct addrinfo hints, *r;
int error, saved_errno;
union_sockaddr ss;
struct sockaddr *sap;
socklen_t salen;
sa = flags = Qnil;
rb_scan_args(argc, argv, "11", &sa, &flags);
fl = 0;
if (!NIL_P(flags)) {
fl = NUM2INT(flags);
}
tmp = rb_check_sockaddr_string_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
rb_raise(rb_eTypeError, "sockaddr length too big");
}
memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
}
sap = &ss.addr;
salen = RSTRING_SOCKLEN(sa);
goto call_nameinfo;
}
tmp = rb_check_array_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
MEMZERO(&hints, struct addrinfo, 1);
if (RARRAY_LEN(sa) == 3) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 2);
}
else if (RARRAY_LEN(sa) >= 4) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 3);
if (NIL_P(host)) {
host = RARRAY_AREF(sa, 2);
}
else {
/*
* 4th element holds numeric form, don't resolve.
* see rsock_ipaddr().
*/
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
hints.ai_flags |= AI_NUMERICHOST;
#endif
}
}
else {
rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
RARRAY_LEN(sa));
}
hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
/* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
sap = res->ai->ai_addr;
salen = res->ai->ai_addrlen;
}
else {
rb_raise(rb_eTypeError, "expecting String or Array");
}
call_nameinfo:
error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf), fl);
if (error) goto error_exit_name;
if (res) {
for (r = res->ai->ai_next; r; r = r->ai_next) {
char hbuf2[1024], pbuf2[1024];
sap = r->ai_addr;
salen = r->ai_addrlen;
error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
pbuf2, sizeof(pbuf2), fl);
if (error) goto error_exit_name;
if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
rb_freeaddrinfo(res);
rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
}
}
rb_freeaddrinfo(res);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
error_exit_name:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_resolution_error("getnameinfo", error);
UNREACHABLE_RETURN(Qnil);
} Получает сведения об имени для sockaddr.
sockaddr должно иметь один из следующих форматов:
-
упакованная строка sockaddr, например
Socket.sockaddr_in(80, “127.0.0.1”) -
массив из 3 элементов, например [“AF_INET”, 80, “127.0.0.1”]
-
массив из 4 элементов, например [“AF_INET”, 80, ignored, “127.0.0.1”]
flags должно быть побитовым ИЛИ констант Socket::NI_*.
Примечание: последний формат совместим с IPSocket#addr и IPSocket#peeraddr.
Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"]) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"]
Если предпочтителен объект Addrinfo, используйте Addrinfo#getnameinfo.
static VALUE
sock_s_getservbyname(int argc, VALUE *argv, VALUE _)
{
VALUE service, proto;
struct servent *sp;
long port;
const char *servicename, *protoname = "tcp";
rb_scan_args(argc, argv, "11", &service, &proto);
StringValue(service);
if (!NIL_P(proto)) StringValue(proto);
servicename = StringValueCStr(service);
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyname(servicename, protoname);
if (sp) {
port = ntohs(sp->s_port);
}
else {
char *end;
port = STRTOUL(servicename, &end, 0);
if (*end != '\0') {
rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
}
}
return INT2FIX(port);
} Получает номер порта для service_name.
Если protocol_name не указан, предполагается значение “tcp”.
Socket.getservbyname("smtp") #=> 25
Socket.getservbyname("shell") #=> 514
Socket.getservbyname("syslog", "udp") #=> 514
static VALUE
sock_s_getservbyport(int argc, VALUE *argv, VALUE _)
{
VALUE port, proto;
struct servent *sp;
long portnum;
const char *protoname = "tcp";
rb_scan_args(argc, argv, "11", &port, &proto);
portnum = NUM2LONG(port);
if (portnum != (uint16_t)portnum) {
const char *s = portnum > 0 ? "big" : "small";
rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
}
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyport((int)htons((uint16_t)portnum), protoname);
if (!sp) {
rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
}
return rb_str_new2(sp->s_name);
} Получает номер порта для port.
Если protocol_name не указан, предполагается значение “tcp”.
Socket.getservbyport(80) #=> "www" Socket.getservbyport(514, "tcp") #=> "shell" Socket.getservbyport(514, "udp") #=> "syslog"
static VALUE
socket_s_ip_address_list(VALUE self)
{
#if defined(HAVE_GETIFADDRS)
struct ifaddrs *ifp = NULL;
struct ifaddrs *p;
int ret;
VALUE list;
ret = getifaddrs(&ifp);
if (ret == -1) {
rb_sys_fail("getifaddrs");
}
list = rb_ary_new();
for (p = ifp; p; p = p->ifa_next) {
if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) {
struct sockaddr *addr = p->ifa_addr;
#if defined(AF_INET6) && defined(__sun)
/*
* OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local
* address with sin6_scope_id == 0.
* So fill it from the interface name (ifa_name).
*/
struct sockaddr_in6 addr6;
if (addr->sa_family == AF_INET6) {
socklen_t len = (socklen_t)sizeof(struct sockaddr_in6);
memcpy(&addr6, addr, len);
addr = (struct sockaddr *)&addr6;
if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
addr6.sin6_scope_id == 0) {
unsigned int ifindex = if_nametoindex(p->ifa_name);
if (ifindex != 0) {
addr6.sin6_scope_id = ifindex;
}
}
}
#endif
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
}
freeifaddrs(ifp);
return list;
#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
/* Solaris if_tcp(7P) */
int fd = -1;
int ret;
struct lifnum ln;
struct lifconf lc;
const char *reason = NULL;
int save_errno;
int i;
VALUE list = Qnil;
lc.lifc_buf = NULL;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
memset(&ln, 0, sizeof(ln));
ln.lifn_family = AF_UNSPEC;
ret = ioctl(fd, SIOCGLIFNUM, &ln);
if (ret == -1) {
reason = "SIOCGLIFNUM";
goto finish;
}
memset(&lc, 0, sizeof(lc));
lc.lifc_family = AF_UNSPEC;
lc.lifc_flags = 0;
lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
lc.lifc_req = xmalloc(lc.lifc_len);
ret = ioctl(fd, SIOCGLIFCONF, &lc);
if (ret == -1) {
reason = "SIOCGLIFCONF";
goto finish;
}
list = rb_ary_new();
for (i = 0; i < ln.lifn_count; i++) {
struct lifreq *req = &lc.lifc_req[i];
if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
if (req->lifr_addr.ss_family == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id == 0) {
struct lifreq req2;
memcpy(req2.lifr_name, req->lifr_name, LIFNAMSIZ);
ret = ioctl(fd, SIOCGLIFINDEX, &req2);
if (ret == -1) {
reason = "SIOCGLIFINDEX";
goto finish;
}
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index;
}
rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen));
}
}
finish:
save_errno = errno;
xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
struct ifconf conf;
struct ifreq *req;
VALUE list = Qnil;
const char *reason = NULL;
int save_errno;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
retry:
conf.ifc_len = bufsize;
conf.ifc_req = (struct ifreq *)buf;
/* fprintf(stderr, "bufsize: %d\n", bufsize); */
ret = ioctl(fd, SIOCGIFCONF, &conf);
if (ret == -1) {
reason = "SIOCGIFCONF";
goto finish;
}
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
if (bufsize < conf.ifc_len) {
/* NetBSD returns required size for all interfaces. */
bufsize = conf.ifc_len + EXTRA_SPACE;
}
else {
bufsize = bufsize << 1;
}
if (buf == initbuf)
buf = NULL;
buf = xrealloc(buf, bufsize);
goto retry;
}
close(fd);
fd = -1;
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
(sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
finish:
save_errno = errno;
if (buf != initbuf)
xfree(buf);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_unicast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
int dummy1;
int dummy2;
int dummy3;
long dummy4;
long dummy5;
long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_anycast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_addresses_st *Next;
void *dummy1;
ip_adapter_unicast_address_t *FirstUnicastAddress;
ip_adapter_anycast_address_t *FirstAnycastAddress;
void *dummy2;
void *dummy3;
void *dummy4;
void *dummy5;
void *dummy6;
BYTE dummy7[8];
DWORD dummy8;
DWORD dummy9;
DWORD dummy10;
DWORD IfType;
int OperStatus;
DWORD dummy12;
DWORD dummy13[16];
void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
GetAdaptersAddresses_t pGetAdaptersAddresses;
ULONG len;
DWORD ret;
ip_adapter_addresses_t *adapters;
VALUE list;
h = LoadLibrary("iphlpapi.dll");
if (!h)
rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
FreeLibrary(h);
rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
ip_adapter_unicast_address_t *uni;
ip_adapter_anycast_address_t *any;
if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
continue;
for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
}
for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
}
}
FreeLibrary(h);
return list;
#endif
} Возвращает локальные IP-адреса в виде массива.
Массив содержит объекты Addrinfo.
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
#<Addrinfo: 192.168.0.128>,
#<Addrinfo: ::1>,
...] static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
VALUE domain, type, protocol;
int fd;
int d, t;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
return rsock_init_sock(sock, fd);
} Создаёт новый объект сокета.
domain должно быть доменом связи, например :INET, :INET6, :UNIX и т. д.
socktype должен быть типом сокета, например :STREAM, :DGRAM, :RAW и т. д.
protocol — необязательный аргумент; он должен быть протоколом, определённым для домена. Если протокол не указан, внутри используется значение 0.
Socket.new(:INET, :STREAM) # TCP socket Socket.new(:INET, :DGRAM) # UDP socket Socket.new(:UNIX, :STREAM) # UNIX stream socket Socket.new(:UNIX, :DGRAM) # UNIX datagram socket
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
return addr;
} Упаковывает port и host в строку sockaddr формата AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
return addr;
} Упаковывает path в строку sockaddr формата AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создаёт пару сокетов, соединённых друг с другом.
domain должно быть доменом связи, например :INET, :INET6, :UNIX и т. д.
socktype должен быть типом сокета, например :STREAM, :DGRAM, :RAW и т. д.
protocol должен быть протоколом, определённым для домена; по умолчанию используется 0 для данного домена.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
return addr;
} Упаковывает port и host в строку sockaddr формата AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
return addr;
} Упаковывает path в строку sockaddr формата AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создаёт пару сокетов, соединённых друг с другом.
domain должно быть доменом связи, например :INET, :INET6, :UNIX и т. д.
socktype должен быть типом сокета, например :STREAM, :DGRAM, :RAW и т. д.
protocol должен быть протоколом, определённым для домена; по умолчанию используется 0 для данного домена.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
# File ext/socket/lib/socket.rb, line 662
def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket
if open_timeout && (connect_timeout || resolv_timeout)
raise ArgumentError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"
end
sock = if fast_fallback && !(host && ip_address?(host))
tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:)
else
tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:)
end
if block_given?
begin
yield sock
ensure
sock.close
end
else
sock
end
end Создаёт новый объект сокета, подключённый к host:port по протоколу TCP/IP.
Начиная с Ruby 3.4, этот метод по умолчанию работает в соответствии с алгоритмом Happy Eyeballs Version 2 (RFC 8305).
Подробные сведения о Happy Eyeballs Version 2 см. в описании Socket.tcp_fast_fallback=.
Чтобы метод работал так же, как в Ruby 3.3 и более ранних версиях, явно укажите параметр fast_fallback:false. Либо задайте Socket.tcp_fast_fallback= значение false: это отключит Happy Eyeballs Version 2 не только для этого метода, но и глобально для всех Socket.
Если указаны local_host:local_port, сокет привязывается к ним.
Необязательный последний аргумент opts — это параметры, представленные в виде хеша. opts может содержать следующие параметры:
- :resolv_timeout
-
Задаёт тайм-аут в секундах, отсчитываемый с момента начала разрешения имени хоста.
- :connect_timeout
-
Этот метод последовательно пытается подключиться ко всем возможным адресам назначения.
connect_timeoutзадаёт тайм-аут в секундах, отсчитываемый с начала попытки подключения к последнему адресу из списка.
По умолчанию все попытки подключения продолжаются до истечения тайм-аута.
Если явно указан параметрfast_fallback:false,
для каждой попытки подключения устанавливается тайм-аут, и попытки, превысившие его, отменяются. - :open_timeout
-
Задаёт тайм-аут в секундах, отсчитываемый с начала выполнения метода.
Если этот тайм-аут истечёт, пока остаются адреса, для которых ещё не предпринималась попытка подключения, дальнейшие попытки выполняться не будут.
Если этот параметр указан вместе с другими параметрами тайм-аута, будет вызвано исключениеArgumentError. - :fast_fallback
-
Включает алгоритм Happy Eyeballs Version 2 (включён по умолчанию).
Если передан блок, он вызывается с сокетом. Возвращается значение блока. При возврате из этого метода сокет закрывается.
Если блок не передан, возвращается сокет.
Socket.tcp("www.ruby-lang.org", 80) {|sock|
sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
sock.close_write
puts sock.read
}
VALUE socket_s_tcp_fast_fallback(VALUE self) {
return rb_ivar_get(rb_cSocket, tcp_fast_fallback);
} Возвращает, включён или отключён Happy Eyeballs Version 2 (RFC 8305), появившийся в Ruby 3.4 при использовании TCPSocket.new и Socket.tcp.
Если возвращается true, алгоритм включён для TCPSocket.new и Socket.tcp. (Примечание: Happy Eyeballs Version 2 недоступен при использовании TCPSocket.new в Windows.)
Если возвращается false, Happy Eyeballs Version 2 отключён.
Подробные сведения о Happy Eyeballs Version 2 см. в описании Socket.tcp_fast_fallback=.
VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) {
rb_ivar_set(rb_cSocket, tcp_fast_fallback, value);
return value;
} Глобально включает или отключает Happy Eyeballs Version 2 (RFC 8305), появившийся в Ruby 3.4 при использовании TCPSocket.new и Socket.tcp.
Если задано значение true, функция включается как для ‘TCPSocket.new`, так и для `Socket.tcp`. (Примечание: эта функция недоступна при использовании TCPSocket.new в Windows.)
Если задано значение false, восстанавливается поведение Ruby 3.3 и более ранних версий.
Если значение явно не задано вызовом этого метода, по умолчанию используется true. Однако если установлена переменная окружения RUBY_TCP_NO_FAST_FALLBACK=1, по умолчанию используется false.
Чтобы управлять параметром отдельно для каждого метода, используйте для каждого метода именованный аргумент fast_fallback.
Happy Eyeballs Version 2
Happy Eyeballs Version 2 (RFC 8305) — это алгоритм, разработанный для улучшения подключения клиентских сокетов.
Он призван сделать подключения более надёжными и эффективными за счёт параллельного разрешения имён хостов и выполнения попыток подключения вместо их последовательного выполнения.
Начиная с Ruby 3.4, этот метод работает с использованием данного алгоритма следующим образом:
-
Начинает параллельно разрешать адреса IPv6 и IPv4.
-
Начинает подключение к одному из первых полученных адресов.
Если адреса IPv4 получены первыми, метод ожидает 50 мс разрешения имени IPv6, чтобы отдать приоритет подключениям IPv6. -
После начала попытки подключения метод ожидает 250 мс установления соединения.
Если за это время соединение не установлено, каждые 250 мс начинается новая попытка подключения,
пока соединение не будет установлено или не закончатся возможные адреса.
(Хотя RFC 8305 строго предписывает сортировать адреса,
из соображений производительности этот метод лишь чередует адреса IPv6 и IPv4.) -
После установления соединения все оставшиеся попытки подключения отменяются.
# File ext/socket/lib/socket.rb, line 1314
def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
tcp_server_sockets(host, port) {|sockets|
accept_loop(sockets, &b)
}
end Создаёт сервер TCP/IP на порту port и вызывает блок для каждого принятого подключения. Блок вызывается с сокетом и адресом клиента в виде объекта Addrinfo.
Если указан host, он используется вместе с port для определения адресов сервера.
При возврате из блока сокет не закрывается. Поэтому приложение должно закрыть его явно.
Этот метод последовательно вызывает блок. Это означает, что следующее подключение не будет принято, пока блок не завершит работу. Поэтому для обслуживания нескольких клиентов одновременно следует использовать механизм параллельной обработки, например потоки.
Обратите внимание, что для определения адресов сокетов сервера используется Addrinfo.getaddrinfo. Если Addrinfo.getaddrinfo возвращает два или более адреса, например адреса IPv4 и IPv6, используются все они. Socket.tcp_server_loop завершается успешно, если можно использовать хотя бы один сокет.
# Sequential echo server.
# It services only one client at a time.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# Threaded echo server
# It services multiple clients at a time.
# Note that it may accept connections too much.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
Thread.new {
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
}
# File ext/socket/lib/socket.rb, line 1214
def self.tcp_server_sockets(host=nil, port)
if port == 0
sockets = tcp_server_sockets_port0(host)
else
last_error = nil
sockets = []
begin
Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
begin
s = ai.listen
rescue SystemCallError
last_error = $!
next
end
sockets << s
}
if sockets.empty?
raise last_error
end
rescue Exception
sockets.each(&:close)
raise
end
end
if block_given?
begin
yield sockets
ensure
sockets.each(&:close)
end
else
sockets
end
end Создаёт серверные сокеты TCP/IP для host и port. Аргумент host необязателен.
Если блок не передан, возвращается массив сокетов, ожидающих подключения.
Если передан блок, он вызывается с сокетами. Возвращается значение блока. При возврате из этого метода сокеты закрываются.
Если port равен 0, фактический номер порта выбирается динамически. Однако у всех сокетов в результате будет один и тот же номер порта.
# tcp_server_sockets returns two sockets.
sockets = Socket.tcp_server_sockets(1296)
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
# The sockets contains IPv6 and IPv4 sockets.
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:1296 TCP>
# #<Addrinfo: 0.0.0.0:1296 TCP>
# IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
sockets = Socket.tcp_server_sockets(0)
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:53114 TCP>
# #<Addrinfo: 0.0.0.0:53114 TCP>
# The block is called with the sockets.
Socket.tcp_server_sockets(0) {|sockets|
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
}
# File ext/socket/lib/socket.rb, line 1484
def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
udp_server_sockets(host, port) {|sockets|
udp_server_loop_on(sockets, &b)
}
end Создаёт сервер UDP/IP на порту port и вызывает блок для каждого полученного сообщения. Блок вызывается с сообщением и сведениями об его источнике.
Этот метод самостоятельно создаёт сокеты, используя port. Если указан host, он используется вместе с port для определения адресов сервера.
msg — это строка.
msg_src — объект Socket::UDPSource. Он используется для отправки ответа.
# UDP/IP echo server.
Socket.udp_server_loop(9261) {|msg, msg_src|
msg_src.reply msg
}
# File ext/socket/lib/socket.rb, line 1457
def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
loop {
readable, _, _ = IO.select(sockets)
udp_server_recv(readable, &b)
}
end Запускает цикл сервера UDP/IP на указанных сокетах.
Для аргумента подходит результат вызова Socket.udp_server_sockets.
Для каждого полученного сообщения вызывается блок.
# File ext/socket/lib/socket.rb, line 1430
def self.udp_server_recv(sockets)
sockets.each {|r|
msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock(exception: false)
next if msg == :wait_readable
ai = r.local_address
if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
}
else
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
r.send reply_msg, 0, sender_addrinfo
}
end
}
end Получает пакеты UDP/IP из указанных sockets. Для каждого полученного пакета вызывается блок.
Блок получает msg и msg_src. msg — это строка с полезной нагрузкой полученного пакета. msg_src — объект Socket::UDPSource, используемый для отправки ответа.
Socket.udp_server_loop можно реализовать с помощью этого метода следующим образом.
udp_server_sockets(host, port) {|sockets|
loop {
readable, _, _ = IO.select(sockets)
udp_server_recv(readable) {|msg, msg_src| ... }
}
} # File ext/socket/lib/socket.rb, line 1342
def self.udp_server_sockets(host=nil, port)
last_error = nil
sockets = []
ipv6_recvpktinfo = nil
if defined? Socket::AncillaryData
if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
elsif defined? Socket::IPV6_PKTINFO # RFC 2292
ipv6_recvpktinfo = Socket::IPV6_PKTINFO
end
end
local_addrs = Socket.ip_address_list
ip_list = []
Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
if ai.ipv4? && ai.ip_address == "0.0.0.0"
local_addrs.each {|a|
next unless a.ipv4?
ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
}
elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
local_addrs.each {|a|
next unless a.ipv6?
ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
}
else
ip_list << ai
end
}
ip_list.uniq!(&:to_sockaddr)
if port == 0
sockets = ip_sockets_port0(ip_list, false)
else
ip_list.each {|ip|
ai = Addrinfo.udp(ip.ip_address, port)
begin
s = ai.bind
rescue SystemCallError
last_error = $!
next
end
sockets << s
}
if sockets.empty?
raise last_error
end
end
sockets.each {|s|
ai = s.local_address
if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
end
}
if block_given?
begin
yield sockets
ensure
sockets.each(&:close) if sockets
end
else
sockets
end
end Создаёт сокеты UDP/IP для UDP-сервера.
Если блок не передан, возвращается массив сокетов.
Если передан блок, он вызывается с сокетами. Возвращается значение блока. При возврате из этого метода сокеты закрываются.
Если port равен нулю, выбирается некоторый порт. Выбранный порт используется для всех сокетов.
# UDP/IP echo server
Socket.udp_server_sockets(0) {|sockets|
p sockets.first.local_address.ip_port #=> 32963
Socket.udp_server_loop_on(sockets) {|msg, msg_src|
msg_src.reply msg
}
}
# File ext/socket/lib/socket.rb, line 1534
def self.unix(path) # :yield: socket
addr = Addrinfo.unix(path)
sock = addr.connect
if block_given?
begin
yield sock
ensure
sock.close
end
else
sock
end
end Создаёт новый сокет, подключённый к пути path через сокет UNIX.
Если передан блок, он вызывается с сокетом. Возвращается значение блока. При возврате из этого метода сокет закрывается.
Если блок не передан, возвращается сокет.
# talk to /tmp/sock socket.
Socket.unix("/tmp/sock") {|sock|
t = Thread.new { IO.copy_stream(sock, STDOUT) }
IO.copy_stream(STDIN, sock)
t.join
}
# File ext/socket/lib/socket.rb, line 1626
def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
unix_server_socket(path) {|serv|
accept_loop(serv, &b)
}
end Создаёт сервер UNIX-сокетов по пути path. Для каждого принятого сокета вызывается блок.
Если указан host, он используется вместе с port для определения портов сервера.
При возврате из блока сокет не закрывается. Поэтому приложение должно закрыть его.
Сначала этот метод удаляет файл сокета, указанный в path, если этот файл является файлом сокета и принадлежит пользователю приложения. Это безопасно, только если каталог, в котором находится path, не изменяется злоумышленником. Поэтому не используйте /tmp/malicious-users-directory/socket. Обратите внимание, что /tmp/socket и /tmp/your-private-directory/socket безопасны, если каталог /tmp имеет бит sticky.
# Sequential echo server.
# It services only one client at a time.
Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# File ext/socket/lib/socket.rb, line 1564
def self.unix_server_socket(path)
unless unix_socket_abstract_name?(path)
begin
st = File.lstat(path)
rescue Errno::ENOENT
end
if st&.socket? && st.owned?
File.unlink path
end
end
s = Addrinfo.unix(path).listen
if block_given?
begin
yield s
ensure
s.close
unless unix_socket_abstract_name?(path)
File.unlink path
end
end
else
s
end
end создаёт серверный сокет UNIX по пути path
Если блок не задан, возвращает сокет, ожидающий подключений.
Если блок задан, он вызывается с сокетом, а возвращается значение блока. При выходе из блока сокет закрывается, а файл сокета удаляется.
socket = Socket.unix_server_socket("/tmp/s")
p socket #=> #<Socket:fd 3>
p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
Socket.unix_server_socket("/tmp/sock") {|s|
p s #=> #<Socket:fd 3>
p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
}
static VALUE
sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
{
struct sockaddr_in * sockaddr;
VALUE host;
sockaddr = (struct sockaddr_in*)SockAddrStringValuePtr(addr);
if (RSTRING_LEN(addr) <
(char*)&((struct sockaddr *)sockaddr)->sa_family +
sizeof(((struct sockaddr *)sockaddr)->sa_family) -
(char*)sockaddr)
rb_raise(rb_eArgError, "too short sockaddr");
if (((struct sockaddr *)sockaddr)->sa_family != AF_INET
#ifdef INET6
&& ((struct sockaddr *)sockaddr)->sa_family != AF_INET6
#endif
) {
#ifdef INET6
rb_raise(rb_eArgError, "not an AF_INET/AF_INET6 sockaddr");
#else
rb_raise(rb_eArgError, "not an AF_INET sockaddr");
#endif
}
host = rsock_make_ipaddr((struct sockaddr*)sockaddr, RSTRING_SOCKLEN(addr));
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
} Распаковывает sockaddr в порт и ip_address.
sockaddr должен быть строкой или объектом addrinfo для AF_INET/AF_INET6.
sockaddr = Socket.sockaddr_in(80, "127.0.0.1") p sockaddr #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" p Socket.unpack_sockaddr_in(sockaddr) #=> [80, "127.0.0.1"]
static VALUE
sock_s_unpack_sockaddr_un(VALUE self, VALUE addr)
{
struct sockaddr_un * sockaddr;
VALUE path;
sockaddr = (struct sockaddr_un*)SockAddrStringValuePtr(addr);
if (RSTRING_LEN(addr) <
(char*)&((struct sockaddr *)sockaddr)->sa_family +
sizeof(((struct sockaddr *)sockaddr)->sa_family) -
(char*)sockaddr)
rb_raise(rb_eArgError, "too short sockaddr");
if (((struct sockaddr *)sockaddr)->sa_family != AF_UNIX) {
rb_raise(rb_eArgError, "not an AF_UNIX sockaddr");
}
if (sizeof(struct sockaddr_un) < (size_t)RSTRING_LEN(addr)) {
rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
}
path = rsock_unixpath_str(sockaddr, RSTRING_SOCKLEN(addr));
return path;
} Распаковывает sockaddr в путь.
sockaddr должен быть строкой или объектом addrinfo для AF_UNIX.
sockaddr = Socket.sockaddr_un("/tmp/sock")
p Socket.unpack_sockaddr_un(sockaddr) #=> "/tmp/sock"
Открытые методы экземпляра
static VALUE
sock_accept(VALUE server)
{
union_sockaddr buffer;
socklen_t length = (socklen_t)sizeof(buffer);
VALUE peer = rsock_s_accept(rb_cSocket, server, &buffer.addr, &length);
return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
} # File ext/socket/lib/socket.rb, line 598 def accept_nonblock(exception: true) __accept_nonblock(exception) end
Принимает входящее соединение с помощью accept(2) после установки O_NONBLOCK для базового файлового дескриптора. Возвращает массив, содержащий принятый сокет для входящего соединения, client_socket, и объект Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
begin # emulate blocking accept
client_socket, client_addrinfo = socket.accept_nonblock
rescue IO::WaitReadable, Errno::EINTR
IO.select([socket])
retry
end
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
Список исключений, которые могут быть выброшены при неудачном вызове accept_nonblock, см. в описании Socket#accept.
Socket#accept_nonblock может вызвать любую ошибку, соответствующую сбою accept(2), включая Errno::EWOULDBLOCK.
Если исключение имеет тип Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED или Errno::EPROTO, к нему добавляется модуль IO::WaitReadable. Поэтому IO::WaitReadable можно использовать для перехвата исключений и повторного вызова accept_nonblock.
Указав ключевой аргумент exception для false, можно задать, чтобы accept_nonblock не вызывал исключение IO::WaitReadable, а возвращал вместо этого символ :wait_readable.
См. также
static VALUE
sock_bind(VALUE sock, VALUE addr)
{
VALUE rai;
rb_io_t *fptr;
SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
} Привязывает сокет к указанному локальному адресу.
Параметр
-
local_sockaddr— sockaddrstruct, содержащийся в строке или объектеAddrinfo
Пример
require 'socket'
# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
# use struct sockaddr
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
Исключения в системах Unix
В системах на базе Unix при сбое вызова bind могут возникнуть следующие системные исключения:
-
Errno::EACCES — указанный sockaddr защищён, и у текущего пользователя нет разрешения на привязку к нему
-
Errno::EADDRINUSE — указанный sockaddr уже используется
-
Errno::EADDRNOTAVAIL — указанный sockaddr недоступен на локальном компьютере
-
Errno::EAFNOSUPPORT — указанный sockaddr не является допустимым адресом для семейства вызывающего
socket -
Errno::EBADF — указанный sockaddr не является допустимым файловым дескриптором
-
Errno::EFAULT — аргумент sockaddr недоступен
-
Errno::EINVAL —
socketуже привязан к адресу, а протокол не поддерживает привязку к новому sockaddr, либоsocketбыл отключён. -
Errno::EINVAL — длина адреса недопустима для данного семейства адресов
-
Errno::ENAMETOOLONG — длина разрешённого пути превышает PATH_MAX
-
Errno::ENOBUFS — недостаточно места в буфере
-
Errno::ENOSR — недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTSOCK —
socketне указывает на сокет -
Errno::EOPNOTSUPP — тип сокета
socketне поддерживает привязку к адресу
В системах на базе Unix, если семейство адресов вызывающего socket — Socket::AF_UNIX, при сбое вызова bind могут возникнуть следующие исключения:
-
Errno::EACCES — запрещён поиск одного из компонентов начального пути или запрещена запись в
socket -
Errno::EDESTADDRREQ — аргумент sockaddr является нулевым указателем
-
Errno::EISDIR — то же, что Errno::EDESTADDRREQ
-
Errno::EIO — произошла ошибка ввода-вывода
-
Errno::ELOOP — при преобразовании имени пути в sockaddr обнаружено слишком много символических ссылок
-
Errno::ENAMETOOLLONG — длина компонента пути превышает NAME_MAX символов либо длина всего пути превышает PATH_MAX символов
-
Errno::ENOENT — компонент пути не указывает на существующий файл или путь является пустой строкой
-
Errno::ENOTDIR — компонент начальной части пути в sockaddr не является каталогом
-
Errno::EROFS — имя должно было бы находиться в файловой системе, доступной только для чтения
Исключения в Windows
В системах Windows при сбое вызова bind могут возникнуть следующие системные исключения:
-
Errno::ENETDOWN — сеть не работает
-
Errno::EACCES — не удалось подключить дейтаграммный сокет к широковещательному адресу
-
Errno::EADDRINUSE — локальный адрес сокета уже используется
-
Errno::EADDRNOTAVAIL — указанный адрес недопустим для этого компьютера
-
Errno::EFAULT — внутренний адрес сокета или параметр длины адреса слишком мал либо не является допустимой частью адресного пространства пользователя
-
Errno::EINVAL —
socketуже привязан к адресу -
Errno::ENOBUFS — недостаточно места в буфере
-
Errno::ENOTSOCK — аргумент
socketне указывает на сокет
См. также
-
справочные страницы bind в системах на базе Unix
-
функцию bind в справочнике функций Winsock от Microsoft
static VALUE
sock_connect(VALUE self, VALUE addr)
{
VALUE rai;
SockAddrStringValueWithAddrinfo(addr, rai);
addr = rb_str_new4(addr);
int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT);
if (result < 0) {
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
return INT2FIX(result);
} Запрашивает установление соединения с указанным remote_sockaddr. В случае успеха возвращает 0, иначе вызывает исключение.
Параметр
-
remote_sockaddr— sockaddrstruct, содержащийся в строке или объектеAddrinfo
Пример:
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) socket.connect( sockaddr ) socket.write( "GET / HTTP/1.0\r\n\r\n" ) results = socket.read
Исключения в системах Unix
В системах на базе Unix при сбое вызова connect могут возникнуть следующие системные исключения:
-
Errno::EACCES — запрещён поиск одного из компонентов начального пути или запрещена запись в
socket -
Errno::EADDRINUSE — sockaddr уже используется
-
Errno::EADDRNOTAVAIL — указанный sockaddr недоступен на локальном компьютере
-
Errno::EAFNOSUPPORT — указанный sockaddr не является допустимым адресом для семейства адресов указанного
socket -
Errno::EALREADY — для указанного сокета уже выполняется подключение
-
Errno::EBADF —
socketне является допустимым файловым дескриптором -
Errno::ECONNREFUSED — целевой sockaddr не ожидал входящих соединений и отклонил запрос на подключение
-
Errno::ECONNRESET — удалённый узел сбросил запрос на подключение
-
Errno::EFAULT — sockaddr недоступен
-
Errno::EHOSTUNREACH — узел назначения недоступен (вероятно, узел не работает или удалённый маршрутизатор не может до него добраться)
-
Errno::EINPROGRESS — для
socketустановлен O_NONBLOCK, и соединение нельзя установить немедленно; оно будет установлено асинхронно -
Errno::EINTR — попытка установить соединение была прервана доставкой перехваченного сигнала; соединение будет установлено асинхронно
-
Errno::EISCONN — указанный
socketуже подключён -
Errno::EINVAL — длина адреса, используемая для sockaddr, недопустима для данного семейства адресов либо в sockaddr указано недопустимое семейство
-
Errno::ENAMETOOLONG — длина разрешённого пути превышает PATH_MAX
-
Errno::ENETDOWN — локальный интерфейс, используемый для доступа к адресу назначения, не работает
-
Errno::ENETUNREACH — маршрут к сети отсутствует
-
Errno::ENOBUFS — недостаточно места в буфере
-
Errno::ENOSR — недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTSOCK — аргумент
socketне указывает на сокет -
Errno::EOPNOTSUPP — вызывающий
socketожидает входящих соединений и не может быть подключён -
Errno::EPROTOTYPE — тип sockaddr отличается от типа сокета, привязанного к указанному адресу узла-партнёра
-
Errno::ETIMEDOUT — время ожидания подключения истекло до установления соединения.
В системах на базе Unix, если семейство адресов вызывающего socket — AF_UNIX, при сбое вызова connect могут возникнуть следующие исключения:
-
Errno::EIO — при чтении из файловой системы или записи в неё произошла ошибка ввода-вывода
-
Errno::ELOOP — при преобразовании имени пути в sockaddr обнаружено слишком много символических ссылок
-
Errno::ENAMETOOLONG — длина компонента пути превышает NAME_MAX символов либо длина всего пути превышает PATH_MAX символов
-
Errno::ENOENT — компонент пути не указывает на существующий файл или путь является пустой строкой
-
Errno::ENOTDIR — компонент начальной части пути в sockaddr не является каталогом
Исключения в Windows
В системах Windows при сбое вызова connect могут возникнуть следующие системные исключения:
-
Errno::ENETDOWN — сеть не работает
-
Errno::EADDRINUSE — локальный адрес сокета уже используется
-
Errno::EINTR — сокет был отменён
-
Errno::EINPROGRESS — выполняется блокирующий сокет или поставщик служб всё ещё обрабатывает функцию обратного вызова. Либо для
socketвыполняется неблокирующий вызов connect. -
Errno::EALREADY — см. Errno::EINVAL
-
Errno::EADDRNOTAVAIL — удалённый адрес недопустим, например ADDR_ANY TODO check ADDRANY TO
INADDR_ANY -
Errno::EAFNOSUPPORT — адреса указанного семейства нельзя использовать с этим
socket -
Errno::ECONNREFUSED — целевой sockaddr не ожидал входящих соединений и отклонил запрос на подключение
-
Errno::EFAULT — внутренний адрес сокета или параметр длины адреса слишком мал либо не является допустимой частью адресного пространства пользователя
-
Errno::EINVAL —
socketявляется сокетом, ожидающим входящих соединений -
Errno::EISCONN —
socketуже подключён -
Errno::ENETUNREACH — в данный момент сеть недоступна с этого узла
-
Errno::EHOSTUNREACH — маршрут к сети отсутствует
-
Errno::ENOBUFS — недостаточно места в буфере
-
Errno::ENOTSOCK — аргумент
socketне указывает на сокет -
Errno::ETIMEDOUT — время ожидания подключения истекло до установления соединения.
-
Errno::EWOULDBLOCK — сокет помечен как неблокирующий, и соединение нельзя установить немедленно
-
Errno::EACCES — не удалось подключить дейтаграммный сокет к широковещательному адресу
См. также
-
справочные страницы connect в системах на базе Unix
-
функцию connect в справочнике функций Winsock от Microsoft
# File ext/socket/lib/socket.rb, line 1676 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
Запрашивает установление соединения с указанным remote_sockaddr после установки O_NONBLOCK для базового файлового дескриптора. В случае успеха возвращает 0, иначе вызывает исключение.
Параметр
-
remote_sockaddr— sockaddrstruct, содержащийся в строке или объектеAddrinfo
Пример:
# Pull down Google's web page
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(80, 'www.google.com')
begin # emulate blocking connect
socket.connect_nonblock(sockaddr)
rescue IO::WaitWritable
IO.select(nil, [socket]) # wait 3-way handshake completion
begin
socket.connect_nonblock(sockaddr) # check connection failure
rescue Errno::EISCONN
end
end
socket.write("GET / HTTP/1.0\r\n\r\n")
results = socket.read
Список исключений, которые могут быть выброшены при неудачном вызове connect_nonblock, см. в описании Socket#connect.
Socket#connect_nonblock может вызвать любую ошибку, соответствующую сбою connect(2), включая Errno::EINPROGRESS.
Если исключение имеет тип Errno::EINPROGRESS, к нему добавляется модуль IO::WaitWritable. Поэтому IO::WaitWritable можно использовать для перехвата исключений и повторного вызова connect_nonblock.
Указав ключевой аргумент exception для false, можно задать, чтобы connect_nonblock не вызывал исключение IO::WaitWritable, а возвращал вместо этого символ :wait_writable.
См. также
# File ext/socket/lib/socket.rb, line 468
def ipv6only!
if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
end
end включает параметр сокета IPV6_V6ONLY, если доступен IPV6_V6ONLY.
VALUE
rsock_sock_listen(VALUE sock, VALUE log)
{
rb_io_t *fptr;
int backlog;
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
rb_sys_fail("listen(2)");
return INT2FIX(0);
} Ожидает входящие соединения, используя указанное значение int в качестве длины очереди. Вызов listen применяется, только если socket имеет тип SOCK_STREAM или SOCK_SEQPACKET.
Параметр
-
backlog— максимальная длина очереди ожидающих соединений.
Пример 1
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
Пример 2 (ожидание подключений на произвольном порту, только в системах на базе Unix):
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
Исключения в системах Unix
В системах на базе Unix приведённый выше пример работает, поскольку на адресе ADDR_ANY создаётся новая структура sockaddr с произвольным номером порта, назначенным ядром. В Windows он не сработает, поскольку перед вызовом listen необходимо привязать socket с помощью вызова bind.
Если значение backlog превышает максимальную длину очереди, установленную реализацией, будет использована максимальная длина очереди этой реализации.
В системах на базе Unix при сбое вызова listen могут возникнуть следующие системные исключения:
-
Errno::EBADF — аргумент socket не является допустимым файловым дескриптором
-
Errno::EDESTADDRREQ — socket не привязан к локальному адресу, а протокол не поддерживает ожидание подключений на непривязанном сокете
-
Errno::EINVAL — socket уже подключён
-
Errno::ENOTSOCK — аргумент socket не указывает на сокет
-
Errno::EOPNOTSUPP — протокол socket не поддерживает listen
-
Errno::EACCES — у вызывающего процесса нет необходимых привилегий
-
Errno::EINVAL — socket был отключён
-
Errno::ENOBUFS — в системе недостаточно ресурсов для завершения вызова
Исключения в Windows
В системах Windows при сбое вызова listen могут возникнуть следующие системные исключения:
-
Errno::ENETDOWN — сеть не работает
-
Errno::EADDRINUSE — локальный адрес сокета уже используется. Обычно это происходит при выполнении bind, но может произойти и позже, если вызов bind был выполнен для частично подстановочного адреса (с использованием ADDR_ANY) и конкретный адрес требуется определить во время вызова listen
-
Errno::EINPROGRESS — выполняется вызов Windows Sockets 1.1 или поставщик служб всё ещё обрабатывает функцию обратного вызова
-
Errno::EINVAL —
socketне был привязан вызовом bind. -
Errno::EISCONN —
socketуже подключён -
Errno::EMFILE — больше нет доступных дескрипторов сокетов
-
Errno::ENOBUFS — недостаточно места в буфере
-
Errno::ENOTSOC —
socketне является сокетом -
Errno::EOPNOTSUPP — тип объекта
socketне поддерживает метод listen
См. также
-
справочные страницы listen в системах на базе Unix
-
функцию listen в справочнике функций Winsock от Microsoft
static VALUE
sock_recvfrom(int argc, VALUE *argv, VALUE sock)
{
return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
} Получает до maxlen байт из socket. flags — ноль или несколько параметров MSG_. Первый элемент результата, mesg, содержит полученные данные. Второй элемент, sender_addrinfo, содержит информацию об адресе отправителя, специфичную для протокола.
Параметры
-
maxlen— максимальное количество байт, получаемых из сокета -
flags— ноль или несколько параметровMSG_
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client, client_addrinfo = socket.accept
data = client.recvfrom( 20 )[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Watch this get cut short!"
socket.close
Исключения в системах Unix
В системах на базе Unix при сбое вызова recvfrom могут возникнуть следующие системные исключения:
-
Errno::EAGAIN — для файлового дескриптора
socketустановлен флаг O_NONBLOCK, и данных для получения нет; либо установлен флагMSG_OOB, внеполосные данные недоступны, а для файлового дескриптораsocketустановлен флаг O_NONBLOCK илиsocketне поддерживает блокирующее ожидание внеполосных данных -
Errno::EWOULDBLOCK — см. Errno::EAGAIN
-
Errno::EBADF —
socketне является допустимым файловым дескриптором -
Errno::ECONNRESET — узел-партнёр принудительно закрыл соединение
-
Errno::EFAULT — невозможно получить доступ к внутреннему буферу сокета, адресу или длине адреса либо выполнить запись в них
-
Errno::EINTR — сигнал прервал recvfrom до появления данных
-
Errno::EINVAL — установлен флаг
MSG_OOB, но внеполосные данные недоступны -
Errno::EIO — при чтении из файловой системы или записи в неё произошла ошибка ввода-вывода
-
Errno::ENOBUFS — в системе недостаточно ресурсов для выполнения операции
-
Errno::ENOMEM — для выполнения запроса недостаточно памяти
-
Errno::ENOSR — недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTCONN — предпринята попытка приёма данных через сокет с режимом соединения, который не подключён
-
Errno::ENOTSOCK —
socketне указывает на сокет -
Errno::EOPNOTSUPP — указанные флаги не поддерживаются для этого типа сокета
-
Errno::ETIMEDOUT — время ожидания соединения истекло во время его установления или из-за тайм-аута передачи в активном соединении
Исключения в Windows
В системах Windows при сбое вызова recvfrom могут возникнуть следующие системные исключения:
-
Errno::ENETDOWN — сеть не работает
-
Errno::EFAULT — внутренний буфер и параметры from для
socketнаходятся за пределами адресного пространства пользователя либо внутренний параметр fromlen слишком мал для размещения адреса узла-партнёра -
Errno::EINTR — (блокирующий) вызов был отменён внутренним вызовом функции WinSock WSACancelBlockingCall
-
Errno::EINPROGRESS — выполняется блокирующий вызов Windows Sockets 1.1 или поставщик служб всё ещё обрабатывает функцию обратного вызова
-
Errno::EINVAL —
socketне был привязан вызовом bind, либо был указан неизвестный флаг, либо для сокета с включённым параметромSO_OOBINLINEбыл указанMSG_OOB, либо (только для сокетов потокового типа) внутренний параметр len дляsocketравен нулю или отрицателен -
Errno::EISCONN —
socketуже подключён. Вызов recvfrom для подключённого сокета не разрешён, если сокет ориентирован на соединение или не ориентирован на соединение. -
Errno::ENETRESET — соединение разорвано, поскольку проверка активности keep-alive обнаружила сбой во время выполнения операции.
-
Errno::EOPNOTSUPP — указан
MSG_OOB, ноsocketне является потоковым, например типаSOCK_STREAM. В домене взаимодействия, связанном сsocket, внеполосные данные не поддерживаются либоsocketявляется однонаправленным и поддерживает только операции отправки -
Errno::ESHUTDOWN —
socketбыл отключён. После вызова shutdown вызвать recvfrom для сокета невозможно. -
Errno::EWOULDBLOCK —
socketпомечен как неблокирующий, и вызов recvfrom был бы заблокирован. -
Errno::EMSGSIZE — сообщение слишком велико для указанного буфера и было усечено.
-
Errno::ETIMEDOUT — соединение было разорвано из-за сбоя сети или из-за того, что система на другом конце неожиданно перестала работать
-
Errno::ECONNRESET — виртуальный канал был сброшен удалённой стороной при жёстком или принудительном закрытии. Приложению следует закрыть сокет; он больше непригоден для использования. Для UDP-дейтаграммного сокета эта ошибка означает, что предыдущая операция отправки привела к получению сообщения ICMP Port Unreachable.
# File ext/socket/lib/socket.rb, line 541 def recvfrom_nonblock(len, flag = 0, str = nil, exception: true) __recvfrom_nonblock(len, flag, str, exception) end
Получает до maxlen байт из socket с помощью recvfrom(2) после установки O_NONBLOCK для базового файлового дескриптора. flags — это ноль или несколько параметров MSG_. Первый элемент результатов, mesg, содержит полученные данные. Второй элемент, sender_addrinfo, содержит сведения об адресе отправителя, зависящие от протокола.
Когда recvfrom(2) возвращает 0, Socket#recv_nonblock возвращает nil. В большинстве случаев это означает, что соединение закрыто, но для соединений UDP это может означать, что получен пустой пакет: базовый API не позволяет различить эти два случая.
Параметры
-
maxlen— максимальное количество байт, которое нужно получить из сокета -
flags— ноль или несколько параметровMSG_ -
outbuf— буфер назначенияString -
opts— хеш ключевых аргументов, поддерживающий ‘exception: false`
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
client, client_addrinfo = socket.accept
begin # emulate blocking recvfrom
pair = client.recvfrom_nonblock(20)
rescue IO::WaitReadable
IO.select([client])
retry
end
data = pair[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Watch this get cut short!"
socket.close
См. Socket#recvfrom, чтобы узнать, какие исключения могут быть вызваны при сбое вызова recvfrom_nonblock.
Socket#recvfrom_nonblock может вызвать любую ошибку, соответствующую сбою recvfrom(2), включая Errno::EWOULDBLOCK.
Если исключением является Errno::EWOULDBLOCK или Errno::EAGAIN, к нему добавляется модуль IO::WaitReadable. Поэтому для перехвата исключений и повторного вызова recvfrom_nonblock можно использовать IO::WaitReadable.
Указав ключевой аргумент exception для false, можно указать, что recvfrom_nonblock не должен вызывать исключение IO::WaitReadable, а должен вместо этого возвращать символ :wait_readable.
См.
static VALUE
sock_sysaccept(VALUE server)
{
union_sockaddr buffer;
socklen_t length = (socklen_t)sizeof(buffer);
VALUE peer = rsock_s_accept(0, server, &buffer.addr, &length);
return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
} Принимает входящее соединение и возвращает массив, содержащий файловый дескриптор (целое число) для входящего соединения, client_socket_fd, и Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client_fd, client_addrinfo = socket.sysaccept
client_socket = Socket.for_fd( client_fd )
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
См. Socket#accept, чтобы узнать, какие исключения могут быть вызваны при сбое вызова sysaccept.
См.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.