класс Socket
Класс Socket предоставляет доступ к базовым реализациям сокетов операционной системы. Его можно использовать для предоставления функциональности, более специфичной для операционной системы, чем классы сокетов, специфичные для протокола.
Константы, определенные в Socket::Constants, также определены в Socket. Например, Socket::AF_INET можно использовать так же, как и Socket::Constants::AF_INET. Список констант см. в Socket::Constants.
Что такое сокет?
Сокеты — это конечные точки двунаправленного канала связи. Сокеты могут взаимодействовать внутри процесса, между процессами на одной машине или между разными машинами. Существует множество типов сокетов: например, TCPSocket, UDPSocket или UNIXSocket.
Сокеты имеют свою собственную терминологию:
домен: Семейство протоколов:
тип: Тип связи между двумя конечными точками, обычно
протокол: Обычно ноль. Это может использоваться для идентификации варианта протокола.
имя хоста: Идентификатор сетевого интерфейса:
-
строка (имя хоста, IPv4 или IPv6 адрес или
broadcast, который определяет адрес широковещательной рассылки) -
строка нулевой длины, которая определяет INADDR_ANY
-
целое число (интерпретируется как двоичный адрес в порядке байтов хоста).
Быстрый старт
Многие классы, такие как TCPSocket, UDPSocket или UNIXSocket, упрощают использование сокетов по сравнению с эквивалентным интерфейсом программирования на языке C.
Давайте создадим сокет Интернета, используя протокол IPv4, в стиле C:
require 'socket' s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM s.connect Socket.pack_sockaddr_in(80, 'example.com')
Вы также можете использовать класс TCPSocket:
s = TCPSocket.new 'example.com', 80
Простой сервер может выглядеть так:
require 'socket'
server = TCPServer.new 2000 # Server bound to port 2000
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
Простой клиент может выглядеть так:
require 'socket' s = TCPSocket.new 'localhost', 2000 while line = s.gets # Read lines from socket puts line # and print them end s.close # close socket when done
Обработка исключений
Реализация сокетов Ruby Socket генерирует исключения, основанные на ошибке, сгенерированной реализацией, зависящей от системы. Именно поэтому методы документированы таким образом, чтобы изолировать исключения систем, основанных на Unix, от исключений, основанных на Windows. Если требуется более подробная информация об определённом исключении, обратитесь к страницам руководства Unix или справке WinSock Windows.
Удобные методы
Хотя общий способ создания сокета — ::new, для большинства случаев существуют несколько способов создания сокета.
- TCP-сокет клиента
- TCP-сокет сервера
- UNIX-сокет клиента
- UNIX-сокет сервера
Документация предоставлена
-
Заком Деннисом
-
Самом Робертсом
-
Программирование на Ruby от The Pragmatic Bookshelf.
Большая часть материала в этой документации взята с разрешения из Программирования на Ruby от The Pragmatic Bookshelf.
Константы
- AF_APPLETALK
-
Протокол AppleTalk
- AF_ATM
-
Режим асинхронной передачи
- AF_AX25
-
Протокол AX.25
- AF_CCITT
-
Протоколы CCITT (теперь ITU-T)
- AF_CHAOS
-
Протоколы MIT CHAOS
- AF_CNT
-
Технология компьютерных сетей
- AF_COIP
-
Ориентированный на соединение IP
- AF_DATAKIT
-
Протокол Datakit
- AF_DEC
-
Протокол DECnet
- AF_DLI
-
Протокол DEC Direct Data Link Interface
- AF_E164
-
Рекомендация CCITT (ITU-T) E.164
- AF_ECMA
-
Протоколы европейских производителей компьютеров
- AF_HYLINK
-
Протокол NSC Hyperchannel
- AF_IMPLINK
-
Протокол ARPANET IMP
- AF_INET
-
Протокол IPv4
- AF_INET6
-
Протокол IPv6
- AF_IPX
-
Протокол IPX
- AF_ISDN
-
Интегрированная цифровая сеть с услугами
- AF_ISO
-
Протоколы ISO модели взаимодействия открытых систем
- AF_LAT
-
Протокол локальной транспортной сети
- AF_LINK
-
Интерфейс канального уровня
- AF_LOCAL
-
Внутрисетевые протоколы
- AF_MAX
-
Максимальная семейство адресов для этой платформы
- AF_NATM
-
Оригинальный доступ ATM
- AF_NDRV
-
Прямой доступ к драйверу сети
- AF_NETBIOS
-
NetBIOS
- AF_NETGRAPH
-
Сокеты Netgraph
- AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы ISO модели взаимодействия открытых систем
- AF_PACKET
-
Прямой доступ к каналу связи
- AF_PPP
-
Протокол точка-точка
- AF_PUP
-
Протокол PARC Universal Packet
- AF_ROUTE
-
Внутренний протокол маршрутизации
- AF_SIP
-
Простой интернет-протокол
- AF_SNA
-
Протокол IBM SNA
- AF_SYSTEM
- AF_UNIX
-
Сокеты UNIX
- AF_UNSPEC
-
Неопределенный протокол, любое поддерживаемое семейство адресов
- AI_ADDRCONFIG
-
Принимать только при назначении любого адреса
- AI_ALL
-
Разрешить все адреса
- AI_CANONNAME
-
Заполнить каноническое имя
- AI_DEFAULT
-
Флаги по умолчанию для getaddrinfo
- AI_MASK
-
Допустимая маска флагов для getaddrinfo (не для использования приложениями)
- AI_NUMERICHOST
-
Запретить разрешение имени хоста
- AI_NUMERICSERV
-
Запретить разрешение имени службы
- AI_PASSIVE
-
Получить адрес для использования с bind()
- AI_V4MAPPED
-
Принимать IPv4-отображенные IPv6-адреса
- AI_V4MAPPED_CFG
-
Принимать IPv4-отображенные адреса, если ядро их поддерживает
- EAI_ADDRFAMILY
-
Семейство адресов не поддерживается для имени хоста
- EAI_AGAIN
-
Временное отказ в разрешении имени
- EAI_BADFLAGS
-
Неверные флаги
- EAI_BADHINTS
-
Неверное значение для подсказок
- EAI_FAIL
-
Необратимый отказ в разрешении имени
- EAI_FAMILY
-
Семейство адресов не поддерживается
- EAI_MAX
-
Максимальный код ошибки из getaddrinfo
- EAI_MEMORY
-
Ошибка выделения памяти
- EAI_NODATA
-
Нет адреса, связанного с именем хоста
- EAI_NONAME
-
Имя хоста или имя службы неизвестны
- EAI_OVERFLOW
-
Переполнение буфера аргументов
- EAI_PROTOCOL
-
Разрешенный протокол неизвестен
- EAI_SERVICE
-
Имя службы не поддерживается для типа сокета
- EAI_SOCKTYPE
-
Тип сокета не поддерживается
- EAI_SYSTEM
-
Возвращена системная ошибка в errno
- IFF_802_1Q_VLAN
-
Устройство VLAN 802.1Q
- IFF_ALLMULTI
-
прием всех пакетов multicast
- IFF_ALTPHYS
-
использовать альтернативное физическое подключение
- IFF_AUTOMEDIA
-
Автоматический выбор среды активен
- IFF_BONDING
-
Ведущий или ведомый агрегирования
- IFF_BRIDGE_PORT
-
устройство используется как порт моста
- IFF_BROADCAST
-
Адрес широковещания действителен
- IFF_CANTCHANGE
-
Флаги не изменяемы
- IFF_CANTCONFIG
-
не конфигурируется с помощью ioctl(2)
- IFF_DEBUG
-
Включить отладку
- IFF_DISABLE_NETPOLL
-
выключить netpoll во время работы
- IFF_DONT_BRIDGE
-
запретить создание моста для данного эфирного устройства
- 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
-
Ведущий агрегирования, 802.3ad.
- IFF_MASTER_ALB
-
Ведущий агрегирования, балансировка-ALB.
- IFF_MASTER_ARPMON
-
Ведущий агрегирования, используется мониторинг ARP
- IFF_MONITOR
-
Пользовательский режим мониторинга
- IFF_MULTICAST
-
Поддерживает multicast
- IFF_NOARP
-
Отсутствует протокол разрешения адресов
- IFF_NOTRAILERS
-
Избегать использования трейлеров
- IFF_OACTIVE
-
Передача данных в процессе
- IFF_OVS_DATAPATH
-
Устройство используется как порт Open vSwitch datapath
- IFF_POINTOPOINT
-
Точечно-точечное соединение
- IFF_PORTSEL
-
Можно установить тип среды
- IFF_PPROMISC
-
Пользовательский режим promisc
- IFF_PROMISC
-
Принимать все пакеты
- IFF_RENAMING
-
Интерфейс переименовывается
- IFF_ROUTE
-
Установка записи маршрутизации
- IFF_RUNNING
-
Ресурсы выделены
- IFF_SIMPLEX
-
Не может услышать собственные передачи
- IFF_SLAVE
-
Ведомый балансировщика нагрузки
- IFF_SLAVE_INACTIVE
-
Ведомый агрегирования не активный
- IFF_SLAVE_NEEDARP
-
Требуются ARP для проверки
- IFF_SMART
-
Интерфейс управляет собственными маршрутами
- IFF_STATICARP
-
Статический ARP
- IFF_SUPP_NOFCS
-
Отправка пользовательской FCS
- IFF_TEAM_PORT
-
Используется в качестве порта команды
- IFF_TX_SKB_SHARING
-
Использование общего skb при передаче
- IFF_UNICAST_FLT
-
Фильтрация одноадресных пакетов
- IFF_UP
-
Интерфейс активен
- IFF_VOLATILE
-
Переменные флаги
- IFF_WAN_HDLC
-
Устройство WAN HDLC
- IFF_XMIT_DST_RELEASE
-
dev_hard_start_xmit() разрешено освобождать skb->dst
- IFNAMSIZ
-
Максимальная длина имени интерфейса
- IF_NAMESIZE
-
Максимальная длина имени интерфейса
- INADDR_ALLHOSTS_GROUP
-
Группа multicast для всех систем в этом подмножестве
- INADDR_ANY
-
Сокет, привязанный к INADDR_ANY, получает пакеты со всех интерфейсов и отправляет с помощью IP-адреса по умолчанию
- INADDR_BROADCAST
-
Адрес широковещательной передачи в сети
- INADDR_LOOPBACK
-
Адрес обратной связи
- INADDR_MAX_LOCAL_GROUP
-
Последняя локальная группа multicast
- INADDR_NONE
-
Маска для совпадения с отсутствующим IP-адресом
- INADDR_UNSPEC_GROUP
-
Зарезервированная группа multicast
- INET6_ADDRSTRLEN
-
Максимальная длина строки IPv6-адреса
- INET_ADDRSTRLEN
-
Максимальная длина строки IPv4-адреса
- IPPORT_RESERVED
-
Минимальный адрес по умолчанию для bind или connect
- IPPORT_USERRESERVED
-
Максимальный адрес по умолчанию для bind или connect
- IPPROTO_AH
-
IP6 заголовок аутентификации
- IPPROTO_BIP
- IPPROTO_DSTOPTS
-
IP6 опция назначения
- IPPROTO_EGP
-
Протокол внешних шлюзов
- IPPROTO_EON
-
ISO cnlp
- IPPROTO_ESP
-
IP6 Зашифрованная полезная нагрузка
- IPPROTO_FRAGMENT
-
Заголовок фрагментации IP6
- IPPROTO_GGP
-
Протокол шлюза-шлюз
- IPPROTO_HELLO
-
Протокол маршрутизации «hello»
- IPPROTO_HOPOPTS
-
Опции IP6 передачи по шагам
- IPPROTO_ICMP
-
Протокол сообщений управления
- IPPROTO_ICMPV6
-
ICMP6
- IPPROTO_IDP
-
XNS IDP
- IPPROTO_IGMP
-
Протокол управления группами
- IPPROTO_IP
-
Протокол-заглушка для IP
- IPPROTO_IPV6
-
Заголовок IP6
- IPPROTO_MAX
-
Максимальная константа IPPROTO
- IPPROTO_ND
-
Протокол Sun net disk
- IPPROTO_NONE
-
IP6 без следующего заголовка
- IPPROTO_PUP
-
Протокол PARC Universal Packet
- IPPROTO_RAW
-
Сырой пакет IP
- IPPROTO_ROUTING
-
Заголовок маршрутизации IP6
- IPPROTO_TCP
-
TCP
- IPPROTO_TP
-
Класс 4 транспортного протокола ISO
- IPPROTO_UDP
-
UDP
- IPPROTO_XTP
-
Протокол Xpress Transport
- IPV6_CHECKSUM
-
Смещение контрольной суммы для сырых сокетов
- IPV6_DONTFRAG
-
Не фрагментировать пакеты
- IPV6_DSTOPTS
-
Опция назначения
- IPV6_HOPLIMIT
-
Предел переходов
- IPV6_HOPOPTS
-
Опция передачи по шагам
- IPV6_JOIN_GROUP
-
Присоединение к группе членства
- IPV6_LEAVE_GROUP
-
Выход из группы членства
- IPV6_MULTICAST_HOPS
-
Переходы IP6 для многоадресной рассылки
- IPV6_MULTICAST_IF
-
Интерфейс IP6 для многоадресной рассылки
- IPV6_MULTICAST_LOOP
-
Петля обратной связи IP6 для многоадресной рассылки
- IPV6_NEXTHOP
-
Адрес следующего узла
- IPV6_PATHMTU
-
Получение текущего максимального размера транспортного блока пути
- IPV6_PKTINFO
-
Получение информации о пакете с датаграммой
- IPV6_RECVDSTOPTS
-
Получение всех опций IP6 для ответа
- IPV6_RECVHOPLIMIT
-
Получение предела переходов с датаграммой
- IPV6_RECVHOPOPTS
-
Получение опций передачи по шагам
- IPV6_RECVPATHMTU
-
Получение текущего максимального размера транспортного блока пути с датаграммой
- 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 с помощью wildcard bind
- 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
-
Открытие максимального размера транспортного блока пути
- 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
-
Получить IP TTL с датаграммами
- IP_RETOPTS
-
Опции IP, которые должны быть включены в датаграммы
- IP_ROUTER_ALERT
-
Уведомить маршрутизаторы транзита, чтобы более внимательно изучить содержимое IP пакета.
- IP_SENDSRCADDR
-
Исходный адрес для исходящих UDP датаграмм
- IP_TOS
-
Тип обслуживания IP
- IP_TRANSPARENT
-
Прозрачный прокси
- IP_TTL
-
Время жизни IP
- IP_UNBLOCK_SOURCE
-
Разблокировать пакеты IPv4 многоадресной рассылки с заданным исходным адресом
- IP_XFRM_POLICY
- LOCAL_CONNWAIT
-
Блокировка соединения до его принятия
- LOCAL_CREDS
-
Передать учётные данные получателю
- LOCAL_PEERCRED
-
Получить учётные данные собеседника
- MCAST_BLOCK_SOURCE
-
Заблокировать пакеты многоадресной рассылки от данного источника
- MCAST_EXCLUDE
-
Исключительный фильтр источника многоадресной рассылки
- MCAST_INCLUDE
-
Включенный фильтр источника многоадресной рассылки
- MCAST_JOIN_GROUP
-
Присоединение к группе многоадресной рассылки
- MCAST_JOIN_SOURCE_GROUP
-
Присоединение к группе многоадресной рассылки по источнику
- MCAST_LEAVE_GROUP
-
Выход из группы многоадресной рассылки
- MCAST_LEAVE_SOURCE_GROUP
-
Выход из группы многоадресной рассылки по источнику
- MCAST_MSFILTER
-
Фильтр источников многоадресной рассылки
- MCAST_UNBLOCK_SOURCE
-
Разблокировать пакеты многоадресной рассылки от данного источника
- MSG_COMPAT
-
Конец записи
- MSG_CONFIRM
-
Подтверждение валидности пути
- MSG_CTRUNC
-
Утерянные управляющие данные до доставки
- MSG_DONTROUTE
-
Отправить без использования таблиц маршрутизации
- MSG_DONTWAIT
-
Это сообщение должно быть неблокирующим
- MSG_EOF
-
Данные завершают соединение
- MSG_EOR
-
Данные завершают запись
- MSG_ERRQUEUE
-
Получить сообщение из очереди ошибок
- MSG_FASTOPEN
-
Уменьшить шаг процесса рукопожатия
- MSG_FIN
- MSG_FLUSH
-
Начало последовательности удержания. Выгружается в so_temp
- MSG_HAVEMORE
-
Данные готовы к чтению
- MSG_HOLD
-
Удерживать фрагмент в so_temp
- MSG_MORE
-
Отправитель отправит больше данных
- MSG_NOSIGNAL
-
Не генерировать SIGPIPE
- MSG_OOB
-
Обработать внеочередные данные
- MSG_PEEK
-
Просмотреть входящее сообщение
- MSG_PROXY
-
Ожидать полного запроса
- MSG_RCVMORE
-
Данные остаются в текущем пакете
- MSG_RST
- MSG_SEND
-
Отправить пакет в so_temp
- MSG_SYN
- MSG_TRUNC
-
Данные были отброшены до доставки
- MSG_WAITALL
-
Ожидать полного запроса или ошибки
- NI_DGRAM
-
Указанная служба является службой датаграмм (ищет UDP порты)
- NI_MAXHOST
-
Максимальная длина имени хоста
- NI_MAXSERV
-
Максимальная длина имени службы
- NI_NAMEREQD
-
Требуется имя
- NI_NOFQDN
-
FQDN не требуется для локальных хостов, возвращается только локальная часть
- NI_NUMERICHOST
-
Возвратить числовой адрес
- NI_NUMERICSERV
-
Возвратить имя службы в виде числовой строки
- PF_APPLETALK
-
Протокол AppleTalk
- PF_ATM
-
Режим асинхронной передачи
- PF_AX25
-
Протокол AX.25
- PF_CCITT
-
Протоколы CCITT (сейчас ITU-T)
- PF_CHAOS
-
Протоколы MIT CHAOS
- PF_CNT
-
Технология компьютерных сетей
- PF_COIP
-
Ориентированный на соединение IP
- PF_DATAKIT
-
Протокол Datakit
- PF_DEC
-
Протокол DECnet
- PF_DLI
-
Протокол DEC Direct Data Link Interface
- PF_ECMA
-
Протоколы европейских производителей компьютеров
- PF_HYLINK
-
Протокол NSC Hyperchannel
- PF_IMPLINK
-
Протокол ARPANET IMP
- PF_INET
-
Протокол IPv4
- PF_INET6
-
Протокол IPv6
- PF_IPX
-
Протокол IPX
- PF_ISDN
-
Цифровая сеть с интегрированными услугами
- PF_ISO
-
Протоколы ISO модели взаимодействия открытых систем
- PF_KEY
- PF_LAT
-
Протокол локальной передачи
- PF_LINK
-
Интерфейс уровня канала связи
- PF_LOCAL
-
Внутрисетевые протоколы хоста
- PF_MAX
-
Максимальная семейство адресов для этой платформы
- PF_NATM
-
Прямой доступ к ATM
- PF_NDRV
-
Непосредственный доступ к драйверу сети
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Гнёзда Netgraph
- PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы ISO модели взаимодействия открытых систем
- PF_PACKET
-
Прямой доступ к уровню канала связи
- PF_PIP
- PF_PPP
-
Протокол точка-точка
- PF_PUP
-
Протокол PARC универсальный пакетный
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_RTIP
- PF_SIP
-
Простой протокол Интернета
- PF_SNA
-
Протокол IBM SNA
- PF_SYSTEM
- PF_UNIX
-
UNIX-гнезда
- PF_UNSPEC
-
Неопределенный протокол, любое поддерживаемое семейство адресов
- PF_XTP
-
Протокол быстрой передачи
- 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_DGRAM
-
Гнездо дейтаграммы обеспечивает беспроводную, ненадежную передачу сообщений
- 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_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_KEEPALIVE
-
Поддерживать соединения активными
- SO_LINGER
-
Задержаться при закрытии, если данные присутствуют
- SO_LOCK_FILTER
-
Заблокировать фильтр, присоединенный к сокету (Linux 3.9)
- SO_MAC_EXEMPT
-
Исключение контроля доступа на основе политики для непомеченных партнёров
- SO_MARK
-
Установить метку для маршрутизации по меткам (Linux 2.6.25)
- SO_MAX_PACING_RATE
-
Ограничить скорость, вычисленную транспортным уровнем. [байты в секунду] (Linux 3.13)
- SO_NKE
-
Установить расширение ядра сети на уровне сокета
- SO_NOFCS
-
Установить netns сокета (Linux 3.4)
- SO_NOSIGPIPE
-
Не отправлять SIGPIPE на EPIPE
- SO_NO_CHECK
-
Отключить контрольные суммы
- SO_NREAD
-
Получить количество байтов первого пакета
- SO_OOBINLINE
-
Оставить полученные данные вне очереди в строке
- SO_PASSCRED
-
Получать сообщения SCM_CREDENTIALS
- SO_PASSSEC
-
Переключать передачу контекста безопасности (Linux 2.6.18)
- SO_PEEK_OFF
-
Установить смещение peek (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_RXQ_OVFL
-
Включить cmsg для количества потерянных пакетов (Linux 2.6.33)
- SO_SECURITY_AUTHENTICATION
- SO_SECURITY_ENCRYPTION_NETWORK
- SO_SECURITY_ENCRYPTION_TRANSPORT
- SO_SELECT_ERR_QUEUE
-
Заставить select() обнаруживать очередь ошибок сокета с ошибочными файловыми дескрипторами (Linux 3.10)
- 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_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_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_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
-
Обработка дублированных подтверждений для thin-потоков (Linux 2.6.34, glibc 2.18)
- TCP_THIN_LINEAR_TIMEOUTS
-
Линейные таймауты для thin-потоков (Linux 2.6.34, glibc 2.18)
- TCP_TIMESTAMP
-
Маркировка времени TCP (Linux 3.9, glibc 2.18)
- TCP_USER_TIMEOUT
-
Максимальный таймаут перед прерыванием соединения TCP (Linux 2.6.37, glibc 2.18)
- TCP_WINDOW_CLAMP
-
Ограничить размер объявленного окна (Linux 2.4, glibc 2.2)
- UDP_CORK
-
Не отправлять частичные кадры (Linux 2.5.44, glibc 2.11)
Методы публичного класса
# File ext/socket/lib/socket.rb, line 798
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
sockets.flatten!(1)
if sockets.empty?
raise ArgumentError, "no sockets"
end
loop {
readable, _, _ = IO.select(sockets)
readable.each {|r|
sock, addr = r.accept_nonblock(exception: false)
next if sock == :wait_readable
yield sock, addr
}
}
end Выполняет итерацию по принятым подключениям через заданные сокеты, передавая каждый сокет и адрес клиента в блок.
Аргументом является список сокетов. Каждый аргумент может быть сокетом или массивом сокетов.
Метод выполняет блок последовательно. Это означает, что следующее подключение не будет принято, пока блок не вернётся. Поэтому для обслуживания нескольких клиентов одновременно необходимо использовать механизм конкурентности, например, потоки.
static VALUE
sock_s_getaddrinfo(int argc, VALUE *argv)
{
VALUE 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 должен быть битовой операцией OR констант 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 same as numeric address. +nil+: obey to the current +do_not_reverse_lookup+ flag.
Если предпочтительнее использовать объект Addrinfo, используйте Addrinfo.getaddrinfo.
static VALUE
sock_s_gethostbyaddr(int argc, VALUE *argv)
{
VALUE addr, family;
struct hostent *h;
char **pch;
VALUE ary, names;
int t = AF_INET;
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
t = AF_INET6;
}
#endif
h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
extern int h_errno;
rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
rb_ary_push(ary, rb_str_new2(h->h_name));
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
for (pch = h->h_aliases; *pch; pch++) {
rb_ary_push(names, rb_str_new2(*pch));
}
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
#endif
return ary;
} Используйте Addrinfo#getnameinfo вместо этого метода. Этот метод устарел по следующим причинам:
-
Необычное представление адреса: 4/16-байтовая двоичная строка для представления адресов IPv4/IPv6.
-
gethostbyaddr() может занимать много времени и блокировать другие потоки. (GVL не может быть освобождён, так как gethostbyname() не потокобезопасен.)
-
Этот метод использует функцию gethostbyname(), которая уже удалена из POSIX.
Этот метод получает информацию о хосте для address.
p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
#=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
["localhost", [], 2, "\x7F\x00\x00\x01"]
p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
#=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
struct rb_addrinfo *res =
rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME);
return rsock_make_hostent(host, res, sock_sockaddr);
} Используйте Addrinfo.getaddrinfo вместо этого метода. Этот метод устарел по следующим причинам:
-
Третий элемент результата — семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Необычное представление адреса: 4/16-байтовая двоичная строка для представления адресов IPv4/IPv6.
-
gethostbyname() может занимать много времени и блокировать другие потоки. (GVL не может быть освобождён, так как gethostbyname() не потокобезопасен.)
-
Этот метод использует функцию gethostbyname(), которая уже удалена из POSIX.
Этот метод получает информацию о хосте для hostname.
p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
static VALUE
sock_gethostname(VALUE obj)
{
#if defined(NI_MAXHOST)
# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST
#elif defined(HOST_NAME_MAX)
# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX
#else
# define RUBY_MAX_HOST_NAME_LEN 1024
#endif
long len = RUBY_MAX_HOST_NAME_LEN;
VALUE name;
name = rb_str_new(0, len);
while (gethostname(RSTRING_PTR(name), len) < 0) {
int e = errno;
switch (e) {
case ENAMETOOLONG:
#ifdef __linux__
case EINVAL:
/* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
#endif
break;
default:
rb_syserr_fail(e, "gethostname(3)");
}
rb_str_modify_expand(name, len);
len += len;
}
rb_str_resize(name, strlen(RSTRING_PTR(name)));
return name;
} Возвращает имя хоста.
p Socket.gethostname #=> "hal"
Обратите внимание, что не гарантируется возможность преобразования в IP-адрес с помощью gethostbyname, getaddrinfo и т.д. Если вам нужен локальный IP-адрес, используйте ::ip_address_list.
static VALUE
socket_s_getifaddrs(VALUE self)
{
return rsock_getifaddrs();
} Возвращает массив адресов интерфейсов. Элемент массива — экземпляр Socket::Ifaddr.
Этот метод можно использовать для поиска интерфейсов с поддержкой мультикаста:
pp Socket.getifaddrs.reject {|ifaddr|
!ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
}.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
#=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
# ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
Пример результата на GNU/Linux:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
Пример результата на FreeBSD:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
static VALUE
sock_s_getnameinfo(int argc, VALUE *argv)
{
VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
char *hptr, *pptr;
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));
}
/* host */
if (NIL_P(host)) {
hptr = NULL;
}
else {
strncpy(hbuf, StringValueCStr(host), sizeof(hbuf));
hbuf[sizeof(hbuf) - 1] = '\0';
hptr = hbuf;
}
/* port */
if (NIL_P(port)) {
strcpy(pbuf, "0");
pptr = NULL;
}
else if (FIXNUM_P(port)) {
snprintf(pbuf, sizeof(pbuf), "%ld", NUM2LONG(port));
pptr = pbuf;
}
else {
strncpy(pbuf, StringValueCStr(port), sizeof(pbuf));
pbuf[sizeof(pbuf) - 1] = '\0';
pptr = pbuf;
}
hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
/* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
error = rb_getaddrinfo(hptr, pptr, &hints, &res);
if (error) goto error_exit_addr;
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_addr:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_socket_error("getaddrinfo", error);
error_exit_name:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_socket_error("getnameinfo", error);
UNREACHABLE;
} Получает информацию о имени для sockaddr.
sockaddr должен быть одним из следующих:
-
упакованная строка sockaddr, например ::sockaddr_in(80, “127.0.0.1”)
-
массив из 3 элементов, например [“AF_INET”, 80, “127.0.0.1”]
-
массив из 4 элементов, например [“AF_INET”, 80, игнорируется, “127.0.0.1”]
flags должен быть битовой операцией OR констант Socket::NI_*.
Примечание: Последний формат совместим с IPSocket#addr и IPSocket#peeraddr.
Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"]) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"]
Если предпочтительнее использовать объект Addrinfo, используйте Addrinfo#getnameinfo.
static VALUE
sock_s_getservbyname(int argc, VALUE *argv)
{
VALUE service, proto;
struct servent *sp;
long port;
const char *servicename, *protoname = "tcp";
rb_scan_args(argc, argv, "11", &service, &proto);
StringValue(service);
if (!NIL_P(proto)) StringValue(proto);
servicename = StringValueCStr(service);
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyname(servicename, protoname);
if (sp) {
port = ntohs(sp->s_port);
}
else {
char *end;
port = STRTOUL(servicename, &end, 0);
if (*end != '\0') {
rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
}
}
return INT2FIX(port);
} Получает номер порта для service_name.
Если protocol_name не указан, предполагается «tcp».
Socket.getservbyname("smtp") #=> 25
Socket.getservbyname("shell") #=> 514
Socket.getservbyname("syslog", "udp") #=> 514
static VALUE
sock_s_getservbyport(int argc, VALUE *argv)
{
VALUE 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_tainted_str_new2(sp->s_name);
} Получает номер порта для port.
Если protocol_name не указан, предполагается «tcp».
Socket.getservbyport(80) #=> "www" Socket.getservbyport(514, "tcp") #=> "shell" Socket.getservbyport(514, "udp") #=> "syslog"
static VALUE
socket_s_ip_address_list(VALUE self)
{
#if defined(HAVE_GETIFADDRS)
struct ifaddrs *ifp = NULL;
struct ifaddrs *p;
int ret;
VALUE list;
ret = getifaddrs(&ifp);
if (ret == -1) {
rb_sys_fail("getifaddrs");
}
list = rb_ary_new();
for (p = ifp; p; p = p->ifa_next) {
if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) {
struct sockaddr *addr = p->ifa_addr;
#if defined(AF_INET6) && defined(__sun)
/*
* OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local
* address with sin6_scope_id == 0.
* So fill it from the interface name (ifa_name).
*/
struct sockaddr_in6 addr6;
if (addr->sa_family == AF_INET6) {
socklen_t len = (socklen_t)sizeof(struct sockaddr_in6);
memcpy(&addr6, addr, len);
addr = (struct sockaddr *)&addr6;
if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
addr6.sin6_scope_id == 0) {
unsigned int ifindex = if_nametoindex(p->ifa_name);
if (ifindex != 0) {
addr6.sin6_scope_id = ifindex;
}
}
}
#endif
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
}
freeifaddrs(ifp);
return list;
#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && !defined(__hpux)
/* Solaris if_tcp(7P) */
/* HP-UX has SIOCGLIFCONF too. But it uses different struct */
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;
if (lc.lifc_buf != NULL)
xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
struct ifconf conf;
struct ifreq *req;
VALUE list = Qnil;
const char *reason = NULL;
int save_errno;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
retry:
conf.ifc_len = bufsize;
conf.ifc_req = (struct ifreq *)buf;
/* fprintf(stderr, "bufsize: %d\n", bufsize); */
ret = ioctl(fd, SIOCGIFCONF, &conf);
if (ret == -1) {
reason = "SIOCGIFCONF";
goto finish;
}
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
if (bufsize < conf.ifc_len) {
/* NetBSD returns required size for all interfaces. */
bufsize = conf.ifc_len + EXTRA_SPACE;
}
else {
bufsize = bufsize << 1;
}
if (buf == initbuf)
buf = NULL;
buf = xrealloc(buf, bufsize);
goto retry;
}
close(fd);
fd = -1;
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
(sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
finish:
save_errno = errno;
if (buf != initbuf)
xfree(buf);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_unicast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
int dummy1;
int dummy2;
int dummy3;
long dummy4;
long dummy5;
long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_anycast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_addresses_st *Next;
void *dummy1;
ip_adapter_unicast_address_t *FirstUnicastAddress;
ip_adapter_anycast_address_t *FirstAnycastAddress;
void *dummy2;
void *dummy3;
void *dummy4;
void *dummy5;
void *dummy6;
BYTE dummy7[8];
DWORD dummy8;
DWORD dummy9;
DWORD dummy10;
DWORD IfType;
int OperStatus;
DWORD dummy12;
DWORD dummy13[16];
void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
GetAdaptersAddresses_t pGetAdaptersAddresses;
ULONG len;
DWORD ret;
ip_adapter_addresses_t *adapters;
VALUE list;
h = LoadLibrary("iphlpapi.dll");
if (!h)
rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
FreeLibrary(h);
rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
ip_adapter_unicast_address_t *uni;
ip_adapter_anycast_address_t *any;
if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
continue;
for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
}
for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
}
}
FreeLibrary(h);
return list;
#endif
} Возвращает локальные IP-адреса в виде массива.
Массив содержит объекты Addrinfo.
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
#<Addrinfo: 192.168.0.128>,
#<Addrinfo: ::1>,
...] static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
VALUE domain, type, protocol;
int fd;
int d, t;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
return rsock_init_sock(sock, fd);
} Создаёт новый объект сокета.
domain должен быть областью обмена данными, например: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
protocol является необязательным и должен быть протоколом, определённым в области. Если protocol не указан, используется 0.
Socket.new(:INET, :STREAM) # TCP socket Socket.new(:INET, :DGRAM) # UDP socket Socket.new(:UNIX, :STREAM) # UNIX stream socket Socket.new(:UNIX, :DGRAM) # UNIX datagram socket
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
OBJ_INFECT(addr, port);
OBJ_INFECT(addr, host);
return addr;
} Упаковывает port и host в строку sockaddr AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
OBJ_INFECT(addr, path);
return addr;
} Упаковывает path в строку sockaddr AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создаёт пару сокетов, соединённых друг с другом.
domain должен быть областью обмена данными, например: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
protocol должен быть протоколом, определённым в области, по умолчанию равен 0.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
OBJ_INFECT(addr, port);
OBJ_INFECT(addr, host);
return addr;
} Упаковывает port и host в строку sockaddr AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
OBJ_INFECT(addr, path);
return addr;
} Упаковывает path в строку sockaddr AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создаёт пару сокетов, соединённых друг с другом.
domain должен быть домен связи, например: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
protocol должен быть протоколом, определённым в домене. По умолчанию 0 для данного домена.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
# File ext/socket/lib/socket.rb, line 621
def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil) # :yield: socket
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) {|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
if block_given?
begin
yield ret
ensure
ret.close
end
else
ret
end
end создаёт новый сокет, подключённый к host:port с использованием TCP/IP.
Если заданы local_host:local_port, сокет связывается с ними.
Необязательный последний аргумент opts — это опции, представленные в виде хэша. opts может содержать следующие опции:
- :connect_timeout
-
устанавливает таймаут в секундах.
Если задан блок, он вызывается с сокетом. Возвращается значение блока. Сокет закрывается по завершении метода.
Если блок не задан, возвращается сокет.
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
}
# File ext/socket/lib/socket.rb, line 854
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 адрес, все они используются. ::tcp_server_loop выполняется успешно, если хотя бы один сокет может быть использован.
# Sequential echo server.
# It services only one client at a time.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# Threaded echo server
# It services multiple clients at a time.
# Note that it may accept connections too much.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
Thread.new {
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
}
# File ext/socket/lib/socket.rb, line 754
def self.tcp_server_sockets(host=nil, port)
if port == 0
sockets = tcp_server_sockets_port0(host)
else
last_error = nil
sockets = []
begin
Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
begin
s = ai.listen
rescue SystemCallError
last_error = $!
next
end
sockets << s
}
if sockets.empty?
raise last_error
end
rescue Exception
sockets.each(&:close)
raise
end
end
if block_given?
begin
yield sockets
ensure
sockets.each(&:close)
end
else
sockets
end
end создаёт сокеты сервера TCP/IP для host и port. host необязателен.
Если блок не задан, возвращается массив прослушивающих сокетов.
Если задан блок, он вызывается с сокетами. Возвращается значение блока. Сокеты закрываются по завершении метода.
Если port равен 0, порт выбирается динамически. Однако все сокеты в результате имеют тот же порт.
# tcp_server_sockets returns two sockets.
sockets = Socket.tcp_server_sockets(1296)
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
# The sockets contains IPv6 and IPv4 sockets.
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:1296 TCP>
# #<Addrinfo: 0.0.0.0:1296 TCP>
# IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
sockets = Socket.tcp_server_sockets(0)
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:53114 TCP>
# #<Addrinfo: 0.0.0.0:53114 TCP>
# The block is called with the sockets.
Socket.tcp_server_sockets(0) {|sockets|
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
}
# File ext/socket/lib/socket.rb, line 1024
def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
udp_server_sockets(host, port) {|sockets|
udp_server_loop_on(sockets, &b)
}
end создаёт сервер UDP/IP на port и вызывает блок для каждого полученного сообщения. Блок вызывается с сообщением и его исходной информацией.
Этот метод выделяет сокеты внутри, используя port. Если host задан, он используется совместно с port для определения адресов сервера.
msg — это строка.
msg_src — объект Socket::UDPSource. Он используется для ответа.
# UDP/IP echo server.
Socket.udp_server_loop(9261) {|msg, msg_src|
msg_src.reply msg
}
Методы класса (приватные)
# File ext/socket/lib/socket.rb, line 1132 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
Общедоступные методы экземпляра
static VALUE
sock_accept(VALUE sock)
{
rb_io_t *fptr;
VALUE sock2;
union_sockaddr buf;
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
sock2 = rsock_s_accept(rb_cSocket,fptr->fd,&buf.addr,&len);
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
} Принимает следующее подключение. Возвращает новый объект Socket и объект Addrinfo.
serv = Socket.new(:INET, :STREAM, 0) serv.listen(5) c = Socket.new(:INET, :STREAM, 0) c.connect(serv.connect_address) p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>]
# File ext/socket/lib/socket.rb, line 591 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 для получения исключений, которые могут быть выброшены, если вызов accept_nonblock завершится неудачей.
#accept_nonblock может вызвать любое исключение, соответствующее ошибке accept(2), включая Errno::EWOULDBLOCK.
Если исключение равно Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED или Errno::EPROTO, оно дополняется IO::WaitReadable. Таким образом, IO::WaitReadable может использоваться для перехвата исключений для повторного выполнения accept_nonblock.
Указав ключевой аргумент exception в false, можно указать, что #accept_nonblock не должен генерировать исключение IO::WaitReadable, а вместо этого вернуть символ :wait_readable.
См. также
static VALUE
sock_bind(VALUE sock, VALUE addr)
{
VALUE rai;
rb_io_t *fptr;
SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
} Связывает с указанным локальным адресом.
Параметр
-
local_sockaddr- содержащийstructsockaddr в строке или объекте Addrinfo
Пример
require 'socket'
# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
# use struct sockaddr
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
Исключения для систем Unix
В системах на основе Unix, если вызов bind завершится неудачей, могут быть выброшены следующие системные исключения:
-
Errno::EACCES - указанный sockaddr защищен, и текущий пользователь не имеет разрешения на привязку к нему
-
Errno::EADDRINUSE - указанный sockaddr уже используется
-
Errno::EADDRNOTAVAIL - указанный sockaddr недоступен на локальном компьютере
-
Errno::EAFNOSUPPORT - указанный sockaddr не является допустимым адресом для семейства вызывающего
socket -
Errno::EBADF - указанный sockaddr не является допустимым дескриптором файла
-
Errno::EFAULT - к указанному аргументу sockaddr нельзя получить доступ
-
Errno::EINVAL -
socketуже привязан к адресу, и протокол не поддерживает привязку к новому sockaddr илиsocketбыл закрыт. -
Errno::EINVAL - длина адреса не является допустимой длиной для семейства адресов
-
Errno::ENAMETOOLONG - длина разрешенного пути превысила PATH_MAX
-
Errno::ENOBUFS - нет доступного буферного пространства
-
Errno::ENOSR - недостаточно ресурсов STREAMS для выполнения операции
-
Errno::ENOTSOCK -
socketне ссылается на сокет -
Errno::EOPNOTSUPP - тип сокета
socketне поддерживает привязку к адресу
В системах на основе Unix, если семейство адресов вызывающего socket равно Socket::AF_UNIX, при неудаче вызова bind могут быть выброшены следующие исключения:
-
Errno::EACCES - права доступа на поиск для компонента пути префикса или права записи к
socketзапрещены -
Errno::EDESTADDRREQ - аргумент sockaddr является указателем null
-
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 в справке функций Microsoft Winsock
static VALUE
sock_connect(VALUE sock, VALUE addr)
{
VALUE rai;
rb_io_t *fptr;
int fd, n;
SockAddrStringValueWithAddrinfo(addr, rai);
addr = rb_str_new4(addr);
GetOpenFile(sock, fptr);
fd = fptr->fd;
n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0);
if (n < 0) {
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
return INT2FIX(n);
} Запрашивает установление соединения с указанным remote_sockaddr. Возвращает 0 при успехе, в противном случае генерируется исключение.
Параметр
-
remote_sockaddr-structsockaddr, содержащийся в строке или объекте Addrinfo
Пример:
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) socket.connect( sockaddr ) socket.write( "GET / HTTP/1.0\r\n\r\n" ) results = socket.read
Исключения на системах Unix
На системах Unix при неудаче вызова connect могут быть сгенерированы следующие системные исключения:
-
Errno::EACCES - запрещён доступ к компоненту префиксного пути или доступ для записи в
socket -
Errno::EADDRINUSE - указанный 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::ENAMETOOLLONG - компонент пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов
-
Errno::ENOENT - компонент пути не указывает на существующий файл или путь пуст
-
Errno::ENOTDIR - компонент префиксного пути пути в sockaddr не является каталогом
Исключения Windows
На системах Windows при неудаче вызова connect могут быть сгенерированы следующие системные исключения:
-
Errno::ENETDOWN - сеть неактивна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется
-
Errno::EINTR - сокет был отменён
-
Errno::EINPROGRESS - блокирующий сокет находится в процессе обработки или поставщик сервиса всё ещё обрабатывает функцию обратного вызова. Или в процессе подключения по вызову connect_nonblock на
socket. -
Errno::EALREADY - см. Errno::EINVAL
-
Errno::EADDRNOTAVAIL - удалённый адрес не является допустимым адресом, например, ADDR_ANY TODO проверить ADDRANY до INADDR_ANY
-
Errno::EAFNOSUPPORT - адреса в указанном семействе не могут использоваться с этим
socket -
Errno::ECONNREFUSED - целевой sockaddr не принимал подключения и отклонил запрос на соединение
-
Errno::EFAULT - внутренний адрес сокета или параметр длины адреса слишком мал или не является допустимой частью адресного пространства пользователя
-
Errno::EINVAL -
socketявляется сокетом, слушающим подключения -
Errno::EISCONN -
socketуже подключен -
Errno::ENETUNREACH - к сети в данный момент нет пути с этого узла
-
Errno::EHOSTUNREACH - маршрут к сети отсутствует
-
Errno::ENOBUFS - нет доступного буфера
-
Errno::ENOTSOCK - аргумент
socketне относится к сокету -
Errno::ETIMEDOUT - попытка подключения закончилась по истечении времени ожидания, прежде чем соединение было установлено.
-
Errno::EWOULDBLOCK - сокет помечен как неблокирующий, и подключение не может быть выполнено немедленно
-
Errno::EACCES - попытка подключения сокета дейтаграммы к широковещательному адресу не удалась
См. также
-
Справочные страницы connect на системах Unix
-
Функция connect в справочнике функций Winsock от Microsoft
# File ext/socket/lib/socket.rb, line 1211 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
Запрашивает установление соединения с указанным remote_sockaddr после установки O_NONBLOCK для базового дескриптора файла. Возвращает 0 при успехе, в противном случае генерируется исключение.
Параметр
# +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object
Пример:
# 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 для исключений, которые могут быть сгенерированы при неудаче вызова connect_nonblock.
#connect_nonblock может генерировать любые ошибки, соответствующие ошибкам вызова connect(2), включая Errno::EINPROGRESS.
Если исключение Errno::EINPROGRESS, оно расширяется IO::WaitWritable. Поэтому IO::WaitWritable можно использовать для перехвата исключений для повторной попытки connect_nonblock.
Указав ключевой аргумент exception в false, вы можете указать, что #connect_nonblock не должен генерировать исключение IO::WaitWritable, а вместо этого вернуть символ :wait_writable.
См. также
# Socket#connect
# File ext/socket/lib/socket.rb, line 462
def ipv6only!
if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
end
end Включает параметр сокета IPV6_V6ONLY, если он доступен.
VALUE
rsock_sock_listen(VALUE sock, VALUE log)
{
rb_io_t *fptr;
int backlog;
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
rb_sys_fail("listen(2)");
return INT2FIX(0);
} Прослушивает подключения, используя указанный int в качестве очереди ожидающих соединений. Вызов listen применяется только в том случае, если socket имеет тип SOCK_STREAM или SOCK_SEQPACKET.
Параметр
-
backlog- максимальная длина очереди для ожидающих подключений.
Пример 1
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
Пример 2 (прослушивание на произвольном порту, только для систем на основе Unix):
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
Исключения для систем на основе Unix
В системах на базе Unix вышеуказанный код будет работать, так как для адреса ADDR_ANY создается новая структура sockaddr с произвольным номером порта, переданным ядром. Он не будет работать в Windows, так как Windows требует, чтобы socket был привязан с помощью вызова bind перед вызовом listen.
Если значение backlog превышает максимальную длину очереди, зависящую от реализации, будет использоваться максимальная длина очереди, определённая реализацией.
В системах на основе Unix при неудачном выполнении вызова listen могут быть подняты следующие системные исключения:
-
Errno::EBADF - аргумент socket не является допустимым дескриптором файла
-
Errno::EDESTADDRREQ - сокет не привязан к локальному адресу, и протокол не поддерживает прослушивание на незакрепленном сокете
-
Errno::EINVAL - сокет уже подключен
-
Errno::ENOTSOCK - аргумент socket не ссылается на сокет
-
Errno::EOPNOTSUPP - протокол сокета не поддерживает прослушивание
-
Errno::EACCES - у вызывающего процесса недостаточно привилегий
-
Errno::EINVAL - сокет был закрыт
-
Errno::ENOBUFS - недостаточно ресурсов в системе для выполнения вызова
Исключения для Windows
В системах Windows при неудачном выполнении вызова listen могут быть подняты следующие системные исключения:
-
Errno::ENETDOWN - сеть недоступна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется. Обычно это происходит во время выполнения bind, но может произойти с задержкой, если вызов bind был к частично подставленному адресу (вовлекая ADDR_ANY), и если конкретный адрес должен быть закреплён в момент вызова listen
-
Errno::EINPROGRESS - выполняется вызов Windows Sockets 1.1 или поставщик услуг всё ещё обрабатывает функцию обратного вызова
-
Errno::EINVAL -
socketне был привязан с помощью вызова bind. -
Errno::EISCONN -
socketуже подключен -
Errno::EMFILE - больше нет доступных дескрипторов сокета
-
Errno::ENOBUFS - нет доступного буфера
-
Errno::ENOTSOC -
socketне является сокетом -
Errno::EOPNOTSUPP - указанный
socketне является типом, который поддерживает метод listen
См. также
-
Руководства по listen в системах на основе Unix
-
Функция listen в справочнике по функциям Winsock Microsoft
static VALUE
sock_recvfrom(int argc, VALUE *argv, VALUE sock)
{
return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
} Принимает до maxlen байтов от socket. flags — ноль или больше MSG_ опций. Первый элемент результата, mesg, — полученные данные. Второй элемент, sender_addrinfo, содержит информацию о протокольном адресе отправителя.
Параметры
-
maxlen- максимальное количество байтов для получения от сокета -
flags- ноль или большеMSG_опций
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client, client_addrinfo = socket.accept
data = client.recvfrom( 20 )[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Watch this get cut short!"
socket.close
Исключения для систем на базе Unix
В системах на основе Unix при неудачном выполнении вызова recvfrom могут быть подняты следующие системные исключения:
-
Errno::EAGAIN - дескриптор файла
socketпомечен как O_NONBLOCK, и данные не ожидают получения; или флаг MSG_OOB установлен, но данные вне очереди недоступны, и либо дескриптор файлаsocketпомечен как O_NONBLOCK, либоsocketне поддерживает ожидание данных вне очереди -
Errno::EWOULDBLOCK - см. Errno::EAGAIN
-
Errno::EBADF -
socketне является допустимым дескриптором файла -
Errno::ECONNRESET - соединение было принудительно закрыто удалённой стороной
-
Errno::EFAULT - внутренний буфер, адрес или длина адреса сокета недоступны или не могут быть записаны
-
Errno::EINTR - сигнал прервал recvfrom до получения каких-либо данных
-
Errno::EINVAL - флаг MSG_OOB установлен, но данные вне очереди недоступны
-
Errno::EIO - произошла ошибка ввода-вывода при чтении или записи в файловую систему
-
Errno::ENOBUFS - недостаточно ресурсов в системе для выполнения операции
-
Errno::ENOMEM - недостаточно памяти для выполнения запроса
-
Errno::ENOSR - недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTCONN - попытка получения данных на сокете в режиме соединения, который не подключен
-
Errno::ENOTSOCK -
socketне ссылается на сокет -
Errno::EOPNOTSUPP - указанные флаги не поддерживаются для данного типа сокета
-
Errno::ETIMEDOUT - соединение истекло во время установления соединения или из-за таймаута передачи в активном соединении
Исключения для Windows
В системах Windows при неудачном выполнении вызова recvfrom могут быть подняты следующие системные исключения:
-
Errno::ENETDOWN - сеть недоступна
-
Errno::EFAULT - внутренний буфер и параметры from в
socketне являются частью адресного пространства пользователя, или внутренний параметр fromlen слишком мал для размещения адреса удалённой стороны -
Errno::EINTR - (блокирующий) вызов был отменён внутренним вызовом функции WinSock WSACancelBlockingCall
-
Errno::EINPROGRESS - выполняется блокирующий вызов Windows Sockets 1.1 или поставщик услуг всё ещё обрабатывает функцию обратного вызова
-
Errno::EINVAL -
socketне был привязан с помощью вызова bind, или был указан неизвестный флаг, или MSG_OOB был указан для сокета с SO_OOBINLINE включённым, или (только для сокетов потокового типа байтов) внутренний параметр len вsocketбыл нулевым или отрицательным -
Errno::EISCONN -
socketуже подключен. Вызов recvfrom запрещён для подключённого сокета в сокете, который ориентирован на соединение или является бессоединением. -
Errno::ENETRESET - соединение было разорвано из-за активности keep-alive, обнаружившей ошибку во время выполнения операции.
-
Errno::EOPNOTSUPP - MSG_OOB был указан, но
socketне является сокетом потокового типа, например, типа SOCK_STREAM. Данные OOB не поддерживаются в области связи, связанной сsocket, илиsocketоднонаправленный и поддерживает только операции отправки -
Errno::ESHUTDOWN -
socketбыл закрыт. После вызова shutdown невозможно вызвать recvfrom на сокете. -
Errno::EWOULDBLOCK -
socketпомечен как неблокирующий, и вызов recvfrom заблокирует. -
Errno::EMSGSIZE - сообщение было слишком большим для указанного буфера и было усечено.
-
Errno::ETIMEDOUT - соединение было разорвано из-за сбоя сети или из-за того, что система на другом конце отключилась без уведомления
-
Errno::ECONNRESET - виртуальный канал был сброшен удалённой стороной, выполнившей жёсткое или аварийное закрытие. Приложение должно закрыть сокет; он больше не используется. В случае сокета UDP-датаграммы эта ошибка указывает, что предыдущая операция отправки привела к сообщению ICMP Port Unreachable.
# File ext/socket/lib/socket.rb, line 534 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, #recvfrom_nonblock возвращает пустую строку в качестве данных. Значение зависит от сокета: EOF для TCP, пустой пакет для UDP и т. д.
Параметры
-
maxlen— максимальное количество байтов для приема от сокета -
flags— ноль или больше изMSG_опций -
outbuf— буфер-строка назначения -
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 за исключениями, которые могут быть выброшены, если вызов recvfrom_nonblock завершится неудачно.
#recvfrom_nonblock может вызвать любое исключение, соответствующее ошибке recvfrom(2), включая Errno::EWOULDBLOCK.
Если исключение — Errno::EWOULDBLOCK или Errno::EAGAIN, оно дополняется IO::WaitReadable. Таким образом, IO::WaitReadable можно использовать для перехвата исключений и повторной попытки вызова recvfrom_nonblock.
Указав ключевой аргумент exception в false, вы можете указать, что #recvfrom_nonblock не должен вызывать исключение IO::WaitReadable, а вместо этого должен возвращать символ :wait_readable.
См. также
static VALUE
sock_sysaccept(VALUE sock)
{
rb_io_t *fptr;
VALUE sock2;
union_sockaddr buf;
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
sock2 = rsock_s_accept(0,fptr->fd,&buf.addr,&len);
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
} Принимает входящее соединение, возвращая массив, содержащий дескриптор файла (целое число) входящего соединения, 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
Обратитесь к #accept за исключениями, которые могут быть выброшены, если вызов sysaccept завершится неудачно.
См. также
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.