Spec-Zone.ru › Ruby 3.4

класс Socket

Родительский класс:
BasicSocket

Класс 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: обработка

Реализация Socket в Ruby генерирует исключения на основе ошибки, возникшей при реализации, зависящей от системы. Поэтому методы описаны таким образом, чтобы отделить исключения систем на базе 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

Авторы документации

  • Zach Dennis

  • Sam Roberts

  • Programming Ruby издательства The Pragmatic Bookshelf.

Значительная часть материала этой документации использована с разрешения из книги Programming Ruby издательства The Pragmatic Bookshelf.

Константы

ADDRESS_FAMILIES
AF_ALG

Интерфейс к криптографическому API ядра

AF_APPLETALK

Протокол AppleTalk

AF_ATM

Асинхронный режим передачи

AF_AX25

Протокол AX.25

AF_BLUETOOTH

Низкоуровневый протокол сокетов Bluetooth

AF_CAN

Протокол автомобильной шины Controller Area Network

AF_CCITT

Протоколы CCITT (ныне ITU-T)

AF_CHAOS

Протоколы MIT CHAOS

AF_CNT

Технология компьютерных сетей

AF_COIP

IP с установлением соединения

AF_DATAKIT

Протокол Datakit

AF_DEC

Протокол DECnet

AF_DECnet

Протокол DECnet

AF_DLI

Протокол интерфейса прямой передачи данных DEC

AF_E164

Рекомендация CCITT (ITU-T) E.164

AF_ECMA

Протоколы Европейской ассоциации производителей компьютеров

AF_HYLINK

Протокол NSC Hyperchannel

AF_IB

Собственная адресация InfiniBand

AF_IMPLINK

Протокол ARPANET IMP

AF_INET

Протокол IPv4

AF_INET6

Протокол IPv6

AF_IPX

Протокол IPX

AF_ISDN

Цифровая сеть с интеграцией служб

AF_ISO

Протоколы взаимодействия открытых систем ISO

AF_KCM

Интерфейс KCM (мультиплексор соединений ядра)

AF_KEY

Протокол управления ключами, изначально разработанный для использования с IPsec

AF_LAT

Протокол Local Area Transport

AF_LINK

Интерфейс канального уровня

AF_LLC

Протокол управления логическим каналом (IEEE 802.2 LLC)

AF_LOCAL

Внутренние протоколы узла

AF_MAX

Максимальное семейство адресов для этой платформы

AF_MPLS

Многопротокольная коммутация по меткам

AF_NATM

Прямой доступ к ATM

AF_NDRV

Прямой доступ к сетевому драйверу

AF_NETBIOS

NetBIOS

AF_NETGRAPH

Сокеты Netgraph

AF_NETLINK

Устройство интерфейса ядра с пользовательским пространством

AF_NS

Протоколы XEROX NS

AF_OSI

Протоколы взаимодействия открытых систем ISO

AF_PACKET

Прямой доступ к канальному уровню

AF_PPP

Протокол PPP

AF_PPPOX

Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)

AF_PUP

Протокол PARC Universal Packet

AF_RDS

Протокол надёжных датаграммных сокетов (RDS)

AF_ROUTE

Внутренний протокол маршрутизации

AF_SIP

Простой интернет-протокол

AF_SNA

Протокол IBM SNA

AF_SYSTEM

Сообщения о событиях ядра

AF_TIPC

TIPC, протокол «сокетов домена кластера»

AF_UNIX

Сокеты UNIX

AF_UNSPEC

Неуказанный протокол, любое поддерживаемое семейство адресов

AF_VSOCK

Протокол VSOCK (изначально «VMWare VSockets») для связи гипервизора с гостевой системой

AF_XDP

Интерфейс XDP (express data path)

AI_ADDRCONFIG

Принимать только при наличии назначенного адреса

AI_ALL

Разрешить все адреса

AI_CANONNAME

Заполнить каноническое имя

AI_DEFAULT

Флаги по умолчанию для getaddrinfo

AI_MASK

Допустимая маска флагов для getaddrinfo (не предназначена для использования приложениями)

AI_NUMERICHOST

Запретить разрешение имени узла

AI_NUMERICSERV

Запретить разрешение имени службы

AI_PASSIVE

Получить адрес для использования с bind()

AI_V4MAPPED

Принимать адреса IPv6, отображённые из IPv4

AI_V4MAPPED_CFG

Принимать отображённые адреса IPv4, если ядро поддерживает эту возможность

CONNECTION_ATTEMPT_DELAY
EAI_ADDRFAMILY

Семейство адресов не поддерживается для имени узла

EAI_AGAIN

Временный сбой при разрешении имени

EAI_BADFLAGS

Недопустимые флаги

EAI_BADHINTS

Недопустимое значение hints

EAI_FAIL

Неустранимый сбой при разрешении имени

EAI_FAMILY

Семейство адресов не поддерживается

EAI_MAX

Максимальный код ошибки getaddrinfo

EAI_MEMORY

Сбой выделения памяти

EAI_NODATA

С именем узла не связан ни один адрес

EAI_NONAME

Имя узла или службы не указано либо неизвестно

EAI_OVERFLOW

Переполнение буфера аргумента

EAI_PROTOCOL

Разрешённый протокол неизвестен

EAI_SERVICE

Имя службы не поддерживается для данного типа сокета

EAI_SOCKTYPE

Тип сокета не поддерживается

EAI_SYSTEM

Системная ошибка, возвращённая в errno

HOSTNAME_RESOLUTION_QUEUE_UPDATED
IFF_802_1Q_VLAN

Устройство VLAN 802.1Q

IFF_ALLMULTI

принимать все многоадресные пакеты

IFF_ALTPHYS

использовать альтернативное физическое соединение

IFF_AUTOMEDIA

автоматический выбор среды передачи включён

IFF_BONDING

ведущее или ведомое устройство bonding

IFF_BRIDGE_PORT

устройство используется как порт моста

IFF_BROADCAST

широковещательный адрес действителен

IFF_CANTCHANGE

флаги нельзя изменить

IFF_CANTCONFIG

нельзя настроить с помощью ioctl(2)

IFF_DEBUG

включить отладку

IFF_DISABLE_NETPOLL

отключить netpoll во время выполнения

IFF_DONT_BRIDGE

запретить использование этого Ethernet-устройства в мосту

IFF_DORMANT

драйвер сообщает о неактивном состоянии

IFF_DRV_OACTIVE

аппаратная очередь передачи заполнена

IFF_DRV_RUNNING

ресурсы выделены

IFF_DYING

интерфейс прекращает работу

IFF_DYNAMIC

устройство дозвона с изменяющимися адресами

IFF_EBRIDGE

устройство Ethernet-моста

IFF_ECHO

отправлять эхо-копии переданных пакетов

IFF_ISATAP

интерфейс ISATAP (RFC4214)

IFF_LINK0

бит 0, определяемый канальным уровнем

IFF_LINK1

бит 1, определяемый канальным уровнем

IFF_LINK2

бит 2, определяемый канальным уровнем

IFF_LIVE_ADDR_CHANGE

изменение аппаратного адреса во время работы

IFF_LOOPBACK

интерфейс обратной петли

IFF_LOWER_UP

драйвер сообщает, что уровень L1 активен

IFF_MACVLAN_PORT

устройство используется как порт macvlan

IFF_MASTER

ведущее устройство балансировщика нагрузки

IFF_MASTER_8023AD

ведущее устройство bonding, 802.3ad.

IFF_MASTER_ALB

ведущее устройство bonding, balance-alb.

IFF_MASTER_ARPMON

ведущее устройство bonding, используется мониторинг ARP

IFF_MONITOR

режим монитора, запрошенный пользователем

IFF_MULTICAST

поддерживает многоадресную рассылку

IFF_NOARP

без протокола разрешения адресов

IFF_NOTRAILERS

не использовать трейлеры

IFF_OACTIVE

выполняется передача

IFF_OVS_DATAPATH

устройство используется как порт datapath Open vSwitch

IFF_POINTOPOINT

соединение «точка-точка»

IFF_PORTSEL

можно задать тип среды передачи

IFF_PPROMISC

промискуитетный режим, запрошенный пользователем

IFF_PROMISC

принимать все пакеты

IFF_RENAMING

интерфейс переименовывается

IFF_ROUTE

маршрут установлен

IFF_RUNNING

ресурсы выделены

IFF_SIMPLEX

не может принимать собственные передачи

IFF_SLAVE

ведомое устройство балансировщика нагрузки

IFF_SLAVE_INACTIVE

ведомое устройство bonding не является текущим активным

IFF_SLAVE_NEEDARP

для проверки требуются ARP-запросы

IFF_SMART

интерфейс управляет собственными маршрутами

IFF_STATICARP

статический ARP

IFF_SUPP_NOFCS

отправка пользовательского FCS

IFF_TEAM_PORT

используется как порт team

IFF_TX_SKB_SHARING

совместное использование skb при передаче

IFF_UNICAST_FLT

фильтрация одноадресного трафика

IFF_UP

интерфейс активен

IFF_VOLATILE

изменяемые флаги

IFF_WAN_HDLC

устройство WAN HDLC

IFF_XMIT_DST_RELEASE

dev_hard_start_xmit() может освободить skb->dst

IFNAMSIZ

Максимальная длина имени интерфейса

IF_NAMESIZE

Максимальная длина имени интерфейса

INADDR_ALLHOSTS_GROUP

Группа многоадресной рассылки для всех систем этой подсети

INADDR_ANY

Сокет, привязанный к INADDR_ANY, получает пакеты от всех интерфейсов и отправляет их с IP-адреса по умолчанию

INADDR_BROADCAST

Сетевой широковещательный адрес

INADDR_LOOPBACK

Адрес обратной петли

INADDR_MAX_LOCAL_GROUP

Последняя локальная группа многоадресной рассылки

INADDR_NONE

Битовая маска для сопоставления с отсутствием допустимого IP-адреса

INADDR_UNSPEC_GROUP

Зарезервированная группа многоадресной рассылки

INET6_ADDRSTRLEN

Максимальная длина строки адреса IPv6

INET_ADDRSTRLEN

Максимальная длина строки адреса IPv4

IPPORT_RESERVED

Минимальный адрес по умолчанию для bind или connect

IPPORT_USERRESERVED

Максимальный адрес по умолчанию для bind или connect

IPPROTO_AH

Заголовок аутентификации IP6

IPPROTO_BIP

IPPROTO_BIP

IPPROTO_DSTOPTS

Параметр назначения IP6

IPPROTO_EGP

Протокол внешнего шлюза

IPPROTO_EON

ISO CNLP

IPPROTO_ESP

Инкапсулированная полезная нагрузка безопасности IP6

IPPROTO_FRAGMENT

Заголовок фрагментации IP6

IPPROTO_GGP

Протокол взаимодействия шлюзов

IPPROTO_HELLO

Протокол маршрутизации «hello»

IPPROTO_HOPOPTS

Параметры IP6 для каждого узла

IPPROTO_ICMP

Протокол управляющих сообщений

IPPROTO_ICMPV6

ICMP6

IPPROTO_IDP

XNS IDP

IPPROTO_IGMP

Протокол управления группами

IPPROTO_IP

Вспомогательный протокол для IP

IPPROTO_IPV6

Заголовок IP6

IPPROTO_MAX

Максимальная константа IPPROTO

IPPROTO_ND

Протокол сетевого диска Sun

IPPROTO_NONE

IP6 без следующего заголовка

IPPROTO_PUP

Протокол PARC Universal Packet

IPPROTO_RAW

Необработанный IP-пакет

IPPROTO_ROUTING

Заголовок маршрутизации IP6

IPPROTO_TCP

TCP

IPPROTO_TP

Транспортный протокол ISO класса 4

IPPROTO_UDP

UDP

IPPROTO_XTP

Транспортный протокол Xpress

IPV6_ADRESS_FORMAT
IPV6_CHECKSUM

Смещение контрольной суммы для необработанных сокетов

IPV6_DONTFRAG

Не фрагментировать пакеты

IPV6_DSTOPTS

Параметр назначения

IPV6_HOPLIMIT

Предел переходов

IPV6_HOPOPTS

Параметр для каждого узла

IPV6_JOIN_GROUP

Вступить в группу

IPV6_LEAVE_GROUP

Покинуть группу

IPV6_MTU_DISCOVER

Определение MTU пути

IPV6_MULTICAST_HOPS

Переходы многоадресной рассылки IP6

IPV6_MULTICAST_IF

Интерфейс многоадресной рассылки IP6

IPV6_MULTICAST_LOOP

Обратная передача многоадресной рассылки IP6

IPV6_NEXTHOP

Адрес следующего перехода

IPV6_PATHMTU

Получить текущий MTU пути

IPV6_PKTINFO

Получать сведения о пакете вместе с датаграммой

IPV6_RECVDSTOPTS

Получать все параметры IP6 для ответа

IPV6_RECVERR

Включить расширенную передачу надёжных сообщений об ошибках

IPV6_RECVHOPLIMIT

Получать предел переходов вместе с датаграммой

IPV6_RECVHOPOPTS

Получать параметры для каждого узла

IPV6_RECVPATHMTU

Получать текущий MTU пути вместе с датаграммой

IPV6_RECVPKTINFO

Получать IP-адрес назначения и входящий интерфейс

IPV6_RECVRTHDR

Получать заголовок маршрутизации

IPV6_RECVTCLASS

Получать класс трафика

IPV6_RTHDR

Позволяет удалять закреплённые заголовки маршрутизации

IPV6_RTHDRDSTOPTS

Позволяет удалять закреплённый заголовок параметров назначения

IPV6_RTHDR_TYPE_0

Тип 0 заголовка маршрутизации

IPV6_TCLASS

Указать класс трафика

IPV6_UNICAST_HOPS

Переходы одноадресной передачи IP6

IPV6_USE_MIN_MTU

Использовать минимальный размер MTU

IPV6_V6ONLY

При привязке к универсальному адресу использовать только IPv6

IPX_TYPE

IPX_TYPE

IP_ADD_MEMBERSHIP

Вступить в группу многоадресной рассылки

IP_ADD_SOURCE_MEMBERSHIP

Вступить в группу многоадресной рассылки

IP_BLOCK_SOURCE

Блокировать пакеты многоадресной рассылки IPv4 с указанным адресом источника

IP_DEFAULT_MULTICAST_LOOP

Обратная передача многоадресной рассылки по умолчанию

IP_DEFAULT_MULTICAST_TTL

TTL многоадресной рассылки по умолчанию

IP_DONTFRAG

Не фрагментировать пакеты

IP_DROP_MEMBERSHIP

Покинуть группу многоадресной рассылки

IP_DROP_SOURCE_MEMBERSHIP

Покинуть группу многоадресной рассылки

IP_FREEBIND

Разрешить привязку к несуществующим IP-адресам

IP_HDRINCL

Заголовок включён в данные

IP_IPSEC_POLICY

Политика безопасности IPsec

IP_MAX_MEMBERSHIPS

Максимальное число групп многоадресной рассылки, в которые может вступить сокет

IP_MINTTL

Минимальный допустимый TTL для полученных пакетов

IP_MSFILTER

Фильтрация источников многоадресной рассылки

IP_MTU

Максимальный размер блока передачи сокета

IP_MTU_DISCOVER

Определение MTU пути

IP_MULTICAST_IF

Интерфейс многоадресной рассылки IP

IP_MULTICAST_LOOP

Обратная передача многоадресной рассылки IP

IP_MULTICAST_TTL

TTL многоадресной рассылки IP

IP_ONESBCAST

Принудительно задавать исходящим широковещательным датаграммам направленный широковещательный адрес

IP_OPTIONS

Параметры IP, включаемые в пакеты

IP_PASSSEC

Получать контекст безопасности вместе с датаграммой

IP_PKTINFO

Получать сведения о пакете вместе с датаграммами

IP_PKTOPTIONS

Получать параметры пакета вместе с датаграммами

IP_PMTUDISC_DO

Всегда отправлять кадры с установленным DF

IP_PMTUDISC_DONT

Никогда не отправлять кадры с установленным DF

IP_PMTUDISC_WANT

Использовать подсказки для каждого маршрута

IP_PORTRANGE

Задать диапазон портов для сокетов с неуказанными номерами портов

IP_RECVDSTADDR

Получать IP-адрес назначения вместе с датаграммой

IP_RECVERR

Включить расширенную передачу надёжных сообщений об ошибках

IP_RECVIF

Получать сведения об интерфейсе вместе с датаграммами

IP_RECVOPTS

Получать все параметры IP вместе с датаграммой

IP_RECVRETOPTS

Получать все параметры IP для ответа

IP_RECVSLLA

Получать адрес канального уровня вместе с датаграммами

IP_RECVTOS

Получать TOS входящих пакетов

IP_RECVTTL

Получать TTL IP вместе с датаграммами

IP_RETOPTS

Параметры IP, включаемые в датаграммы

IP_ROUTER_ALERT

Уведомлять транзитные маршрутизаторы о необходимости более тщательно проверять содержимое IP-пакета

IP_SENDSRCADDR

Адрес источника исходящих датаграмм UDP

IP_TOS

Тип обслуживания IP

IP_TRANSPARENT

Прозрачный прокси-сервер

IP_TTL

Время жизни IP

IP_UNBLOCK_SOURCE

Разблокировать пакеты многоадресной рассылки IPv4 с указанным адресом источника

IP_XFRM_POLICY

IP_XFRM_POLICY

LOCAL_CONNWAIT

Вызов connect блокируется до принятия соединения

LOCAL_CREDS

Передавать учётные данные получателю

LOCAL_PEERCRED

Получить учётные данные узла-партнёра

MCAST_BLOCK_SOURCE

Блокировать пакеты многоадресной рассылки от этого источника

MCAST_EXCLUDE

Исключающий фильтр источников многоадресной рассылки

MCAST_INCLUDE

Включающий фильтр источников многоадресной рассылки

MCAST_JOIN_GROUP

Вступить в группу многоадресной рассылки

MCAST_JOIN_SOURCE_GROUP

Вступить в группу источников многоадресной рассылки

MCAST_LEAVE_GROUP

Покинуть группу многоадресной рассылки

MCAST_LEAVE_SOURCE_GROUP

Покинуть группу источников многоадресной рассылки

MCAST_MSFILTER

Фильтрация источников многоадресной рассылки

MCAST_UNBLOCK_SOURCE

Разблокировать пакеты многоадресной рассылки от этого источника

MSG_COMPAT

Конец записи

MSG_CONFIRM

Подтвердить допустимость маршрута

MSG_CTRUNC

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

MSG_DONTROUTE

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

MSG_DONTWAIT

Это сообщение должно обрабатываться без блокировки

MSG_EOF

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

MSG_EOR

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

MSG_ERRQUEUE

Извлечь сообщение из очереди ошибок

MSG_FASTOPEN

Сократить этап процедуры рукопожатия

MSG_FIN

MSG_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

Для локальных узлов полное доменное имя не требуется; возвращать только локальную часть

NI_NUMERICHOST

Возвращать числовой адрес

NI_NUMERICSERV

Возвращать имя службы в виде строки цифр

PF_ALG

Интерфейс к криптографическому API ядра

PF_APPLETALK

Протокол AppleTalk

PF_ATM

Асинхронный режим передачи

PF_AX25

Протокол AX.25

PF_BLUETOOTH

Низкоуровневый протокол сокетов Bluetooth

PF_CAN

Протокол автомобильной шины Controller Area Network

PF_CCITT

Протоколы CCITT (ныне ITU-T)

PF_CHAOS

Протоколы MIT CHAOS

PF_CNT

Технология компьютерных сетей

PF_COIP

IP с установлением соединения

PF_DATAKIT

Протокол Datakit

PF_DEC

Протокол DECnet

PF_DECnet

Протокол DECnet

PF_DLI

Протокол интерфейса прямой передачи данных DEC

PF_ECMA

Протоколы Европейской ассоциации производителей компьютеров

PF_HYLINK

Протокол NSC Hyperchannel

PF_IB

Собственная адресация InfiniBand

PF_IMPLINK

Протокол ARPANET IMP

PF_INET

Протокол IPv4

PF_INET6

Протокол IPv6

PF_IPX

Протокол IPX

PF_ISDN

Цифровая сеть с интеграцией служб

PF_ISO

Протоколы взаимодействия открытых систем ISO

PF_KCM

Интерфейс KCM (мультиплексор соединений ядра)

PF_KEY

Протокол управления ключами, изначально разработанный для использования с IPsec

PF_LAT

Протокол Local Area Transport

PF_LINK

Интерфейс канального уровня

PF_LLC

Протокол управления логическим каналом (IEEE 802.2 LLC)

PF_LOCAL

Внутренние протоколы узла

PF_MAX

Максимальное семейство адресов для этой платформы

PF_MPLS

Многопротокольная коммутация по меткам

PF_NATM

Прямой доступ к ATM

PF_NDRV

Прямой доступ к сетевому драйверу

PF_NETBIOS

NetBIOS

PF_NETGRAPH

Сокеты Netgraph

PF_NETLINK

Устройство интерфейса ядра с пользовательским пространством

PF_NS

Протоколы XEROX NS

PF_OSI

Протоколы взаимодействия открытых систем ISO

PF_PACKET

Прямой доступ к канальному уровню

PF_PIP

Помогает идентифицировать пакеты PIP

PF_PPP

Протокол PPP

PF_PPPOX

Универсальный транспортный уровень PPP для создания туннелей L2 (L2TP и PPPoE)

PF_PUP

Протокол PARC Universal Packet

PF_RDS

Протокол надёжных датаграммных сокетов (RDS)

PF_ROUTE

Внутренний протокол маршрутизации

PF_RTIP

Помогает идентифицировать пакеты RTIP

PF_SIP

Простой интернет-протокол

PF_SNA

Протокол IBM SNA

PF_SYSTEM

Сообщения о событиях ядра

PF_TIPC

TIPC, протокол «сокетов домена кластера»

PF_UNIX

Сокеты UNIX

PF_UNSPEC

Неуказанный протокол, любое поддерживаемое семейство адресов

PF_VSOCK

Протокол VSOCK (изначально «VMWare VSockets») для связи гипервизора с гостевой системой

PF_XDP

Интерфейс XDP (express data path)

PF_XTP

Протокол eXpress Transfer

RESOLUTION_DELAY
SCM_BINTIME

Метка времени (bintime)

SCM_CREDENTIALS

Учётные данные отправителя

SCM_CREDS

Учётные данные процесса

SCM_RIGHTS

Права доступа

SCM_TIMESTAMP

Метка времени (timeval)

SCM_TIMESTAMPING

Метка времени (список timespec) (Linux 2.6.30)

SCM_TIMESTAMPNS

Timespec (timespec)

SCM_UCRED

Учётные данные пользователя

SCM_WIFI_STATUS

Состояние Wi-Fi (Linux 3.3)

SHUT_RD

Отключить чтение из сокета

SHUT_RDWR

Отключить чтение и запись в сокете

SHUT_WR

Отключить запись в сокет

SOCK_CLOEXEC

Установить флаг close-on-exec (FD_CLOEXEC) для нового файлового дескриптора.

SOCK_DGRAM

Датаграммный сокет обеспечивает обмен сообщениями без установления соединения и без гарантии доставки

SOCK_NONBLOCK

Установить флаг состояния файла O_NONBLOCK для описания открытого файла (см. open(2)), на которое ссылается новый файловый дескриптор.

SOCK_PACKET

Доступ к пакетам на уровне устройства

SOCK_RAW

Сырой сокет обеспечивает низкоуровневый доступ для непосредственного обращения к сети или реализации сетевых протоколов

SOCK_RDM

Надёжный датаграммный сокет обеспечивает гарантированную доставку сообщений

SOCK_SEQPACKET

Сокет последовательных пакетов обеспечивает упорядоченное, надёжное двустороннее соединение для датаграмм

SOCK_STREAM

Потоковый сокет обеспечивает упорядоченное, надёжное двустороннее соединение для потока байтов

SOL_ATALK

Параметры сокета AppleTalk

SOL_AX25

Параметры сокета AX.25

SOL_IP

Параметры сокета IP

SOL_IPX

Параметры сокета IPX

SOL_SOCKET

Параметры уровня сокета

SOL_TCP

Параметры сокета TCP

SOL_UDP

Параметры сокета UDP

SOMAXCONN

Максимальное количество запросов на соединение, которые могут находиться в очереди сокета

SOPRI_BACKGROUND

Фоновый приоритет сокета

SOPRI_INTERACTIVE

Интерактивный приоритет сокета

SOPRI_NORMAL

Обычный приоритет сокета

SO_ACCEPTCONN

Для сокета был вызван listen()

SO_ACCEPTFILTER

Используется фильтр принятия соединений

SO_ALLZONES

Обходить границы зон

SO_ATTACH_FILTER

Присоединить фильтр принятия соединений

SO_BINDTODEVICE

Отправлять пакеты только через указанный интерфейс

SO_BINTIME

Получать временную метку датаграмм (bintime)

SO_BPF_EXTENSIONS

Запросить поддерживаемые расширения BPF (Linux 3.14)

SO_BROADCAST

Разрешить отправку широковещательных сообщений

SO_BUSY_POLL

Задать порог в микросекундах для опроса с низкой задержкой (Linux 3.11)

SO_CONNECT_TIME

Возвращает время в секундах, в течение которого сокет был подключён. Этот параметр действителен только для протоколов, ориентированных на соединение (Windows)

SO_DEBUG

Запись отладочной информации

SO_DETACH_FILTER

Отсоединить фильтр принятия соединений

SO_DOMAIN

Домен, указанный для socket() (Linux 2.6.32)

SO_DONTROUTE

Использовать адреса интерфейсов

SO_DONTTRUNC

Сохранять непрочитанные данные

SO_ERROR

Получить и сбросить состояние ошибки

SO_GET_FILTER

Получить фильтр, заданный параметром SO_ATTACH_FILTER (Linux 3.8)

SO_INCOMING_CPU

Получать идентификатор процессора, связанного с сокетом (Linux 3.19)

SO_INCOMING_NAPI_ID

Получать идентификатор NAPI, связанный с очередью RX (Linux 4.12)

SO_KEEPALIVE

Поддерживать соединения активными

SO_LINGER

Ожидать при закрытии, если имеются данные

SO_LOCK_FILTER

Заблокировать фильтр, присоединённый к сокету (Linux 3.9)

SO_MAC_EXEMPT

Исключение из обязательного контроля доступа для неразмеченных узлов

SO_MARK

Установить метку для маршрутизации на основе меток (Linux 2.6.25)

SO_MAX_PACING_RATE

Ограничить скорость, вычисляемую транспортным уровнем. [байт в секунду] (Linux 3.13)

SO_NKE

Установить расширение сетевого ядра на уровне сокета

SO_NOFCS

Задать сетевое пространство имён сокета (Linux 3.4)

SO_NOSIGPIPE

Не отправлять SIGPIPE при EPIPE

SO_NO_CHECK

Отключить контрольные суммы

SO_NREAD

Получить количество байтов в первом пакете

SO_OOBINLINE

Оставлять полученные внеполосные данные во входном потоке

SO_PASSCRED

Получать сообщения SCM_CREDENTIALS

SO_PASSSEC

Включить или отключить передачу контекста безопасности (Linux 2.6.18)

SO_PEEK_OFF

Задать смещение для просмотра данных (Linux 3.4)

SO_PEERCRED

Учётные данные удалённого процесса, подключённого к этому сокету

SO_PEERNAME

Имя подключающегося пользователя

SO_PEERSEC

Получить учётные данные безопасности (Linux 2.6.2)

SO_PRIORITY

Приоритет, определённый протоколом, для всех пакетов этого сокета

SO_PROTOCOL

Протокол, указанный для socket() (Linux 2.6.32)

SO_RCVBUF

Размер буфера приёма

SO_RCVBUFFORCE

Размер буфера приёма без ограничения rmem_max (Linux 2.6.14)

SO_RCVLOWAT

Нижний порог буфера приёма

SO_RCVTIMEO

Тайм-аут приёма

SO_RECVUCRED

Получать учётные данные пользователя вместе с датаграммой

SO_REUSEADDR

Разрешить повторное использование локального адреса

SO_REUSEPORT

Разрешить повторное использование локального адреса и порта

SO_RTABLE

Задать таблицу маршрутизации для этого сокета (OpenBSD)

SO_RXQ_OVFL

Включить или отключить cmsg с количеством отброшенных пакетов (Linux 2.6.33)

SO_SECURITY_AUTHENTICATION

SO_SECURITY_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

Не отправлять последний блок данных при записи

TCP_QUEUE_SEQ

Порядковый номер очереди для режима восстановления (Linux 3.5, glibc 2.18)

TCP_QUICKACK

Включить режим quickack (Linux 2.4.4, glibc 2.3)

TCP_REPAIR

Режим восстановления (Linux 3.5, glibc 2.18)

TCP_REPAIR_OPTIONS

Параметры режима восстановления (Linux 3.5, glibc 2.18)

TCP_REPAIR_QUEUE

Очередь режима восстановления (Linux 3.5, glibc 2.18)

TCP_SYNCNT

Количество повторных передач SYN до разрыва соединения (Linux 2.4, glibc 2.2)

TCP_THIN_DUPACK

Обработка дублирующихся подтверждений для тонких потоков (Linux 2.6.34, glibc 2.18)

TCP_THIN_LINEAR_TIMEOUTS

Линейные тайм-ауты для тонких потоков (Linux 2.6.34, glibc 2.18)

TCP_TIMESTAMP

Временная метка TCP (Linux 3.9, glibc 2.18)

TCP_USER_TIMEOUT

Максимальный тайм-аут до разрыва TCP-соединения (Linux 2.6.37, glibc 2.18)

TCP_WINDOW_CLAMP

Ограничить размер объявляемого окна (Linux 2.4, glibc 2.2)

UDP_CORK

Не отправлять неполные кадры (Linux 2.5.44, glibc 2.11)

Публичные методы класса

accept_loop (*sockets) { |socket, client_addrinfo| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1225
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
  sockets.flatten!(1)
  if sockets.empty?
    raise ArgumentError, "no sockets"
  end
  loop {
    readable, _, _ = IO.select(sockets)
    readable.each {|r|
      sock, addr = r.accept_nonblock(exception: false)
      next if sock == :wait_readable
      yield sock, addr
    }
  }
end

Передаёт в блок сокет и адрес клиента для каждого соединения, принятого через указанные сокеты.

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

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

getaddrinfo(nodename, servname[, family[, socktype[, protocol[, flags[, reverse_lookup]]]]]) → array Показать исходный код
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, ignored, “127.0.0.1”]

flags должно быть побитовым ИЛИ констант Socket::NI_*.

Примечание: последний формат совместим с IPSocket#addr и IPSocket#peeraddr.

Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1"))       #=> ["localhost", "www"]
Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"])              #=> ["localhost", "www"]
Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"]

Если предпочтительно использовать объект Addrinfo, воспользуйтесь Addrinfo#getnameinfo.

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 → array Показать исходный код
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]) → socket Показать исходный код
static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
    VALUE domain, type, protocol;
    int fd;
    int d, t;

    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);

    setup_domain_and_type(domain, &d, type, &t);
    fd = rsock_socket(d, t, NUM2INT(protocol));
    if (fd < 0) rb_sys_fail("socket(2)");

    return rsock_init_sock(sock, fd);
}

Создаёт новый объект сокета.

domain должно быть доменом связи, например: :INET, :INET6, :UNIX и т. д.

socktype должно быть типом сокета, например: :STREAM, :DGRAM, :RAW и т. д.

protocol является необязательным параметром и должно быть протоколом, определённым в домене. Если протокол не указан, внутренне используется значение 0.

Socket.new(:INET, :STREAM) # TCP socket
Socket.new(:INET, :DGRAM)  # UDP socket
Socket.new(:UNIX, :STREAM) # UNIX stream socket
Socket.new(:UNIX, :DGRAM)  # UNIX datagram socket
sockaddr_in(port, host) → sockaddr Показать исходный код
pack_sockaddr_in(port, host) → 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;
}

Упаковывает port и host в строку sockaddr формата AF_INET/AF_INET6.

Socket.sockaddr_in(80, "127.0.0.1")
#=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"

Socket.sockaddr_in(80, "::1")
#=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
sockaddr_un(path) → sockaddr Показать исходный код
pack_sockaddr_un(path) → 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;
}

Упаковывает path в строку sockaddr формата AF_UNIX.

Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
pair(domain, type, protocol) → [socket1, socket2] Показать исходный код
socketpair(domain, type, protocol) → [socket1, socket2]
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
    VALUE domain, type, protocol;
    int d, t, p, sp[2];
    int ret;
    VALUE s1, s2, r;

    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);

    setup_domain_and_type(domain, &d, type, &t);
    p = NUM2INT(protocol);
    ret = rsock_socketpair(d, t, p, sp);
    if (ret < 0) {
        rb_sys_fail("socketpair(2)");
    }

    s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
    s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
    r = rb_assoc_new(s1, s2);
    if (rb_block_given_p()) {
        return rb_ensure(pair_yield, r, io_close, s1);
    }
    return r;
}

Создаёт пару сокетов, соединённых друг с другом.

domain должно быть доменом связи, например: :INET, :INET6, :UNIX и т. д.

socktype должно быть типом сокета, например: :STREAM, :DGRAM, :RAW и т. д.

protocol должно быть протоколом, определённым в домене; по умолчанию используется значение 0 для этого домена.

s1, s2 = Socket.pair(:UNIX, :STREAM, 0)
s1.send "a", 0
s1.send "b", 0
s1.close
p s2.recv(10) #=> "ab"
p s2.recv(10) #=> ""
p s2.recv(10) #=> ""

s1, s2 = Socket.pair(:UNIX, :DGRAM, 0)
s1.send "a", 0
s1.send "b", 0
p s2.recv(10) #=> "a"
p s2.recv(10) #=> "b"
sockaddr_in(port, host) → sockaddr Показать исходный код
pack_sockaddr_in(port, host) → 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;
}

Упаковывает port и host в строку sockaddr формата AF_INET/AF_INET6.

Socket.sockaddr_in(80, "127.0.0.1")
#=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"

Socket.sockaddr_in(80, "::1")
#=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
sockaddr_un(path) → sockaddr Показать исходный код
pack_sockaddr_un(path) → 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;
}

Упаковывает path в строку sockaddr формата AF_UNIX.

Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
pair(domain, type, protocol) → [socket1, socket2] Показать исходный код
socketpair(domain, type, protocol) → [socket1, socket2]
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
    VALUE domain, type, protocol;
    int d, t, p, sp[2];
    int ret;
    VALUE s1, s2, r;

    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);

    setup_domain_and_type(domain, &d, type, &t);
    p = NUM2INT(protocol);
    ret = rsock_socketpair(d, t, p, sp);
    if (ret < 0) {
        rb_sys_fail("socketpair(2)");
    }

    s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
    s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
    r = rb_assoc_new(s1, s2);
    if (rb_block_given_p()) {
        return rb_ensure(pair_yield, r, io_close, s1);
    }
    return r;
}

Создаёт пару сокетов, соединённых друг с другом.

domain должно быть доменом связи, например: :INET, :INET6, :UNIX и т. д.

socktype должно быть типом сокета, например: :STREAM, :DGRAM, :RAW и т. д.

protocol должно быть протоколом, определённым в домене; по умолчанию используется значение 0 для этого домена.

s1, s2 = Socket.pair(:UNIX, :STREAM, 0)
s1.send "a", 0
s1.send "b", 0
s1.close
p s2.recv(10) #=> "ab"
p s2.recv(10) #=> ""
p s2.recv(10) #=> ""

s1, s2 = Socket.pair(:UNIX, :DGRAM, 0)
s1.send "a", 0
s1.send "b", 0
p s2.recv(10) #=> "a"
p s2.recv(10) #=> "b"
tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... } Показать исходный код
tcp(host, port, local_host=nil, local_port=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

создаёт новый объект сокета, подключённый к host:port по TCP/IP.

Начиная с Ruby 3.4, по умолчанию этот метод работает согласно алгоритму Happy Eyeballs Version 2 (RFC 8305).

Подробнее об алгоритме Happy Eyeballs Version 2 см. в разделе Socket.tcp_fast_fallback=.

Чтобы метод работал так же, как в Ruby 3.3 и более ранних версиях, явно укажите параметр fast_fallback:false. Также можно установить Socket.tcp_fast_fallback= в false, чтобы отключить Happy Eyeballs Version 2 не только для этого метода, но и глобально для всех Socket.

Если указан local_host:local_port, сокет привязывается к нему.

Необязательный последний аргумент opts — это параметры, представленные в виде хеша. opts может содержать следующие параметры:

:resolv_timeout

Задаёт время ожидания в секундах с момента начала разрешения имени хоста.

:connect_timeout

Этот метод последовательно пытается подключиться ко всем возможным адресам назначения.
Параметр connect_timeout задаёт время ожидания в секундах от начала попытки подключения до последнего возможного адреса.
По умолчанию все попытки подключения продолжаются до истечения времени ожидания.
Если явно указан параметр fast_fallback:false,
для каждой попытки подключения задаётся время ожидания, и попытка, превысившая его, отменяется.

:fast_fallback

Включает алгоритм Happy Eyeballs Version 2 (включён по умолчанию).

Если передан блок, он вызывается с сокетом. Возвращается значение блока. После завершения этого метода сокет закрывается.

Если блок не передан, возвращается сокет.

Socket.tcp("www.ruby-lang.org", 80) {|sock|
  sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
  sock.close_write
  puts sock.read
}
tcp_fast_fallback → true or 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=.

tcp_fast_fallback= → true or 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 1281
def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
  tcp_server_sockets(host, port) {|sockets|
    accept_loop(sockets, &b)
  }
end

создаёт сервер TCP/IP на port и вызывает блок для каждого принятого соединения. Блок вызывается с сокетом и адресом клиента в виде объекта Addrinfo.

Если указан host, он используется вместе с port для определения адресов сервера.

Сокет не закрывается после завершения блока. Поэтому приложение должно закрыть его явно.

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

Обратите внимание, что для определения адресов сокетов сервера используется Addrinfo.getaddrinfo. Если Addrinfo.getaddrinfo возвращает два или более адреса, например адреса IPv4 и IPv6, используются все они. Вызов Socket.tcp_server_loop завершается успешно, если можно использовать хотя бы один сокет.

# Sequential echo server.
# It services only one client at a time.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
  begin
    IO.copy_stream(sock, sock)
  ensure
    sock.close
  end
}

# Threaded echo server
# It services multiple clients at a time.
# Note that it may accept connections too much.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
  Thread.new {
    begin
      IO.copy_stream(sock, sock)
    ensure
      sock.close
    end
  }
}
tcp_server_sockets (host=nil, port) { |sockets| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1181
def self.tcp_server_sockets(host=nil, port)
  if port == 0
    sockets = tcp_server_sockets_port0(host)
  else
    last_error = nil
    sockets = []
    begin
      Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
        begin
          s = ai.listen
        rescue SystemCallError
          last_error = $!
          next
        end
        sockets << s
      }
      if sockets.empty?
        raise last_error
      end
    rescue Exception
      sockets.each(&:close)
      raise
    end
  end
  if block_given?
    begin
      yield sockets
    ensure
      sockets.each(&:close)
    end
  else
    sockets
  end
end

создаёт сокеты сервера TCP/IP для host и port. Параметр host необязателен.

Если блок не передан, возвращается массив прослушивающих сокетов.

Если передан блок, он вызывается с сокетами. Возвращается значение блока. После завершения этого метода сокеты закрываются.

Если port равен 0, номер порта выбирается динамически. Однако все сокеты в результате будут использовать один и тот же номер порта.

# tcp_server_sockets returns two sockets.
sockets = Socket.tcp_server_sockets(1296)
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]

# The sockets contains IPv6 and IPv4 sockets.
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:1296 TCP>
#   #<Addrinfo: 0.0.0.0:1296 TCP>

# IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
sockets = Socket.tcp_server_sockets(0)
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:53114 TCP>
#   #<Addrinfo: 0.0.0.0:53114 TCP>

# The block is called with the sockets.
Socket.tcp_server_sockets(0) {|sockets|
  p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
}
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
  last_error_from_thread = false

  if resolving_family_names.size == 1
    family_name = resolving_family_names.first
    addrinfos = Addrinfo.getaddrinfo(host, port, family_name, :STREAM, timeout: resolv_timeout)
    resolution_store.add_resolved(family_name, addrinfos)
    hostname_resolution_result = nil
    hostname_resolution_notifier = nil
    user_specified_resolv_timeout_at = nil
  else
    hostname_resolution_result = HostnameResolutionResult.new(resolving_family_names.size)
    hostname_resolution_notifier = hostname_resolution_result.notifier

    hostname_resolution_threads.concat(
      resolving_family_names.map { |family|
        thread_args = [family, host, port, hostname_resolution_result]
        thread = Thread.new(*thread_args) { |*thread_args| resolve_hostname(*thread_args) }
        Thread.pass
        thread
      }
    )

    user_specified_resolv_timeout_at = resolv_timeout ? now + resolv_timeout : Float::INFINITY
  end

  loop do
    if resolution_store.any_addrinfos? &&
        !resolution_delay_expires_at &&
        !connection_attempt_delay_expires_at
      while (addrinfo = resolution_store.get_addrinfo)
        if local_addrinfos.any?
          local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily }

          if local_addrinfo.nil? # Connecting addrinfoと同じアドレスファミリのLocal addrinfoがない
            if resolution_store.any_addrinfos?
              # Try other Addrinfo in next "while"
              next
            elsif connecting_sockets.any? || resolution_store.any_unresolved_family?
              # Exit this "while" and wait for connections to be established or hostname resolution in next loop
              # Or exit this "while" and wait for hostname resolution in next loop
              break
            else
              raise SocketError.new 'no appropriate local address'
            end
          end
        end

        begin
          if resolution_store.any_addrinfos? ||
             connecting_sockets.any? ||
             resolution_store.any_unresolved_family?
            socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
            socket.bind(local_addrinfo) if local_addrinfo
            result = socket.connect_nonblock(addrinfo, exception: false)
          else
            result = socket = local_addrinfo ?
              addrinfo.connect_from(local_addrinfo, timeout: connect_timeout) :
              addrinfo.connect(timeout: connect_timeout)
          end

          if result == :wait_writable
            connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY
            if resolution_store.empty_addrinfos?
              user_specified_connect_timeout_at = connect_timeout ? now + connect_timeout : Float::INFINITY
            end

            connecting_sockets[socket] = addrinfo
            break
          else
            return socket # connection established
          end
        rescue SystemCallError => e
          socket&.close
          last_error = e

          if resolution_store.any_addrinfos?
            # Try other Addrinfo in next "while"
            next
          elsif connecting_sockets.any? || resolution_store.any_unresolved_family?
            # Exit this "while" and wait for connections to be established or hostname resolution in next loop
            # Or exit this "while" and wait for hostname resolution in next loop
            break
          else
            raise last_error
          end
        end
      end
    end

    ends_at =
      if resolution_store.any_addrinfos?
        resolution_delay_expires_at || connection_attempt_delay_expires_at
      else
        [user_specified_resolv_timeout_at, user_specified_connect_timeout_at].compact.max
      end

    hostname_resolved, writable_sockets, except_sockets = IO.select(
      hostname_resolution_notifier,
      connecting_sockets.keys,
      # Use errorfds to wait for non-blocking connect failures on Windows
      is_windows_environment ? connecting_sockets.keys : nil,
      second_to_timeout(current_clock_time, ends_at),
    )
    now = current_clock_time
    resolution_delay_expires_at = nil if expired?(now, resolution_delay_expires_at)
    connection_attempt_delay_expires_at = nil if expired?(now, connection_attempt_delay_expires_at)

    if writable_sockets&.any?
      while (writable_socket = writable_sockets.pop)
        is_connected = is_windows_environment || (
          sockopt = writable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
          sockopt.int.zero?
        )

        if is_connected
          connecting_sockets.delete writable_socket
          return writable_socket
        else
          failed_ai = connecting_sockets.delete writable_socket
          writable_socket.close
          ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address
          last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int)

          if writable_sockets.any? || connecting_sockets.any?
            # Try other writable socket in next "while"
            # Or exit this "while" and wait for connections to be established or hostname resolution in next loop
          elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family?
            # Exit this "while" and try other connection attempt
            # Or exit this "while" and wait for hostname resolution in next loop
            connection_attempt_delay_expires_at = nil
            user_specified_connect_timeout_at = nil
          else
            raise last_error
          end
        end
      end
    end

    if except_sockets&.any?
      except_sockets.each do |except_socket|
        failed_ai = connecting_sockets.delete except_socket
        sockopt = except_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
        except_socket.close
        ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address
        last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int)

        if except_sockets.any? || connecting_sockets.any?
          # Cleanup other except socket in next "each"
          # Or exit this "while" and wait for connections to be established or hostname resolution in next loop
        elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family?
          # Exit this "while" and try other connection attempt
          # Or exit this "while" and wait for hostname resolution in next loop
          connection_attempt_delay_expires_at = nil
          user_specified_connect_timeout_at = nil
        else
          raise last_error
        end
      end
    end

    if hostname_resolved&.any?
      while (family_and_result = hostname_resolution_result.get)
        family_name, result = family_and_result

        if result.is_a? Exception
          resolution_store.add_error(family_name, result)

          unless (Socket.const_defined?(:EAI_ADDRFAMILY)) &&
            (result.is_a?(Socket::ResolutionError)) &&
            (result.error_code == Socket::EAI_ADDRFAMILY)
            other = family_name == :ipv6 ? :ipv4 : :ipv6
            if !resolution_store.resolved?(other) || !resolution_store.resolved_successfully?(other)
              last_error = result
              last_error_from_thread = true
            end
          end
        else
          resolution_store.add_resolved(family_name, result)
        end
      end

      if resolution_store.resolved?(:ipv4)
        if resolution_store.resolved?(:ipv6)
          hostname_resolution_notifier = nil
          resolution_delay_expires_at = nil
          user_specified_resolv_timeout_at = nil
        elsif resolution_store.resolved_successfully?(:ipv4)
          resolution_delay_expires_at = now + RESOLUTION_DELAY
        end
      end
    end

    if resolution_store.empty_addrinfos?
      if connecting_sockets.empty? && resolution_store.resolved_all_families?
        if last_error_from_thread
          raise last_error.class, last_error.message, cause: last_error
        else
          raise last_error
        end
      end

      if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) &&
         (expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?)
        raise Errno::ETIMEDOUT, 'user specified timeout'
      end
    end
  end
ensure
  hostname_resolution_threads.each do |thread|
    thread.exit
  end

  hostname_resolution_result&.close

  connecting_sockets.each_key do |connecting_socket|
    connecting_socket.close
  end
end
udp_server_loop(port) {|msg, msg_src| ... } Показать исходный код
udp_server_loop(host, port) {|msg, msg_src| ... }
# File ext/socket/lib/socket.rb, line 1451
def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
  udp_server_sockets(host, port) {|sockets|
    udp_server_loop_on(sockets, &b)
  }
end

создаёт сервер UDP/IP на port и вызывает блок для каждого полученного сообщения. Блок вызывается с сообщением и информацией об источнике.

Этот метод выделяет сокеты внутри себя, используя port. Если указан host, он используется вместе с port для определения адресов сервера.

msg — это строка.

msg_src — это объект Socket::UDPSource. Он используется для отправки ответа.

# UDP/IP echo server.
Socket.udp_server_loop(9261) {|msg, msg_src|
  msg_src.reply msg
}
udp_server_loop_on(sockets) {|msg, msg_src| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1424
def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
  loop {
    readable, _, _ = IO.select(sockets)
    udp_server_recv(readable, &b)
  }
end

Запускает цикл сервера UDP/IP на указанных сокетах.

Для этого аргумента подходит возвращаемое значение Socket.udp_server_sockets.

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

udp_server_recv(sockets) {|msg, msg_src| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1397
def self.udp_server_recv(sockets)
  sockets.each {|r|
    msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock(exception: false)
    next if msg == :wait_readable
    ai = r.local_address
    if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
      ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
      yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
        r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
      }
    else
      yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
        r.send reply_msg, 0, sender_addrinfo
      }
    end
  }
end

Получает пакеты UDP/IP из указанных sockets. Для каждого полученного пакета вызывается блок.

Блок получает msg и msg_src. msg — строка с полезной нагрузкой полученного пакета. msg_src — объект Socket::UDPSource, который используется для отправки ответа.

Socket.udp_server_loop можно реализовать с помощью этого метода следующим образом.

udp_server_sockets(host, port) {|sockets|
  loop {
    readable, _, _ = IO.select(sockets)
    udp_server_recv(readable) {|msg, msg_src| ... }
  }
}
udp_server_sockets([host, ] port) Показать исходный код
# File ext/socket/lib/socket.rb, line 1309
def self.udp_server_sockets(host=nil, port)
  last_error = nil
  sockets = []

  ipv6_recvpktinfo = nil
  if defined? Socket::AncillaryData
    if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
      ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
    elsif defined? Socket::IPV6_PKTINFO # RFC 2292
      ipv6_recvpktinfo = Socket::IPV6_PKTINFO
    end
  end

  local_addrs = Socket.ip_address_list

  ip_list = []
  Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
    if ai.ipv4? && ai.ip_address == "0.0.0.0"
      local_addrs.each {|a|
        next unless a.ipv4?
        ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
      }
    elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
      local_addrs.each {|a|
        next unless a.ipv6?
        ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
      }
    else
      ip_list << ai
    end
  }
  ip_list.uniq!(&:to_sockaddr)

  if port == 0
    sockets = ip_sockets_port0(ip_list, false)
  else
    ip_list.each {|ip|
      ai = Addrinfo.udp(ip.ip_address, port)
      begin
        s = ai.bind
      rescue SystemCallError
        last_error = $!
        next
      end
      sockets << s
    }
    if sockets.empty?
      raise last_error
    end
  end

  sockets.each {|s|
    ai = s.local_address
    if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
      s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
    end
  }

  if block_given?
    begin
      yield sockets
    ensure
      sockets.each(&:close) if sockets
    end
  else
    sockets
  end
end

Создаёт сокеты UDP/IP для UDP-сервера.

Если блок не передан, возвращается массив сокетов.

Если передан блок, он вызывается с сокетами. Возвращается значение блока. После завершения этого метода сокеты закрываются.

Если port равен нулю, выбирается некоторый порт. Выбранный порт используется всеми сокетами.

# UDP/IP echo server
Socket.udp_server_sockets(0) {|sockets|
  p sockets.first.local_address.ip_port     #=> 32963
  Socket.udp_server_loop_on(sockets) {|msg, msg_src|
    msg_src.reply msg
  }
}
unix (path) { |socket| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1501
def self.unix(path) # :yield: socket
  addr = Addrinfo.unix(path)
  sock = addr.connect
  if block_given?
    begin
      yield sock
    ensure
      sock.close
    end
  else
    sock
  end
end

создаёт новый сокет, подключённый к path с использованием сокета UNIX.

Если передан блок, он вызывается с сокетом. Возвращается значение блока. После завершения этого метода сокет закрывается.

Если блок не передан, возвращается сокет.

# talk to /tmp/sock socket.
Socket.unix("/tmp/sock") {|sock|
  t = Thread.new { IO.copy_stream(sock, STDOUT) }
  IO.copy_stream(STDIN, sock)
  t.join
}
unix_server_loop (path) { |socket, client_addrinfo| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1588
def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
  unix_server_socket(path) {|serv|
    accept_loop(serv, &b)
  }
end

создаёт сервер UNIX-сокетов по адресу path. Для каждого принятого сокета вызывается блок.

Если указан host, он используется вместе с port для определения портов сервера.

Сокет не закрывается после завершения блока. Поэтому приложение должно закрыть его самостоятельно.

Сначала этот метод удаляет файл сокета, указанный в path, если файл является сокетом и принадлежит пользователю приложения. Это безопасно только в том случае, если каталог, указанный в path, не изменяется злоумышленником. Поэтому не используйте /tmp/malicious-users-directory/socket. Обратите внимание, что /tmp/socket и /tmp/your-private-directory/socket безопасны при условии, что для /tmp установлен sticky bit.

# Sequential echo server.
# It services only one client at a time.
Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
  begin
    IO.copy_stream(sock, sock)
  ensure
    sock.close
  end
}
unix_server_socket (path) { |s| ... } Показать исходный код
# File ext/socket/lib/socket.rb, line 1531
def self.unix_server_socket(path)
  unless unix_socket_abstract_name?(path)
    begin
      st = File.lstat(path)
    rescue Errno::ENOENT
    end
    if st&.socket? && st.owned?
      File.unlink path
    end
  end
  s = Addrinfo.unix(path).listen
  if block_given?
    begin
      yield s
    ensure
      s.close
      unless unix_socket_abstract_name?(path)
        File.unlink path
      end
    end
  else
    s
  end
end

создаёт серверный сокет UNIX по пути path

Если блок не передан, возвращается сокет, ожидающий подключений.

Если блок передан, он вызывается с сокетом, а возвращается значение блока. После завершения блока сокет закрывается, а файл сокета удаляется.

socket = Socket.unix_server_socket("/tmp/s")
p socket                  #=> #<Socket:fd 3>
p socket.local_address    #=> #<Addrinfo: /tmp/s SOCK_STREAM>

Socket.unix_server_socket("/tmp/sock") {|s|
  p s                     #=> #<Socket:fd 3>
  p s.local_address       #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
}
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 969
def self.current_clock_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
expired? (started_at, ends_at) Показать исходный код
# File ext/socket/lib/socket.rb, line 982
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 954
def self.ip_address?(hostname)
  hostname.match?(IPV6_ADRESS_FORMAT) || hostname.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/)
end
resolve_hostname (family, host, port, hostname_resolution_result) Показать исходный код
# File ext/socket/lib/socket.rb, line 959
def self.resolve_hostname(family, host, port, hostname_resolution_result)
  begin
    resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM)
    hostname_resolution_result.add(family, resolved_addrinfos)
  rescue => e
    hostname_resolution_result.add(family, e)
  end
end
second_to_timeout (started_at, ends_at) Показать исходный код
# File ext/socket/lib/socket.rb, line 974
def self.second_to_timeout(started_at, ends_at)
  return nil if ends_at == Float::INFINITY || ends_at.nil?

  remaining = (ends_at - started_at)
  remaining.negative? ? 0 : remaining
end
tcp_without_fast_fallback (host, port, local_host, local_port, connect_timeout:, resolv_timeout:) Показать исходный код
# File ext/socket/lib/socket.rb, line 915
def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:)
  last_error = nil
  ret = nil

  local_addr_list = nil
  if local_host != nil || local_port != nil
    local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
  end

  Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai|
    if local_addr_list
      local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
      next unless local_addr
    else
      local_addr = nil
    end
    begin
      sock = local_addr ?
        ai.connect_from(local_addr, timeout: connect_timeout) :
        ai.connect(timeout: connect_timeout)
    rescue SystemCallError
      last_error = $!
      next
    end
    ret = sock
    break
  }
  unless ret
    if last_error
      raise last_error
    else
      raise SocketError, "no appropriate local address"
    end
  end

  ret
end
unix_socket_abstract_name? (path) Показать исходный код
# File ext/socket/lib/socket.rb, line 1559
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

Сведения об исключениях, которые могут быть выброшены при ошибке вызова accept_nonblock, см. в разделе Socket#accept.

Socket#accept_nonblock может вызвать любую ошибку, соответствующую сбою accept(2), включая Errno::EWOULDBLOCK.

Если исключением является Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED или Errno::EPROTO, оно дополняется модулем IO::WaitReadable. Поэтому для перехвата исключений и повторного вызова accept_nonblock можно использовать IO::WaitReadable.

Указав ключевой аргумент exception для false, можно указать, что accept_nonblock не должен вызывать исключение IO::WaitReadable, а должен возвращать символ :wait_readable.

См. также

  • 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 — sockaddr в формате struct, содержащийся в строке или объекте Addrinfo

Пример

require 'socket'

# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>

# use struct sockaddr
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )

Исключения в Unix-подобных системах

В Unix-подобных системах при ошибке вызова bind могут возникнуть следующие системные исключения:

  • Errno::EACCES — указанный sockaddr защищён, и у текущего пользователя нет разрешения на привязку к нему

  • Errno::EADDRINUSE — указанный sockaddr уже используется

  • Errno::EADDRNOTAVAIL — указанный sockaddr недоступен на локальном компьютере

  • Errno::EAFNOSUPPORT — указанный sockaddr не является допустимым адресом для семейства вызываемого socket

  • Errno::EBADF — указанный sockaddr не является допустимым файловым дескриптором

  • Errno::EFAULT — аргумент sockaddr недоступен

  • Errno::EINVAL — socket уже привязан к адресу, а протокол не поддерживает привязку к новому sockaddr, либо socket был отключён.

  • Errno::EINVAL — длина адреса недопустима для данного семейства адресов

  • Errno::ENAMETOOLONG — длина разрешённого пути превысила PATH_MAX

  • Errno::ENOBUFS — недостаточно места в буфере

  • Errno::ENOSR — недостаточно ресурсов STREAMS для выполнения операции

  • Errno::ENOTSOCK — socket не является сокетом

  • Errno::EOPNOTSUPP — тип сокета socket не поддерживает привязку к адресу

В Unix-подобных системах, если семейство адресов вызываемого socket — Socket::AF_UNIX, при ошибке вызова bind могут возникнуть следующие исключения:

  • Errno::EACCES — запрещён поиск компонента пути-префикса или запрещён доступ на запись к socket

  • Errno::EDESTADDRREQ — аргумент sockaddr является нулевым указателем

  • Errno::EISDIR — то же, что Errno::EDESTADDRREQ

  • Errno::EIO — произошла ошибка ввода-вывода

  • Errno::ELOOP — при преобразовании пути в sockaddr обнаружено слишком много символических ссылок

  • Errno::ENAMETOOLLONG — компонент пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов

  • Errno::ENOENT — компонент пути не указывает на существующий файл либо путь представляет собой пустую строку

  • Errno::ENOTDIR — компонент префикса пути в sockaddr не является каталогом

  • Errno::EROFS — имя будет размещено в файловой системе только для чтения

Исключения в Windows

В системах Windows при ошибке вызова bind могут возникнуть следующие системные исключения:

  • Errno::ENETDOWN — сеть не работает

  • Errno::EACCES — не удалось подключить дейтаграммный сокет к широковещательному адресу

  • Errno::EADDRINUSE — локальный адрес сокета уже используется

  • Errno::EADDRNOTAVAIL — указанный адрес недопустим для этого компьютера

  • Errno::EFAULT — внутренний адрес сокета или параметр длины адреса слишком малы либо не являются допустимой частью адресного пространства пользователя

  • Errno::EINVAL — socket уже привязан к адресу

  • Errno::ENOBUFS — недостаточно места в буфере

  • Errno::ENOTSOCK — аргумент socket не является сокетом

См. также

  • страницы руководства по bind в Unix-подобных системах

  • функция bind в справочнике функций Winsock от Microsoft

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 — sockaddr в формате struct, содержащийся в строке или объекте Addrinfo

Пример:

# Pull down Google's web page
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
socket.connect( sockaddr )
socket.write( "GET / HTTP/1.0\r\n\r\n" )
results = socket.read

Исключения в Unix-подобных системах

В Unix-подобных системах при ошибке вызова connect могут возникнуть следующие системные исключения:

  • Errno::EACCES — запрещён поиск компонента пути-префикса или запрещён доступ на запись к socket

  • Errno::EADDRINUSE — sockaddr уже используется

  • Errno::EADDRNOTAVAIL — указанный sockaddr недоступен на локальном компьютере

  • Errno::EAFNOSUPPORT — указанный sockaddr не является допустимым адресом для семейства адресов указанного socket

  • Errno::EALREADY — для указанного сокета уже выполняется подключение

  • Errno::EBADF — socket не является допустимым файловым дескриптором

  • Errno::ECONNREFUSED — целевой sockaddr не принимал соединения и отклонил запрос на подключение

  • Errno::ECONNRESET — удалённый узел сбросил запрос на подключение

  • Errno::EFAULT — sockaddr недоступен

  • Errno::EHOSTUNREACH — узел назначения недоступен (вероятно, узел не работает или удалённый маршрутизатор не может установить с ним связь)

  • Errno::EINPROGRESS — для socket установлен O_NONBLOCK, и соединение не может быть установлено немедленно; соединение будет установлено асинхронно

  • Errno::EINTR — попытка установить соединение была прервана полученным сигналом; соединение будет установлено асинхронно

  • Errno::EISCONN — указанный socket уже подключён

  • Errno::EINVAL — длина адреса, использованная для sockaddr, недопустима для данного семейства адресов либо в sockaddr указано недопустимое семейство

  • Errno::ENAMETOOLONG — длина разрешённого пути превысила PATH_MAX

  • Errno::ENETDOWN — локальный интерфейс, используемый для связи с узлом назначения, не работает

  • Errno::ENETUNREACH — маршрут к сети отсутствует

  • Errno::ENOBUFS — недостаточно места в буфере

  • Errno::ENOSR — недостаточно ресурсов STREAMS для выполнения операции

  • Errno::ENOTSOCK — аргумент socket не является сокетом

  • Errno::EOPNOTSUPP — вызывающий socket ожидает подключения и не может быть подключён

  • Errno::EPROTOTYPE — тип sockaddr отличается от типа сокета, привязанного к указанному адресу узла

  • Errno::ETIMEDOUT — время ожидания подключения истекло до установления соединения.

В Unix-подобных системах, если семейство адресов вызываемого socket — AF_UNIX, при ошибке вызова connect могут возникнуть следующие исключения:

  • Errno::EIO — при чтении из файловой системы или записи в неё произошла ошибка ввода-вывода

  • Errno::ELOOP — при преобразовании пути в sockaddr обнаружено слишком много символических ссылок

  • Errno::ENAMETOOLONG — компонент пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов

  • Errno::ENOENT — компонент пути не указывает на существующий файл либо путь представляет собой пустую строку

  • Errno::ENOTDIR — компонент префикса пути в sockaddr не является каталогом

Исключения в Windows

В системах Windows при ошибке вызова connect могут возникнуть следующие системные исключения:

  • Errno::ENETDOWN — сеть не работает

  • Errno::EADDRINUSE — локальный адрес сокета уже используется

  • Errno::EINTR — работа сокета была отменена

  • Errno::EINPROGRESS — выполняется блокирующий вызов или поставщик служб всё ещё обрабатывает функцию обратного вызова. Либо для socket выполняется неблокирующий вызов connect.

  • Errno::EALREADY — см. Errno::EINVAL

  • Errno::EADDRNOTAVAIL — удалённый адрес недопустим, например ADDR_ANY; TODO: проверить ADDRANY TO INADDR_ANY

  • Errno::EAFNOSUPPORT — адреса указанного семейства нельзя использовать с этим socket

  • Errno::ECONNREFUSED — целевой sockaddr не принимал соединения и отклонил запрос на подключение

  • Errno::EFAULT — внутренний адрес сокета или параметр длины адреса слишком малы либо не являются допустимой частью адресного пространства пользователя

  • Errno::EINVAL — socket является сокетом, ожидающим подключения

  • Errno::EISCONN — socket уже подключён

  • Errno::ENETUNREACH — в данный момент сеть недоступна с этого узла

  • Errno::EHOSTUNREACH — маршрут к сети отсутствует

  • Errno::ENOBUFS — недостаточно места в буфере

  • Errno::ENOTSOCK — аргумент socket не является сокетом

  • Errno::ETIMEDOUT — время ожидания подключения истекло до установления соединения.

  • Errno::EWOULDBLOCK — сокет помечен как неблокирующий, и соединение не может быть установлено немедленно

  • Errno::EACCES — не удалось подключить дейтаграммный сокет к широковещательному адресу

См. также

  • страницы руководства по connect в Unix-подобных системах

  • функция connect в справочнике функций Winsock от Microsoft

connect_nonblock(remote_sockaddr, [options]) → 0 Показать исходный код
# File ext/socket/lib/socket.rb, line 1638
def connect_nonblock(addr, exception: true)
  __connect_nonblock(addr, exception)
end

Запрашивает установление соединения с указанным remote_sockaddr после установки O_NONBLOCK для базового файлового дескриптора. В случае успеха возвращает 0, в противном случае вызывает исключение.

Параметр

  • remote_sockaddr — sockaddr в формате struct, содержащийся в строке или объекте Addrinfo

Пример:

# Pull down Google's web page
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(80, 'www.google.com')
begin # emulate blocking connect
  socket.connect_nonblock(sockaddr)
rescue IO::WaitWritable
  IO.select(nil, [socket]) # wait 3-way handshake completion
  begin
    socket.connect_nonblock(sockaddr) # check connection failure
  rescue Errno::EISCONN
  end
end
socket.write("GET / HTTP/1.0\r\n\r\n")
results = socket.read

Сведения об исключениях, которые могут быть выброшены при ошибке вызова connect_nonblock, см. в разделе Socket#connect.

Socket#connect_nonblock может вызвать любую ошибку, соответствующую сбою connect(2), включая Errno::EINPROGRESS.

Если исключением является Errno::EINPROGRESS, оно дополняется модулем IO::WaitWritable. Поэтому для перехвата исключений и повторного вызова connect_nonblock можно использовать IO::WaitWritable.

Указав ключевой аргумент exception для false, можно указать, что connect_nonblock не должен вызывать исключение IO::WaitWritable, а должен возвращать символ :wait_writable.

См. также

  • 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 это не сработает, поскольку перед вызовом listen необходимо привязать socket с помощью bind.

Если значение backlog превышает зависящую от реализации максимальную длину очереди, будет использована максимальная длина очереди, установленная реализацией.

В Unix-подобных системах при ошибке вызова listen могут возникнуть следующие системные исключения:

  • Errno::EBADF — аргумент socket не является допустимым файловым дескриптором

  • Errno::EDESTADDRREQ — socket не привязан к локальному адресу, а протокол не поддерживает ожидание подключений на непривязанном сокете

  • Errno::EINVAL — socket уже подключён

  • Errno::ENOTSOCK — аргумент socket не является сокетом

  • Errno::EOPNOTSUPP — протокол socket не поддерживает listen

  • Errno::EACCES — у вызывающего процесса нет необходимых привилегий

  • Errno::EINVAL — socket был отключён

  • Errno::ENOBUFS — в системе недостаточно ресурсов для выполнения вызова

Исключения в Windows

В системах Windows при ошибке вызова listen могут возникнуть следующие системные исключения:

  • Errno::ENETDOWN — сеть не работает

  • Errno::EADDRINUSE — локальный адрес сокета уже используется. Обычно это происходит во время выполнения bind, но может обнаружиться позже, если вызов bind был сделан для частично шаблонного адреса (с использованием ADDR_ANY) и конкретный адрес необходимо назначить при вызове listen

  • Errno::EINPROGRESS — выполняется вызов Windows Sockets 1.1 или поставщик служб всё ещё обрабатывает функцию обратного вызова

  • Errno::EINVAL — socket не был привязан вызовом bind.

  • Errno::EISCONN — socket уже подключён

  • Errno::EMFILE — больше нет доступных дескрипторов сокетов

  • Errno::ENOBUFS — недостаточно места в буфере

  • Errno::ENOTSOC — socket не является сокетом

  • Errno::EOPNOTSUPP — тип объекта socket не поддерживает метод listen

См. также

  • страницы руководства по listen в Unix-подобных системах

  • функция listen в справочнике функций Winsock от Microsoft

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 — соединение было разорвано, поскольку во время выполнения операции проверка активности keep-alive обнаружила сбой.

  • Errno::EOPNOTSUPP — указан MSG_OOB, но socket не является потоковым, например типа SOCK_STREAM. В домене взаимодействия, связанном с socket, внеполосные данные не поддерживаются либо socket является однонаправленным и поддерживает только операции отправки

  • Errno::ESHUTDOWN — socket был отключён. После вызова shutdown вызвать recvfrom для сокета невозможно.

  • Errno::EWOULDBLOCK — socket помечен как неблокирующий, и вызов recvfrom заблокировался бы.

  • Errno::EMSGSIZE — сообщение оказалось слишком большим для указанного буфера и было усечено.

  • Errno::ETIMEDOUT — соединение было разорвано из-за сбоя сети или потому, что система на другом конце отключилась без предупреждения

  • Errno::ECONNRESET — виртуальный канал был сброшен удалённой стороной в результате принудительного или аварийного закрытия. Приложению следует закрыть сокет: он больше непригоден для использования. Для сокета UDP-дейтаграмм эта ошибка означает, что предыдущая операция отправки привела к получению сообщения ICMP Port Unreachable.

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 — хеш ключевых аргументов, поддерживающий ‘exception: false`

Пример

# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
client, client_addrinfo = socket.accept
begin # emulate blocking recvfrom
  pair = client.recvfrom_nonblock(20)
rescue IO::WaitReadable
  IO.select([client])
  retry
end
data = pair[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close

# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Watch this get cut short!"
socket.close

Сведения об исключениях, которые могут быть выброшены при ошибке вызова recvfrom_nonblock, см. в разделе Socket#recvfrom.

Socket#recvfrom_nonblock может вызвать любую ошибку, соответствующую сбою recvfrom(2), включая Errno::EWOULDBLOCK.

Если исключением является Errno::EWOULDBLOCK или Errno::EAGAIN, оно дополняется модулем IO::WaitReadable. Поэтому для перехвата исключений и повторного вызова recvfrom_nonblock можно использовать IO::WaitReadable.

Указав ключевой аргумент exception для false, можно указать, что recvfrom_nonblock не должен вызывать исключение IO::WaitReadable, а должен возвращать символ :wait_readable.

См. также

  • 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

Сведения об исключениях, которые могут быть выброшены при ошибке вызова sysaccept, см. в разделе Socket#accept.

См. также

  • Socket#accept

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

Spec-Zone.ru

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