класс 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.
Константы
- ADDRESS_FAMILIES
- 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
-
Протокол PPP
- AF_PPPOX
-
Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)
- AF_PUP
-
Протокол PARC Universal Packet
- AF_RDS
-
Протокол надёжных датаграммных сокетов (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
-
Принимать адреса IPv6, отображённые из IPv4
- AI_V4MAPPED_CFG
-
Принимать отображённые адреса IPv4, если ядро поддерживает эту возможность
- CONNECTION_ATTEMPT_DELAY
- 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
- HOSTNAME_RESOLUTION_QUEUE_UPDATED
- 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_ADRESS_FORMAT
- 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
-
Протокол PPP
- PF_PPPOX
-
Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)
- PF_PUP
-
Протокол PARC Universal Packet
- PF_RDS
-
Протокол надёжных датаграммных сокетов (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
- RESOLUTION_DELAY
- 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, связанный с очередью RX (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
-
Включить режим 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 1225
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);
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. Этот метод объявлен устаревшим по следующим причинам:
-
Нестандартное представление адреса: двоичная строка длиной 4/16 байт для представления адреса IPv4/IPv6.
-
Выполнение 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);
return rsock_make_hostent(host, res, sock_sockaddr);
} Вместо этого используйте Addrinfo.getaddrinfo. Этот метод объявлен устаревшим по следующим причинам:
-
Третий элемент результата — семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Нестандартное представление адреса: двоичная строка длиной 4/16 байт для представления адреса IPv4/IPv6.
-
Выполнение 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);
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);
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);
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 659
def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket
sock = if fast_fallback && !(host && ip_address?(host))
tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:)
else
tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_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,
для каждой попытки подключения задаётся время ожидания, и попытка, превысившая его, отменяется. - :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 1281
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 1181
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 677
def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil)
if local_host || local_port
local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, timeout: resolv_timeout)
resolving_family_names = local_addrinfos.map { |lai| ADDRESS_FAMILIES.key(lai.afamily) }.uniq
else
local_addrinfos = []
resolving_family_names = ADDRESS_FAMILIES.keys
end
hostname_resolution_threads = []
resolution_store = HostnameResolutionStore.new(resolving_family_names)
connecting_sockets = {}
is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/)
now = current_clock_time
resolution_delay_expires_at = nil
connection_attempt_delay_expires_at = nil
user_specified_connect_timeout_at = nil
last_error = nil
last_error_from_thread = false
if resolving_family_names.size == 1
family_name = resolving_family_names.first
addrinfos = Addrinfo.getaddrinfo(host, port, family_name, :STREAM, timeout: resolv_timeout)
resolution_store.add_resolved(family_name, addrinfos)
hostname_resolution_result = nil
hostname_resolution_notifier = nil
user_specified_resolv_timeout_at = nil
else
hostname_resolution_result = HostnameResolutionResult.new(resolving_family_names.size)
hostname_resolution_notifier = hostname_resolution_result.notifier
hostname_resolution_threads.concat(
resolving_family_names.map { |family|
thread_args = [family, host, port, hostname_resolution_result]
thread = Thread.new(*thread_args) { |*thread_args| resolve_hostname(*thread_args) }
Thread.pass
thread
}
)
user_specified_resolv_timeout_at = resolv_timeout ? now + resolv_timeout : Float::INFINITY
end
loop do
if resolution_store.any_addrinfos? &&
!resolution_delay_expires_at &&
!connection_attempt_delay_expires_at
while (addrinfo = resolution_store.get_addrinfo)
if local_addrinfos.any?
local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily }
if local_addrinfo.nil? # Connecting addrinfoと同じアドレスファミリのLocal addrinfoがない
if resolution_store.any_addrinfos?
# Try other Addrinfo in next "while"
next
elsif connecting_sockets.any? || resolution_store.any_unresolved_family?
# Exit this "while" and wait for connections to be established or hostname resolution in next loop
# Or exit this "while" and wait for hostname resolution in next loop
break
else
raise SocketError.new 'no appropriate local address'
end
end
end
begin
if resolution_store.any_addrinfos? ||
connecting_sockets.any? ||
resolution_store.any_unresolved_family?
socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
socket.bind(local_addrinfo) if local_addrinfo
result = socket.connect_nonblock(addrinfo, exception: false)
else
result = socket = local_addrinfo ?
addrinfo.connect_from(local_addrinfo, timeout: connect_timeout) :
addrinfo.connect(timeout: connect_timeout)
end
if result == :wait_writable
connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY
if resolution_store.empty_addrinfos?
user_specified_connect_timeout_at = connect_timeout ? now + connect_timeout : Float::INFINITY
end
connecting_sockets[socket] = addrinfo
break
else
return socket # connection established
end
rescue SystemCallError => e
socket&.close
last_error = e
if resolution_store.any_addrinfos?
# Try other Addrinfo in next "while"
next
elsif connecting_sockets.any? || resolution_store.any_unresolved_family?
# Exit this "while" and wait for connections to be established or hostname resolution in next loop
# Or exit this "while" and wait for hostname resolution in next loop
break
else
raise last_error
end
end
end
end
ends_at =
if resolution_store.any_addrinfos?
resolution_delay_expires_at || connection_attempt_delay_expires_at
else
[user_specified_resolv_timeout_at, user_specified_connect_timeout_at].compact.max
end
hostname_resolved, writable_sockets, except_sockets = IO.select(
hostname_resolution_notifier,
connecting_sockets.keys,
# Use errorfds to wait for non-blocking connect failures on Windows
is_windows_environment ? connecting_sockets.keys : nil,
second_to_timeout(current_clock_time, ends_at),
)
now = current_clock_time
resolution_delay_expires_at = nil if expired?(now, resolution_delay_expires_at)
connection_attempt_delay_expires_at = nil if expired?(now, connection_attempt_delay_expires_at)
if writable_sockets&.any?
while (writable_socket = writable_sockets.pop)
is_connected = is_windows_environment || (
sockopt = writable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
sockopt.int.zero?
)
if is_connected
connecting_sockets.delete writable_socket
return writable_socket
else
failed_ai = connecting_sockets.delete writable_socket
writable_socket.close
ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address
last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int)
if writable_sockets.any? || connecting_sockets.any?
# Try other writable socket in next "while"
# Or exit this "while" and wait for connections to be established or hostname resolution in next loop
elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family?
# Exit this "while" and try other connection attempt
# Or exit this "while" and wait for hostname resolution in next loop
connection_attempt_delay_expires_at = nil
user_specified_connect_timeout_at = nil
else
raise last_error
end
end
end
end
if except_sockets&.any?
except_sockets.each do |except_socket|
failed_ai = connecting_sockets.delete except_socket
sockopt = except_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
except_socket.close
ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address
last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int)
if except_sockets.any? || connecting_sockets.any?
# Cleanup other except socket in next "each"
# Or exit this "while" and wait for connections to be established or hostname resolution in next loop
elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family?
# Exit this "while" and try other connection attempt
# Or exit this "while" and wait for hostname resolution in next loop
connection_attempt_delay_expires_at = nil
user_specified_connect_timeout_at = nil
else
raise last_error
end
end
end
if hostname_resolved&.any?
while (family_and_result = hostname_resolution_result.get)
family_name, result = family_and_result
if result.is_a? Exception
resolution_store.add_error(family_name, result)
unless (Socket.const_defined?(:EAI_ADDRFAMILY)) &&
(result.is_a?(Socket::ResolutionError)) &&
(result.error_code == Socket::EAI_ADDRFAMILY)
other = family_name == :ipv6 ? :ipv4 : :ipv6
if !resolution_store.resolved?(other) || !resolution_store.resolved_successfully?(other)
last_error = result
last_error_from_thread = true
end
end
else
resolution_store.add_resolved(family_name, result)
end
end
if resolution_store.resolved?(:ipv4)
if resolution_store.resolved?(:ipv6)
hostname_resolution_notifier = nil
resolution_delay_expires_at = nil
user_specified_resolv_timeout_at = nil
elsif resolution_store.resolved_successfully?(:ipv4)
resolution_delay_expires_at = now + RESOLUTION_DELAY
end
end
end
if resolution_store.empty_addrinfos?
if connecting_sockets.empty? && resolution_store.resolved_all_families?
if last_error_from_thread
raise last_error.class, last_error.message, cause: last_error
else
raise last_error
end
end
if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) &&
(expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?)
raise Errno::ETIMEDOUT, 'user specified timeout'
end
end
end
ensure
hostname_resolution_threads.each do |thread|
thread.exit
end
hostname_resolution_result&.close
connecting_sockets.each_key do |connecting_socket|
connecting_socket.close
end
end # File ext/socket/lib/socket.rb, line 1451
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 1424
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 1397
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 1309
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 1501
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 1588
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 bit.
# 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 1531
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"
Приватные методы класса
# File ext/socket/lib/socket.rb, line 969 def self.current_clock_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end
# File ext/socket/lib/socket.rb, line 982 def self.expired?(started_at, ends_at) second_to_timeout(started_at, ends_at)&.zero? end
# File ext/socket/lib/socket.rb, line 954
def self.ip_address?(hostname)
hostname.match?(IPV6_ADRESS_FORMAT) || hostname.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/)
end # File ext/socket/lib/socket.rb, line 959
def self.resolve_hostname(family, host, port, hostname_resolution_result)
begin
resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM)
hostname_resolution_result.add(family, resolved_addrinfos)
rescue => e
hostname_resolution_result.add(family, e)
end
end # File ext/socket/lib/socket.rb, line 974 def self.second_to_timeout(started_at, ends_at) return nil if ends_at == Float::INFINITY || ends_at.nil? remaining = (ends_at - started_at) remaining.negative? ? 0 : remaining end
# File ext/socket/lib/socket.rb, line 915
def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:)
last_error = nil
ret = nil
local_addr_list = nil
if local_host != nil || local_port != nil
local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
end
Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai|
if local_addr_list
local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
next unless local_addr
else
local_addr = nil
end
begin
sock = local_addr ?
ai.connect_from(local_addr, timeout: connect_timeout) :
ai.connect(timeout: connect_timeout)
rescue SystemCallError
last_error = $!
next
end
ret = sock
break
}
unless ret
if last_error
raise last_error
else
raise SocketError, "no appropriate local address"
end
end
ret
end # File ext/socket/lib/socket.rb, line 1559 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
Общедоступные методы экземпляра
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));
} Принимает следующее соединение. Возвращает новый объект Socket и объект Addrinfo.
serv = Socket.new(:INET, :STREAM, 0) serv.listen(5) c = Socket.new(:INET, :STREAM, 0) c.connect(serv.connect_address) p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>]
# 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. Поэтому для перехвата исключений и повторного вызова accept_nonblock можно использовать IO::WaitReadable.
Указав ключевой аргумент 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— sockaddr в форматеstruct, содержащийся в строке или объекте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— sockaddr в форматеstruct, содержащийся в строке или объекте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: проверить 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 1638 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
Запрашивает установление соединения с указанным remote_sockaddr после установки O_NONBLOCK для базового файлового дескриптора. В случае успеха возвращает 0, в противном случае вызывает исключение.
Параметр
-
remote_sockaddr— sockaddr в форматеstruct, содержащийся в строке или объекте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. Поэтому для перехвата исключений и повторного вызова connect_nonblock можно использовать IO::WaitWritable.
Указав ключевой аргумент 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-подобных системах приведённый выше пример работает, потому что новая структура sockaddr создаётся по адресу ADDR_ANY с произвольным номером порта, назначенным ядром. В 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, указан неизвестный флаг, указанMSG_OOBдля сокета с включённым параметромSO_OOBINLINEлибо (только для потоковых сокетов) внутренний параметр 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
Сведения об исключениях, которые могут быть выброшены при ошибке вызова recvfrom_nonblock, см. в разделе Socket#recvfrom.
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
Сведения об исключениях, которые могут быть выброшены при ошибке вызова sysaccept, см. в разделе Socket#accept.
См. также
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.