класс Socket
Class 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
Реализация Ruby’s Socket генерирует исключения на основе ошибки, сгенерированной реализацией, зависимой от системы. Именно поэтому методы документированы таким образом, чтобы изолировать исключения систем, основанных на Unix, от исключений, основанных на Windows. Если нужна дополнительная информация об определённом исключении, обратитесь к страницам руководства Unix или справочнику WinSock Windows.
Удобные методы
Хотя общий способ создания сокета — Socket.new, для большинства случаев существуют несколько способов создания сокета.
- TCP-клиентский сокет
- TCP-серверный сокет
- UNIX-клиентский сокет
- UNIX-серверный сокет
Документация составлена
-
Заком Денисом
-
Самом Робертсом
-
Программирование на Ruby от The Pragmatic Bookshelf.
Большая часть материала в этой документации взята с разрешения из Программирования на Ruby от The Pragmatic Bookshelf.
Константы
- ADDRESS_FAMILIES
- AF_ALG
-
Интерфейс к ядру крипто API
- AF_APPLETALK
-
Протокол AppleTalk
- AF_ATM
-
Режим асинхронной передачи
- AF_AX25
-
Протокол AX.25
- AF_BLUETOOTH
-
Протокол Bluetooth низкого уровня
- AF_CAN
-
Протокол автобуса CAN автомобильной сети
- 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 Direct Data Link Interface
- 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
-
Протокол локального транспорта
- AF_LINK
-
Интерфейс уровня канала связи
- AF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- AF_LOCAL
-
Протоколы внутри хоста
- AF_MAX
-
Максимальная семейство адресов для этой платформы
- AF_MPLS
-
Многопротокольный коммутируемый обмен метками
- AF_NATM
-
Нативный доступ ATM
- AF_NDRV
-
Прямой доступ к драйверу сети
- AF_NETBIOS
-
NetBIOS
- AF_NETGRAPH
-
Сокеты Netgraph
- AF_NETLINK
-
Устройство интерфейса ядро-пользователь
- AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы ISO Модель взаимодействия открытых систем
- AF_PACKET
-
Прямой доступ к уровню канала связи
- AF_PPP
-
Протокол точка-точка
- AF_PPPOX
-
Общий транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- AF_PUP
-
Протокол PARC Universal Packet
- AF_RDS
-
Протокол надёжных дейтаграммных сокетов (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 (быстрый путь данных)
- AI_ADDRCONFIG
-
Принимать только если какой-либо адрес назначен
- AI_ALL
-
Разрешить все адреса
- AI_CANONNAME
-
Заполнить каноническое имя
- AI_DEFAULT
-
Флаги по умолчанию для getaddrinfo
- AI_MASK
-
Допустимая маска флагов для getaddrinfo (не для использования приложения)
- AI_NUMERICHOST
-
Запретить разрешение имени хоста
- AI_NUMERICSERV
-
Запретить разрешение имени службы
- AI_PASSIVE
-
Получить адрес для использования с bind()
- AI_V4MAPPED
-
Принимать IPv4-отображаемые IPv6 адреса
- AI_V4MAPPED_CFG
-
Принимать IPv4-отображаемые адреса, если ядро их поддерживает
- CONNECTION_ATTEMPT_DELAY
- EAI_ADDRFAMILY
-
Семейство адресов для имени хоста не поддерживается
- EAI_AGAIN
-
Временное 실패 в разрешении имени
- EAI_BADFLAGS
-
Неверные флаги
- EAI_BADHINTS
-
Неверное значение для подсказок
- 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
-
Получать все пакеты multicast
- IFF_ALTPHYS
-
Использовать альтернативное физическое соединение
- IFF_AUTOMEDIA
-
Автоматический выбор среды активен
- IFF_BONDING
-
Мастер или раб соединения
- IFF_BRIDGE_PORT
-
Устройство используется как порт моста
- IFF_BROADCAST
-
Адрес broadcast валиден
- IFF_CANTCHANGE
-
Флаги не изменяемы
- IFF_CANTCONFIG
-
Не настраиваемо с помощью ioctl(2)
- IFF_DEBUG
-
Включить отладку
- IFF_DISABLE_NETPOLL
-
Отключить netpoll во время работы
- IFF_DONT_BRIDGE
-
Запретить создание моста этого ether dev
- IFF_DORMANT
-
Драйвер сигнализирует о спящем режиме
- IFF_DRV_OACTIVE
-
Очередь tx аппаратного обеспечения заполнена
- IFF_DRV_RUNNING
-
Ресурсы выделены
- IFF_DYING
-
Интерфейс завершает работу
- IFF_DYNAMIC
-
Устройство dialup с изменяемыми адресами
- IFF_EBRIDGE
-
Устройство ethernet bridging
- 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
-
Мастер соединения, 802.3ad
- IFF_MASTER_ALB
-
Мастер соединения, балансировка ALB
- IFF_MASTER_ARPMON
-
Мастер соединения, ARP мониторинг в использовании
- IFF_MONITOR
-
Режим мониторинга по запросу пользователя
- IFF_MULTICAST
-
Поддержка multicast
- IFF_NOARP
-
Отсутствует протокол разрешения адресов
- IFF_NOTRAILERS
-
Избегать использования трейлеров
- IFF_OACTIVE
-
Передача в процессе
- IFF_OVS_DATAPATH
-
Устройство используется как порт Open vSwitch datapath
- IFF_POINTOPOINT
-
Точечно-точечное соединение
- IFF_PORTSEL
-
Может задать тип среды
- IFF_PPROMISC
-
Режим promiscuous по запросу пользователя
- IFF_PROMISC
-
Принимать все пакеты
- IFF_RENAMING
-
Интерфейс переименовывается
- IFF_ROUTE
-
Маршрутная запись установлена
- IFF_RUNNING
-
Ресурсы выделены
- IFF_SIMPLEX
-
Не может принимать собственные передачи
- IFF_SLAVE
-
Рабочая станция балансировщика нагрузки
- IFF_SLAVE_INACTIVE
-
Подчинённый элемент соединения не активен
- IFF_SLAVE_NEEDARP
-
Требуются ARPs для проверки
- IFF_SMART
-
Интерфейс управляет собственными маршрутами
- IFF_STATICARP
-
Статический ARP
- IFF_SUPP_NOFCS
-
Отправка пользовательского FCS
- IFF_TEAM_PORT
-
Используется как порт команды
- 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
-
Минимальный стандартный адрес для привязки или подключения
- IPPORT_USERRESERVED
-
Максимальный стандартный адрес для привязки или подключения
- 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 net disk
- IPPROTO_NONE
-
IP6 без заголовка следующего уровня
- IPPROTO_PUP
-
Протокол универсальных пакетов PARC
- IPPROTO_RAW
-
Необработанный пакет IP
- IPPROTO_ROUTING
-
IP6 заголовок маршрутизации
- IPPROTO_TCP
-
TCP
- IPPROTO_TP
-
Класс 4 ISO транспортного протокола
- IPPROTO_UDP
-
UDP
- IPPROTO_XTP
-
Протокол Xpress Transport
- IPV6_ADRESS_FORMAT
- IPV6_CHECKSUM
-
Смещение контрольной суммы для необработанных сокетов
- IPV6_DONTFRAG
-
Не фрагментировать пакеты
- IPV6_DSTOPTS
-
Опция назначения
- IPV6_HOPLIMIT
-
Предел переходов
- IPV6_HOPOPTS
-
Опция по шагам
- IPV6_JOIN_GROUP
-
Присоединение к членству в группе
- IPV6_LEAVE_GROUP
-
Выход из членства в группе
- IPV6_MTU_DISCOVER
-
Открытие максимального размера блока
- IPV6_MULTICAST_HOPS
-
IP6 переходы мультикаста
- IPV6_MULTICAST_IF
-
IP6 интерфейс мультикаста
- IPV6_MULTICAST_LOOP
-
IP6 петля обратной связи мультикаста
- IPV6_NEXTHOP
-
Следующий адрес перехода
- IPV6_PATHMTU
-
Получить текущий максимальный размер блока пути
- IPV6_PKTINFO
-
Получить информацию о пакете с помощью датаграммы
- IPV6_RECVDSTOPTS
-
Получить все IP6 опции для ответа
- IPV6_RECVERR
-
Включить расширенную передачу надежных сообщений об ошибках
- IPV6_RECVHOPLIMIT
-
Получить предел переходов с помощью датаграммы
- IPV6_RECVHOPOPTS
-
Получить опции по шагам
- IPV6_RECVPATHMTU
-
Получить текущий максимальный размер блока пути с помощью датаграммы
- IPV6_RECVPKTINFO
-
Получить адрес назначения IP и входящий интерфейс
- IPV6_RECVRTHDR
-
Получить заголовок маршрутизации
- IPV6_RECVTCLASS
-
Получить класс трафика
- IPV6_RTHDR
-
Разрешает удаление заголовков маршрутизации sticky
- IPV6_RTHDRDSTOPTS
-
Разрешает удаление заголовка sticky опций назначения
- IPV6_RTHDR_TYPE_0
-
Тип заголовка маршрутизации 0
- IPV6_TCLASS
-
Указать класс трафика
- IPV6_UNICAST_HOPS
-
IP6 переходы одноадресной рассылки
- IPV6_USE_MIN_MTU
-
Использовать минимальный размер MTU
- IPV6_V6ONLY
-
Привязка только к IPv6 с помощью маски wild card
- IPX_TYPE
- IP_ADD_MEMBERSHIP
-
Добавить членство в группе мультикаста
- IP_ADD_SOURCE_MEMBERSHIP
-
Добавить членство в группе мультикаста
- IP_BLOCK_SOURCE
-
Заблокировать пакеты IPv4 мультикаста с заданным адресом источника
- IP_DEFAULT_MULTICAST_LOOP
-
Стандартная петля обратной связи мультикаста
- IP_DEFAULT_MULTICAST_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
-
Открытие максимального размера блока пути
- IP_MULTICAST_IF
-
Интерфейс IP мультикаста
- IP_MULTICAST_LOOP
-
Петля обратной связи IP мультикаста
- IP_MULTICAST_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
-
Получить 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
-
Подключение блокируется до подтверждения
- 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
-
FQDN не требуется для локальных хостов, вернуть только локальную часть
- NI_NUMERICHOST
-
Вернуть числовой адрес
- NI_NUMERICSERV
-
Вернуть имя службы как строку цифр
- PF_ALG
-
Интерфейс к API криптографии ядра
- PF_APPLETALK
-
Протокол AppleTalk
- PF_ATM
-
Режим асинхронной передачи
- PF_AX25
-
Протокол AX.25
- PF_BLUETOOTH
-
Протокол сокетов низкого уровня Bluetooth
- PF_CAN
-
Протокол шины CAN автомобильной сети
- 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 Direct Data Link Interface
- 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
-
Протокол локальной транспортной сети
- PF_LINK
-
Интерфейс уровня канала связи
- PF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- PF_LOCAL
-
Внутрихостовые протоколы
- PF_MAX
-
Максимальная семейство адресов для данной платформы
- PF_MPLS
-
Многопротокольный коммутационный протокол с метками
- PF_NATM
-
Нативный доступ ATM
- PF_NDRV
-
Прямой доступ к драйверу сети
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Сокеты Netgraph
- PF_NETLINK
-
Устройство пользовательского интерфейса ядра
- PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы ISO модели взаимодействия открытых систем
- PF_PACKET
-
Прямой доступ к каналу связи
- PF_PIP
-
Помогает идентифицировать пакеты PIP
- PF_PPP
-
Протокол точка-точка
- PF_PPPOX
-
Общий транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- PF_PUP
-
Протокол PARC Universal Packet
- PF_RDS
-
Протокол надежных сокетов дейтаграмм (RDS)
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_RTIP
-
Помогает идентифицировать пакеты RTIP
- PF_SIP
-
Простой протокол Internet
- 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
-
Протокол быстрой передачи
- 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
-
Сокет 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
-
Установить netns для сокета (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
-
Не производить push последнего блока записи
- 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
-
Обработка дублированных подтверждений для thin-потоков (Linux 2.6.34, glibc 2.18)
- TCP_THIN_LINEAR_TIMEOUTS
-
Линейные таймауты для thin-потоков (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 1216
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, игнорируется, “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 — необязательный аргумент, должен быть протоколом, определённым в области. Если 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;
} Упаковывает порт и хост в строку 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;
} Упаковывает путь в строку 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;
} Создаёт пару сокетов, соединённых друг с другом.
домен должен быть домен связи, например: :INET, :INET6, :UNIX и т.д.
тип сокета должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
протокол должен быть определённым протоколом в домене, по умолчанию 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;
} Упаковывает порт и хост в строку 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;
} Упаковывает путь в строку 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;
} Создаёт пару сокетов, соединённых друг с другом.
домен должен быть домен связи, например: :INET, :INET6, :UNIX и т.д.
тип сокета должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
протокол должен быть определённым протоколом в домене, по умолчанию 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 создаёт новый сокет, подключённый к хосту:порту с использованием 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 глобально.
Если заданы локальный_хост:локальный_порт, сокет привязывается к ним.
Дополнительный последний аргумент 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 1272
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 сервер на порту и вызывает блок для каждого принятого соединения. Блок вызывается с сокетом и адресом клиента в виде объекта Addrinfo.
Если указан хост, он используется с портом для определения адресов сервера.
Сокет не закрывается при возврате блока. Поэтому приложение должно закрыть его явно.
Этот метод вызывает блок последовательно. Это означает, что следующее соединение не принимается до тех пор, пока не вернется блок. Таким образом, для обслуживания нескольких клиентов одновременно следует использовать конкурентный механизм, например, поток.
Обратите внимание, что 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 1172
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 серверные сокеты для хоста и порта. Хост является необязательным.
Если блок не задан, возвращается массив прослушивающих сокетов.
Если блок задан, блок вызывается с сокетами. Возвращается значение блока. Сокет закрывается при возврате этого метода.
Если порт равен 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
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_CONNECT_TIME)
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)
last_error = result
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?
raise last_error
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 1442
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 сервер на порту и вызывает блок для каждого полученного сообщения. Блок вызывается с сообщением и информацией об источнике.
Этот метод выделяет сокеты внутренне, используя порт. Если указан хост, он используется совместно с портом для определения адресов сервера.
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 1415
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 1388
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 пакеты от заданных сокетов. Для каждого полученного пакета вызывается блок.
Блок получает 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 1300
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 сервера.
Если блок не задан, возвращается массив сокетов.
Если блок задан, блок вызывается с сокетами. Возвращается значение блока. Сокеты закрываются при возврате этого метода.
Если порт равен нулю, выбирается какой-то порт. Но выбранный порт используется для всех сокетов.
# 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 1492
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 создает новый сокет, подключенный к пути, используя сокет 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 1579
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 установлен бит «липкость».
# 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 1522
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 960 def self.current_clock_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end
Исходный код
# File ext/socket/lib/socket.rb, line 973 def self.expired?(started_at, ends_at) second_to_timeout(started_at, ends_at)&.zero? end
Исходный код
# File ext/socket/lib/socket.rb, line 945
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 950
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 965 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 906
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 1550 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
Обратитесь к Socket#accept для получения информации об исключениях, которые могут быть выброшены, если вызов accept_nonblock завершится неудачей.
Socket#accept_nonblock может вызвать любую ошибку, соответствующую ошибке accept(2), включая Errno::EWOULDBLOCK.
Если исключение является Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED или Errno::EPROTO, оно расширяется с помощью IO::WaitReadable. Таким образом, IO::WaitReadable может использоваться для обработки исключений при повторной попытке accept_nonblock.
Указав именованный аргумент exception для false, вы можете указать, что accept_nonblock не должен вызывать исключение IO::WaitReadable, а возвращать символ :wait_readable вместо этого.
См. также
Исходный код
static VALUE
sock_bind(VALUE sock, VALUE addr)
{
VALUE rai;
rb_io_t *fptr;
SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
} Привязывается к указанному локальному адресу.
Параметр
-
local_sockaddr-structsockaddr, содержащийся в строке или объект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-structsockaddr, содержащийся в строке или объекте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 -
Addrinfoуже используется -
Errno::EADDRNOTAVAIL - указанный
Addrinfoнедоступен на локальной машине -
Errno::EAFNOSUPPORT - указанный
Addrinfoне является допустимым адресом для семейства адресов указанногоsocket -
Errno::EALREADY - для указанного сокета уже происходит соединение
-
Errno::EBADF -
socketне является допустимым дескриптором файла -
Errno::ECONNREFUSED - целевой
Addrinfoне ожидал подключения, отклонил запрос на подключение -
Errno::ECONNRESET - удаленный хост прервал запрос на подключение
-
Errno::EFAULT - доступ к
Addrinfoневозможен -
Errno::EHOSTUNREACH - целевой хост недоступен (вероятно, потому что хост выключен или удалённый маршрутизатор не может к нему подключиться)
-
Errno::EINPROGRESS - для
socketустановлен флаг O_NONBLOCK, и соединение не может быть установлено немедленно; соединение будет установлено асинхронно -
Errno::EINTR - попытка установить соединение была прервана доставкой сигнала, который был перехвачен; соединение будет установлено асинхронно
-
Errno::EISCONN - указанный
socketуже подключён -
Errno::EINVAL - длина адреса, используемого для
Addrinfo, не является допустимой длиной для семейства адресов или вAddrinfoприсутствует некорректное семейство -
Errno::ENAMETOOLONG - длина пути, который был разрешен, превысила PATH_MAX
-
Errno::ENETDOWN - локальный интерфейс, используемый для достижения пункта назначения, выключен
-
Errno::ENETUNREACH - маршрут к сети отсутствует
-
Errno::ENOBUFS - нет доступного буферного пространства
-
Errno::ENOSR - недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTSOCK - аргумент
socketне относится к сокету -
Errno::EOPNOTSUPP - вызывающий
socketпрослушивает и не может быть подключён -
Errno::EPROTOTYPE -
Addrinfoимеет другой тип, чем сокет, привязанный к указанному адресу удалённого узла -
Errno::ETIMEDOUT - попытка подключения истекла, прежде чем подключение было установлено.
В системах на базе Unix, если семейство адресов вызывающего socket является AF_UNIX, при неудаче вызова connect могут быть сгенерированы следующие исключения:
-
Errno::EIO - произошла ошибка ввода-вывода при чтении или записи в файловую систему
-
Errno::ELOOP - при преобразовании пути в
Addrinfoбыло обнаружено слишком много символических ссылок -
Errno::ENAMETOOLLONG - компонент пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов
-
Errno::ENOENT - компонент пути не указывает на существующий файл или путь является пустой строкой
-
Errno::ENOTDIR - компонент префикса пути в
Addrinfoне является каталогом
Исключения в Windows
В системах Windows могут быть сгенерированы следующие системные исключения, если вызов connect завершился неудачей:
-
Errno::ENETDOWN - сеть не активна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется
-
Errno::EINTR - сокет был отменён
-
Errno::EINPROGRESS - блокирующий сокет находится в процессе обработки, или поставщик услуг всё ещё обрабатывает функцию обратного вызова. Или неблокирующий вызов connect выполняется на
socket. -
Errno::EALREADY - см. Errno::EINVAL
-
Errno::EADDRNOTAVAIL - удалённый адрес не является допустимым, например, ADDR_ANY TODO проверить ADDRANY до
INADDR_ANY -
Errno::EAFNOSUPPORT - адреса в указанном семействе не могут быть использованы с данным
socket -
Errno::ECONNREFUSED - целевой
Addrinfoне ожидал подключения, отклонил запрос на подключение -
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 Майкрософт
Исходный код
# File ext/socket/lib/socket.rb, line 1629 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
Запрашивает установление соединения с указанным remote_sockaddr после установки флага O_NONBLOCK для базового дескриптора файла. Возвращает 0 в случае успеха, в противном случае генерируется исключение.
Параметр
-
remote_sockaddr-structsockaddr, содержащийся в строке или объекте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
Обратитесь к Socket#connect для получения списка исключений, которые могут быть сгенерированы, если вызов connect_nonblock завершился неудачей.
Socket#connect_nonblock может генерировать любые ошибки, соответствующие ошибке connect(2), включая Errno::EINPROGRESS.
Если исключение Errno::EINPROGRESS, оно дополняется IO::WaitWritable. Поэтому IO::WaitWritable может быть использована для перехвата исключений для повторной попытки connect_nonblock.
Указав ключевой аргумент exception в false, вы можете указать, что connect_nonblock не должен генерировать исключение IO::WaitWritable, а вместо этого должен возвращать символ :wait_writable.
См. также
Исходный код
# File ext/socket/lib/socket.rb, line 468
def ipv6only!
if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
end
end Включает опцию сокета IPV6_V6ONLY, если IPV6_V6ONLY доступна.
Исходный код
VALUE
rsock_sock_listen(VALUE sock, VALUE log)
{
rb_io_t *fptr;
int backlog;
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
rb_sys_fail("listen(2)");
return INT2FIX(0);
} Прослушивает подключения, используя указанный int в качестве очереди ожидающих подключений. Вызов listen применяется только если socket имеет тип SOCK_STREAM или SOCK_SEQPACKET.
Параметр
-
backlog- максимальная длина очереди ожидающих подключений.
Пример 1
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
Пример 2 (прослушивание на произвольном порту, только системы на Unix):
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
Исключения для систем на Unix
В системах на основе Unix вышеуказанный код будет работать, так как новый sockaddr создаётся по адресу ADDR_ANY для произвольного номера порта, переданного ядром. Он не будет работать в Windows, так как Windows требует, чтобы socket был привязан вызовом bind перед вызовом listen.
Если значение backlog превышает максимальную длину очереди, зависящую от реализации, будет использоваться максимальная длина очереди реализации.
В системах на основе Unix при неудачном вызове listen могут быть выброшены следующие системные исключения:
-
Errno::EBADF - аргумент socket не является допустимым дескриптором файла
-
Errno::EDESTADDRREQ - сокет не привязан к локальному адресу, и протокол не поддерживает прослушивание на не привязанном сокете
-
Errno::EINVAL - сокет уже подключён
-
Errno::ENOTSOCK - аргумент socket не ссылается на сокет
-
Errno::EOPNOTSUPP - протокол сокета не поддерживает listen
-
Errno::EACCES - у вызывающего процесса нет соответствующих привилегий
-
Errno::EINVAL - сокет был закрыт
-
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 Майкрософт
Исходный код
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 - соединение было прервано из-за активности проверки поддержания соединения, обнаружившей ошибку во время выполнения операции.
-
Errno::EOPNOTSUPP -
MSG_OOBбыл указан, ноsocketне является потоковым, например типаSOCK_STREAM. Данные вне очереди не поддерживаются в области связи, связанной сsocket, илиsocketоднонаправленный и поддерживает только операции отправки. -
Errno::ESHUTDOWN -
socketбыл закрыт. Невозможно вызвать recvfrom для сокета после вызова shutdown. -
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— хэш ключевых слов, поддерживающий «исключение: false»
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
client, client_addrinfo = socket.accept
begin # emulate blocking recvfrom
pair = client.recvfrom_nonblock(20)
rescue IO::WaitReadable
IO.select([client])
retry
end
data = pair[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Watch this get cut short!"
socket.close
Обратитесь к Socket#recvfrom для получения информации об исключениях, которые могут возникнуть, если вызов recvfrom_nonblock завершится неудачно.
Socket#recvfrom_nonblock может вызвать любые ошибки, соответствующие неудачному вызову recvfrom(2), включая Errno::EWOULDBLOCK.
Если исключение — Errno::EWOULDBLOCK или Errno::EAGAIN, оно расширяется IO::WaitReadable. Таким образом, IO::WaitReadable можно использовать для перехвата исключений и повторной попытки вызова recvfrom_nonblock.
Указав ключевой аргумент exception в false, вы можете указать, что recvfrom_nonblock не должен вызывать исключение IO::WaitReadable, а вместо этого должен возвращать символ :wait_readable.
См. также
Исходный код
static VALUE
sock_sysaccept(VALUE server)
{
union_sockaddr buffer;
socklen_t length = (socklen_t)sizeof(buffer);
VALUE peer = rsock_s_accept(0, server, &buffer.addr, &length);
return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
} Принимает входящее соединение, возвращая массив, содержащий дескриптор файла (целое число) входящего соединения, client_socket_fd, и Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client_fd, client_addrinfo = socket.sysaccept
client_socket = Socket.for_fd( client_fd )
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
Обратитесь к Socket#accept для получения информации об исключениях, которые могут возникнуть, если вызов sysaccept завершится неудачно.
См. также
Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.