Spec-Zone.ru › Ruby 3.4

класс Socket

Родитель:
BasicSocket

Class Socket предоставляет доступ к реализациям сокетов операционной системы. Его можно использовать для предоставления функциональности, более специфичной для операционной системы, чем для протокол-специфических классов сокетов.

Константы, определенные в Socket::Constants, также определены в Socket. Например, Socket::AF_INET можно использовать так же, как и Socket::Constants::AF_INET. Список констант см. в Socket::Constants.

Что такое сокет?

Сокеты — это конечные точки двунаправленного канала связи. Сокеты могут обмениваться данными внутри процесса, между процессами на одном компьютере или между различными компьютерами. Существует множество типов сокетов: TCPSocket, UDPSocket или UNIXSocket, например.

Сокеты имеют свою собственную терминологию:

домен: Семейство протоколов:

  • Socket::PF_INET

  • Socket::PF_INET6

  • Socket::PF_UNIX

  • и т.д.

тип: Тип связи между двумя конечными точками, обычно

  • Socket::SOCK_STREAM

  • Socket::SOCK_DGRAM.

протокол: Обычно ноль. Это может использоваться для идентификации варианта протокола.

имя хоста: Идентификатор сетевого интерфейса:

  • строка (имя хоста, 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-клиентский сокет

Socket.tcp, TCPSocket.open

TCP-серверный сокет

Socket.tcp_server_loop, TCPServer.open

UNIX-клиентский сокет

Socket.unix, UNIXSocket.open

UNIX-серверный сокет

Socket.unix_server_loop, UNIXServer.open

Документация составлена

  • Заком Денисом

  • Самом Робертсом

  • Программирование на 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

Максимальный размер имени интерфейса

END_OF_DOCUMENT_MARKER
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_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

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

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
END_OF_DOCUMENT_MARKER

Подтвердить корректность пути

MSG_CTRUNC

Утеряны управляющие данные до доставки

MSG_DONTROUTE

Отправить без использования таблиц маршрутизации

MSG_DONTWAIT

Данное сообщение должно быть неблокирующим

MSG_EOF

Данные завершают соединение

MSG_EOR

Данные завершают запись

MSG_ERRQUEUE

Получить сообщение из очереди ошибок

MSG_FASTOPEN

Уменьшить этап процесса рукопожатия

MSG_FIN

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_RST

MSG_SEND

Отправить пакет из so_temp

MSG_SYN

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_AUTHENTICATION

SO_SECURITY_ENCRYPTION_NETWORK

SO_SECURITY_ENCRYPTION_NETWORK

SO_SECURITY_ENCRYPTION_TRANSPORT

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)

Методы публичного класса

accept_loop (*sockets) { |socket, client_addrinfo| ... }
Исходный код
# 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

Выполняет итерацию по приёму подключений через заданные сокеты, передавая сокет и адрес клиента в блок.

В качестве аргументов передаётся список сокетов. Каждый аргумент может быть сокетом или массивом сокетов.

Метод последовательно выполняет блок. Это означает, что следующее подключение не будет принято, пока блок не вернёт значение. Таким образом, для обслуживания нескольких клиентов одновременно необходимо использовать механизм одновременного выполнения, например, потоки.

getaddrinfo(nodename, servname[, family[, socktype[, protocol[, flags[, reverse_lookup]]]]]) → массив
Исходный код
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.

gethostbyaddr(address_string [, address_family]) → hostent
Исходный код
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"]
gethostbyname(hostname) → [official_hostname, alias_hostnames, address_family, *address_list]
Исходный код
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"]
gethostname → hostname
Исходный код
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.

getifaddrs → [ifaddr1, ...]
Исходный код
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)>]
getnameinfo(sockaddr [, flags]) → [hostname, servicename]
Исходный код
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.

getservbyname(service_name) → port_number
getservbyname(service_name, protocol_name) → port_number
Исходный код
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
getservbyport(port [, protocol_name]) → service
Исходный код
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"
ip_address_list → массив
Исходный код
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>,
     ...]
new(domain, socktype [, protocol]) → сокет
Исходный код
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
sockaddr_in(порт, хост) → sockaddr
pack_sockaddr_in(порт, хост) → sockaddr
Исходный код
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"
sockaddr_un(путь) → sockaddr
pack_sockaddr_un(путь) → sockaddr
Исходный код
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..."
pair(домен, тип, протокол) → [сокет1, сокет2]
socketpair(домен, тип, протокол) → [сокет1, сокет2]
Исходный код
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"
sockaddr_in(порт, хост) → sockaddr
pack_sockaddr_in(порт, хост) → sockaddr
Исходный код
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"
sockaddr_un(путь) → sockaddr
pack_sockaddr_un(путь) → sockaddr
Исходный код
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..."
pair(домен, тип, протокол) → [сокет1, сокет2]
socketpair(домен, тип, протокол) → [сокет1, сокет2]
Исходный код
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"
tcp(хост, порт, локальный_хост=nil, локальный_порт=nil, [opts]) {|сокет| ... }
tcp(хост, порт, локальный_хост=nil, локальный_порт=nil, [opts])
Исходный код
# 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
}
tcp_fast_fallback → true или false
Исходный код
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=.

END_OF_DOCUMENT_MARKER
tcp_fast_fallback= → true или false
Исходный код
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, этот метод работает следующим образом с этим алгоритмом:

  1. Начать одновременное разрешение адресов IPv6 и IPv4.

  2. Начать соединение с одним из адресов, полученных первым.
    Если адреса IPv4 получены первыми, метод ожидает 50 мс разрешения имени IPv6, чтобы приоритизировать соединения IPv6.

  3. После начала попытки соединения подождать 250 мс установления соединения.
    Если соединение не установлено в течение этого времени, новое соединение запускается каждые 250 мс
    до тех пор, пока соединение не будет установлено или не останется кандидатов на адреса.
    (Хотя RFC 8305 строго указывает на сортировку адресов,
    этот метод только чередует адреса IPv6/IPv4 из-за проблем с производительностью)

  4. После установления соединения все остальные попытки соединения отменяются.

tcp_server_loop (host=nil, port) { |socket, client_addrinfo| ... }
Исходный код
# 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
  }
}
tcp_server_sockets (host=nil, port) { |sockets| ... }
Исходный код
# 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>]
}
tcp_with_fast_fallback (host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil)
Исходный код
# 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
udp_server_loop(port) {|msg, msg_src| ... }
udp_server_loop(host, port) {|msg, msg_src| ... }
Исходный код
# 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
}
udp_server_loop_on(sockets) {|msg, msg_src| ... }
Исходный код
# 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 подходит для аргумента.

Он вызывает блок для каждого полученного сообщения.

udp_server_recv(sockets) {|msg, msg_src| ... }
Исходный код
# 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| ... }
  }
}
udp_server_sockets([host, ] port)
Исходный код
# 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
  }
}
unix (path) { |socket| ... }
Исходный код
# 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
}
unix_server_loop (path) { |socket, client_addrinfo| ... }
Исходный код
# 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
}
unix_server_socket (path) { |s| ... }
Исходный код
# 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>
}
unpack_sockaddr_in(sockaddr) → [port, ip_address]
Исходный код
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"]
unpack_sockaddr_un(sockaddr) → path
Исходный код
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"

Методы частного класса

current_clock_time ()
Исходный код
# File ext/socket/lib/socket.rb, line 960
def self.current_clock_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
expired? (started_at, ends_at)
Исходный код
# File ext/socket/lib/socket.rb, line 973
def self.expired?(started_at, ends_at)
  second_to_timeout(started_at, ends_at)&.zero?
end
ip_address? (hostname)
Исходный код
# 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
resolve_hostname (family, host, port, hostname_resolution_result)
Исходный код
# 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
second_to_timeout (started_at, ends_at)
Исходный код
# 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
tcp_without_fast_fallback (host, port, local_host, local_port, connect_timeout:, resolv_timeout:)
Исходный код
# 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
unix_socket_abstract_name? (path)
Исходный код
# File ext/socket/lib/socket.rb, line 1550
def unix_socket_abstract_name?(path)
  /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path
end

Публичные методы экземпляра

accept → [client_socket, client_addrinfo]
Исходный код
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>]
accept_nonblock([options]) → [client_socket, client_addrinfo]
Исходный код
# 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 вместо этого.

См. также

  • Socket#accept

bind(local_sockaddr) → 0
Исходный код
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 - struct sockaddr, содержащийся в строке или объект 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

connect(remote_sockaddr) → 0
Исходный код
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 - struct sockaddr, содержащийся в строке или объекте 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 Майкрософт

connect_nonblock(remote_sockaddr, [options]) → 0
Исходный код
# 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 - struct sockaddr, содержащийся в строке или объекте 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.

См. также

  • Socket#connect

ipv6only! ()
Исходный код
# 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 доступна.

listen( int ) → 0
Исходный код
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 Майкрософт

recvfrom(maxlen) → [mesg, sender_addrinfo]
recvfrom(maxlen, flags) → [mesg, sender_addrinfo]
Исходный код
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.

END_OF_DOCUMENT_MARKER
recvfrom_nonblock(maxlen[, flags[, outbuf[, opts]]]) → [mesg, sender_addrinfo]
Исходный код
# 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.

См. также

  • Socket#recvfrom

sysaccept → [client_socket_fd, client_addrinfo]
Исходный код
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 завершится неудачно.

См. также

  • Socket#accept

Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API