класс Socket
Class Socket предоставляет доступ к реализациям сокетов операционной системы. Его можно использовать для предоставления функциональности, специфичной для операционной системы, отличной от классов сокетов, специфичных для протокола.
Константы, определенные в Socket::Constants, также определены в Socket. Например, Socket::AF_INET можно использовать так же, как Socket::Constants::AF_INET. Список констант см. в Socket::Constants.
Что такое сокет?
Сокеты — это конечные точки двунаправленного канала связи. Сокеты могут взаимодействовать внутри процесса, между процессами на одном компьютере или между различными компьютерами. Существует много типов сокетов: TCPSocket, UDPSocket или UNIXSocket, например.
Сокеты имеют свой собственный словарь терминов:
домен: Семейство протоколов:
тип: Тип коммуникации между двумя конечными точками, обычно
протокол: Обычно ноль. Это может быть использовано для идентификации варианта протокола.
имя хоста: Идентификатор сетевого интерфейса:
-
строка (имя хоста, IPv4 или IPv6 адрес или
broadcast, которая указывает на адрес широковещательной передачи) -
строка нулевой длины, которая указывает на
INADDR_ANY -
целое число (интерпретируется как двоичный адрес в порядке байтов хоста).
Быстрый старт
Многие классы, такие как TCPSocket, UDPSocket или UNIXSocket, облегчают использование сокетов по сравнению с эквивалентным интерфейсом программирования на C.
Давайте создадим интернет-сокет, используя IPv4 протокол, по аналогии с C:
require 'socket' s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM s.connect Socket.pack_sockaddr_in(80, 'example.com')
Вы также можете использовать класс TCPSocket:
s = TCPSocket.new 'example.com', 80
Простой сервер может выглядеть так:
require 'socket'
server = TCPServer.new 2000 # Server bound to port 2000
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
Простой клиент может выглядеть так:
require 'socket' s = TCPSocket.new 'localhost', 2000 while line = s.gets # Read lines from socket puts line # and print them end s.close # close socket when done
Обработка Exception
Реализация Ruby’s Socket генерирует исключения на основе ошибки, сгенерированной системой. Именно поэтому методы документированы таким образом, чтобы изолировать исключения систем Unix от исключений Windows. Если вам нужна дополнительная информация об определенном исключении, пожалуйста, обратитесь к руководству Unix или справочнику Windows WinSock.
Удобные методы
Хотя общий способ создания сокета — Socket.new, для большинства случаев существуют несколько методов создания сокетов.
- TCP-клиентский сокет
- TCP-серверный сокет
- UNIX-клиентский сокет
- UNIX-серверный сокет
Документация подготовлена
-
Заком Денисом
-
Самом Робертсом
-
Программирование на Ruby от The Pragmatic Bookshelf.
Большая часть материала в этой документации взята с разрешения из Программирования на Ruby от The Pragmatic Bookshelf.
Константы
- 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 Direct Data Link Interface
- AF_E164
-
Рекомендация CCITT (ITU-T) E.164
- AF_ECMA
-
Протоколы Европейских производителей компьютеров
- AF_HYLINK
-
Протокол NSC Hyperchannel
- AF_IB
-
Родная адресация InfiniBand
- AF_IMPLINK
-
Протокол ARPANET IMP
- AF_INET
-
Протокол IPv4
- AF_INET6
-
Протокол IPv6
- AF_IPX
-
Протокол IPX
- AF_ISDN
-
Цифровая сеть с интегрированными службами
- AF_ISO
-
Протоколы ISO Открытых систем
- AF_KCM
-
Интерфейс KCM (ядерный мультиплексор соединений)
- AF_KEY
-
Протокол управления ключами, изначально разработанный для использования с IPsec
- AF_LAT
-
Протокол локальной транспортной сети
- AF_LINK
-
Интерфейс уровня канала
- AF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- AF_LOCAL
-
Внутрисистемные протоколы
- AF_MAX
-
Максимальная семейство адресов для данной платформы
- AF_MPLS
-
Мультипротокольная коммутация меток
- AF_NATM
-
Родной доступ ATM
- AF_NDRV
-
Непосредственный доступ к драйверу сети
- AF_NETBIOS
-
NetBIOS
- AF_NETGRAPH
-
Сокеты Netgraph
- AF_NETLINK
-
Устройство ядра пользовательского интерфейса
- AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы ISO Открытых систем
- AF_PACKET
-
Непосредственный доступ к уровню канала
- AF_PPP
-
Протокол точка-точка
- AF_PPPOX
-
Общий транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- AF_PUP
-
Протокол PARC Universal Packet
- AF_RDS
-
Протокол Reliable Datagram Sockets (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
-
Принимать 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
-
очередь tx аппаратного обеспечения заполнена
- IFF_DRV_RUNNING
-
ресурсы выделены
- IFF_DYING
-
интерфейс завершает работу
- IFF_DYNAMIC
-
устройство с коммутацией соединения с изменяемыми адресами
- IFF_EBRIDGE
-
устройство эфирного моста
- IFF_ECHO
-
отправленные пакеты эха
- IFF_ISATAP
-
Интерфейс ISATAP (RFC4214)
- IFF_LINK0
-
бит 0, определенный для уровня канала
- IFF_LINK1
-
бит 1, определенный для уровня канала
- IFF_LINK2
-
бит 2, определенный для уровня канала
- IFF_LIVE_ADDR_CHANGE
-
изменение аппаратного адреса во время работы
- IFF_LOOPBACK
-
петлевое соединение сети
- IFF_LOWER_UP
-
драйвер сигнализирует о готовности L1
- IFF_MACVLAN_PORT
-
устройство, используемое в качестве порта macvlan
- IFF_MASTER
-
мастер балансировщика нагрузки
- IFF_MASTER_8023AD
-
мастер бондинга, 802.3ad.
- IFF_MASTER_ALB
-
мастер бондинга, балансировщик ALB.
- IFF_MASTER_ARPMON
-
мастер бондинга, используется ARP мониторинг
- IFF_MONITOR
-
режим мониторинга, запрошенный пользователем
- IFF_MULTICAST
-
поддерживает multicast
- IFF_NOARP
-
отсутствует протокол разрешения адресов
- IFF_NOTRAILERS
-
избежать использования трейлеров
- IFF_OACTIVE
-
передача в процессе
- IFF_OVS_DATAPATH
-
устройство, используемое в качестве порта Open vSwitch datapath
- IFF_POINTOPOINT
-
точка-точка соединение
- IFF_PORTSEL
-
можно установить тип среды
- IFF_PPROMISC
-
режим promiscuous, запрошенный пользователем
- IFF_PROMISC
-
прием всех пакетов
- IFF_RENAMING
-
интерфейс переименовывается
- IFF_ROUTE
-
установлен маршрут
- IFF_RUNNING
-
ресурсы выделены
- IFF_SIMPLEX
-
не может услышать собственные передачи
- IFF_SLAVE
-
раб балансировщика нагрузки
- IFF_SLAVE_INACTIVE
-
раб бондинга не является текущим активным
- IFF_SLAVE_NEEDARP
-
нужны 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
-
Мультиадресная группа для всех систем в этом подмножестве
- INADDR_ANY
-
Сокет, привязанный к
INADDR_ANY, получает пакеты со всех интерфейсов и отправляет их с использованием стандартного IP-адреса. - INADDR_BROADCAST
-
Адрес широковещательной сети.
- INADDR_LOOPBACK
-
Адрес обратной связи.
- INADDR_MAX_LOCAL_GROUP
-
Последняя локальная группа многоадресной рассылки.
- INADDR_NONE
-
Маска для сопоставления отсутствия валидного IP-адреса.
- INADDR_UNSPEC_GROUP
-
Зарезервированная группа многоадресной рассылки.
- INET6_ADDRSTRLEN
-
Максимальная длина строки IPv6-адреса.
- INET_ADDRSTRLEN
-
Максимальная длина строки IPv4-адреса.
- IPPORT_RESERVED
-
Минимальный стандартный адрес для привязки или подключения.
- IPPORT_USERRESERVED
-
Максимальный стандартный адрес для привязки или подключения.
- IPPROTO_AH
-
Заголовок аутентификации IP6.
- IPPROTO_BIP
- IPPROTO_DSTOPTS
-
Опция назначения IP6.
- IPPROTO_EGP
-
Протокол внешних шлюзов.
- IPPROTO_EON
-
ISO cnlp
- IPPROTO_ESP
-
Зашифрованная полезная нагрузка IP6.
- IPPROTO_FRAGMENT
-
Заголовок фрагментации IP6.
- IPPROTO_GGP
-
Протокол шлюза-шлюзу.
- IPPROTO_HELLO
-
Протокол маршрутизации «hello».
- IPPROTO_HOPOPTS
-
Опции IP6 "прыжок за прыжком".
- IPPROTO_ICMP
-
Протокол управляющих сообщений.
- IPPROTO_ICMPV6
-
ICMP6.
- IPPROTO_IDP
-
XNS IDP.
- IPPROTO_IGMP
-
Протокол управления группами.
- IPPROTO_IP
-
Протокол-заглушка для IP.
- IPPROTO_IPV6
-
Заголовок IP6.
- IPPROTO_MAX
-
Максимальная константа IPPROTO.
- IPPROTO_ND
-
Протокол Sun net disk.
- IPPROTO_NONE
-
IP6 без следующего заголовка.
- IPPROTO_PUP
-
Протокол универсальных пакетов PARC.
- IPPROTO_RAW
-
Сырой пакет IP.
- IPPROTO_ROUTING
-
Заголовок маршрутизации IP6.
- IPPROTO_TCP
-
TCP.
- IPPROTO_TP
-
Класс 4 ISO транспортного протокола.
- IPPROTO_UDP
-
UDP.
- IPPROTO_XTP
-
Протокол передачи Xpress.
- 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
-
Получение текущего пути MTU.
- IPV6_PKTINFO
-
Получение информации о пакете с помощью дейтаграммы.
- IPV6_RECVDSTOPTS
-
Получение всех опций IP6 для ответа.
- 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 при wildcard-привязке.
- 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
-
Максимальный размер пакета (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
-
Получение 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_ALG
-
Интерфейс к API шифрования ядра
- PF_APPLETALK
-
Протокол AppleTalk
- PF_ATM
-
Режим асинхронной передачи
- PF_AX25
-
Протокол AX.25
- PF_BLUETOOTH
-
Протокол сокетов низкого уровня Bluetooth
- PF_CAN
-
Протокол шины CAN автомобильной системы
- PF_CCITT
-
Протоколы CCITT (сейчас ITU-T)
- PF_CHAOS
-
Протоколы MIT CHAOS
- PF_CNT
-
Технология компьютерных сетей
- PF_COIP
-
Ориентированный на соединение IP
- PF_DATAKIT
-
Протокол Datakit
- PF_DEC
-
Протокол DECnet
- PF_DECnet
-
Протокол DECnet
- PF_DLI
-
Протокол DEC Direct Data Link Interface
- PF_ECMA
-
Протоколы производителей компьютерной техники Европы
- PF_HYLINK
-
Протокол NSC Hyperchannel
- PF_IB
-
Нативное адресование InfiniBand
- PF_IMPLINK
-
Протокол ARPANET IMP
- PF_INET
-
Протокол IPv4
- PF_INET6
-
Протокол IPv6
- PF_IPX
-
Протокол IPX
- PF_ISDN
-
Цифровая сеть с интегрированными услугами
- PF_ISO
-
Протоколы ISO Open Systems Interconnection
- PF_KCM
-
Интерфейс KCM (kernel connection multiplexor)
- PF_KEY
-
Протокол управления ключами, первоначально разработанный для использования с IPsec
- PF_LAT
-
Протокол локальной транспортной системы
- PF_LINK
-
Интерфейс уровня канала
- PF_LLC
-
Протокол управления логическим каналом (IEEE 802.2 LLC)
- PF_LOCAL
-
Внутрихостовые протоколы
- PF_MAX
-
Максимальная адресная семья для данной платформы
- PF_MPLS
-
Мультипротокольный коммутируемый обмен метками
- PF_NATM
-
Нативный доступ ATM
- PF_NDRV
-
Необработанный доступ к драйверу сети
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Сокеты Netgraph
- PF_NETLINK
-
Устройство интерфейса ядра пользователя
- PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы ISO Open Systems Interconnection
- PF_PACKET
-
Прямой доступ к уровню канала связи
- PF_PIP
-
Помощь в идентификации пакетов PIP
- PF_PPP
-
Протокол точка-точка
- PF_PPPOX
-
Обобщенный транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- PF_PUP
-
Протокол PARC Universal Packet
- PF_RDS
-
Протокол надежных сокетов дейтаграмм (RDS)
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_RTIP
-
Помощь в идентификации пакетов RTIP
- PF_SIP
-
Простой протокол Интернет
- 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
-
Протокол экспресс-передачи
- 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_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, подключенный к очереди приёма (Linux 4.12)
- SO_KEEPALIVE
-
Поддерживать подключение живым
- SO_LINGER
-
Задерживаться при закрытии, если имеются данные
- SO_LOCK_FILTER
-
Заблокировать фильтр, присоединённый к сокету (Linux 3.9)
- SO_MAC_EXEMPT
-
Исключение обязательного контроля доступа для неуказанных коллег
- SO_MARK
-
Установить метку для маршрутизации на основе меток (Linux 2.6.25)
- SO_MAX_PACING_RATE
-
Ограничить скорость, вычисляемую транспортным уровнем. [байты в секунду] (Linux 3.13)
- SO_NKE
-
Установить расширение ядра сети на уровне сокета
- SO_NOFCS
-
Установить netns для сокета (Linux 3.4)
- SO_NOSIGPIPE
-
Не отправлять SIGPIPE при EPIPE
- SO_NO_CHECK
-
Отключить контрольные суммы
- SO_NREAD
-
Получить количество байтов первого пакета
- SO_OOBINLINE
-
Оставить полученные данные вне очереди в строке
- SO_PASSCRED
-
Получать сообщения
SCM_CREDENTIALS - SO_PASSSEC
-
Включить/выключить передачу контекста безопасности (Linux 2.6.18)
- SO_PEEK_OFF
-
Установить смещение для 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_RTABLE
-
Установить таблицу маршрутизации для этого сокета (OpenBSD)
- SO_RXQ_OVFL
-
Включить/выключить cmsg для количества потерянных пакетов (Linux 2.6.33)
- SO_SECURITY_AUTHENTICATION
- SO_SECURITY_ENCRYPTION_NETWORK
- SO_SECURITY_ENCRYPTION_TRANSPORT
- SO_SELECT_ERR_QUEUE
-
Сделать select() обнаруживающим очередь ошибок сокета с ошибочными дескрипторами (Linux 3.10)
- SO_SETFIB
-
Установить связанную таблицу маршрутизации для сокета (FreeBSD)
- SO_SNDBUF
-
Размер буфера отправки
- SO_SNDBUFFORCE
-
Размер буфера отправки без ограничения wmem_max (Linux 2.6.14)
- SO_SNDLOWAT
-
Нижняя граница для отправки данных
- SO_SNDTIMEO
-
Таймаут отправки
- SO_TIMESTAMP
-
Принимать отметку времени с дейтаграммами (timeval)
- SO_TIMESTAMPING
-
Маркировка времени входящих и исходящих пакетов (Linux 2.6.30)
- SO_TIMESTAMPNS
-
Принимать отметку времени в наносекундах с дейтаграммами (timespec)
- SO_TYPE
-
Получить тип сокета
- SO_USELOOPBACK
-
Обойти оборудование, если возможно
- SO_USER_COOKIE
-
Установка идентификатора в основном для ipfw
- SO_WANTMORE
-
Подсказка о готовности дополнительных данных
- SO_WANTOOBFLAG
-
Данные вне очереди требуются в MSG_FLAG при приёме
- SO_WIFI_STATUS
-
Включить/выключить cmsg для статуса Wi-Fi (Linux 3.3)
- TCP_CONGESTION
-
Алгоритм управления заторами TCP (Linux 2.6.13, glibc 2.6)
- TCP_CONNECTION_INFO
-
Получить информацию об этом сокете (macOS)
- TCP_COOKIE_TRANSACTIONS
-
Транзакции TCP Cookie (Linux 2.6.33, glibc 2.18)
- TCP_CORK
-
Не отправлять частичные кадры (Linux 2.2, glibc 2.2)
- TCP_DEFER_ACCEPT
-
Не уведомлять сокет в ожидании до готовности данных (Linux 2.4, glibc 2.2)
- TCP_FASTOPEN
-
Уменьшить этап процесса рукопожатия (Linux 3.7, glibc 2.18)
- TCP_INFO
-
Получить информацию об этом сокете (Linux 2.4, glibc 2.2)
- TCP_KEEPALIVE
-
Время простоя перед отправкой зондирующих пакетов keepalive (macOS)
- TCP_KEEPCNT
-
Максимальное количество зондирующих пакетов keepalive, разрешенных перед разрывом соединения (Linux 2.4, glibc 2.2)
- TCP_KEEPIDLE
-
Время простоя перед отправкой зондирующих пакетов keepalive (Linux 2.4, glibc 2.2)
- TCP_KEEPINTVL
-
Интервал между зондирующими пакетами keepalive (Linux 2.4, glibc 2.2)
- TCP_LINGER2
-
Срок жизни осиротевших сокетов FIN_WAIT2 (Linux 2.4, glibc 2.2)
- TCP_MAXSEG
-
Установить максимальный размер сегмента
- TCP_MD5SIG
-
Использовать хэши MD5 (RFC2385, Linux 2.6.20, glibc 2.7)
- TCP_NODELAY
-
Не задерживать отправку для объединения пакетов
- TCP_NOOPT
-
Не использовать опции TCP
- TCP_NOPUSH
-
Не отправлять последний блок записи
- TCP_QUEUE_SEQ
-
Последовательность очереди для режима восстановления (Linux 3.5, glibc 2.18)
- TCP_QUICKACK
-
Включить режим quickack (Linux 2.4.4, glibc 2.3)
- TCP_REPAIR
-
Режим восстановления (Linux 3.5, glibc 2.18)
- TCP_REPAIR_OPTIONS
-
Опции для режима восстановления (Linux 3.5, glibc 2.18)
- TCP_REPAIR_QUEUE
-
Очередь для режима восстановления (Linux 3.5, glibc 2.18)
- TCP_SYNCNT
-
Количество повторных передач SYN перед разрывом соединения (Linux 2.4, glibc 2.2)
- TCP_THIN_DUPACK
-
Обработка дублированных подтверждений для тонких потоков (Linux 2.6.34, glibc 2.18)
- TCP_THIN_LINEAR_TIMEOUTS
-
Линейные таймауты для тонких потоков (Linux 2.6.34, glibc 2.18)
- TCP_TIMESTAMP
-
TCP метка времени (Linux 3.9, glibc 2.18)
- TCP_USER_TIMEOUT
-
Максимальный таймаут перед прерыванием TCP соединения (Linux 2.6.37, glibc 2.18)
- TCP_WINDOW_CLAMP
-
Ограничить размер рекламируемого окна (Linux 2.4, glibc 2.2)
- UDP_CORK
-
Не отправлять частичные кадры (Linux 2.5.44, glibc 2.11)
Публичные методы класса
# File ext/socket/lib/socket.rb, line 805
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
sockets.flatten!(1)
if sockets.empty?
raise ArgumentError, "no sockets"
end
loop {
readable, _, _ = IO.select(sockets)
readable.each {|r|
sock, addr = r.accept_nonblock(exception: false)
next if sock == :wait_readable
yield sock, addr
}
}
end Передает сокет и адрес клиента для каждого соединения, принятого через заданные сокеты.
Аргументами является список сокетов. Отдельный аргумент должен быть сокетом или массивом сокетов.
Этот метод выполняет блок последовательно. Это означает, что следующее соединение не принимается до тех пор, пока блок не вернет значение. Таким образом, для обслуживания нескольких клиентов одновременно следует использовать конкурентный механизм, например, поток.
static VALUE
sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _)
{
VALUE host, port, family, socktype, protocol, flags, ret, revlookup;
struct addrinfo hints;
struct rb_addrinfo *res;
int norevlookup;
rb_scan_args(argc, argv, "25", &host, &port, &family, &socktype, &protocol, &flags, &revlookup);
MEMZERO(&hints, struct addrinfo, 1);
hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family);
if (!NIL_P(socktype)) {
hints.ai_socktype = rsock_socktype_arg(socktype);
}
if (!NIL_P(protocol)) {
hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
hints.ai_flags = NUM2INT(flags);
}
if (NIL_P(revlookup) || !rsock_revlookup_flag(revlookup, &norevlookup)) {
norevlookup = rsock_do_not_reverse_lookup;
}
res = rsock_getaddrinfo(host, port, &hints, 0);
ret = make_addrinfo(res, norevlookup);
rb_freeaddrinfo(res);
return ret;
} Получает информацию об адресе для nodename:servname.
Обратите внимание, что Addrinfo.getaddrinfo предоставляет тот же функционал в объектно-ориентированном стиле.
family должен быть семейством адресов, таким как: :INET, :INET6 и т.д.
socktype должен быть типом сокета, таким как: :STREAM, :DGRAM, :RAW и т.д.
protocol должен быть протоколом, определенным в семействе, и по умолчанию равен 0 для семейства.
flags должен быть побитовым ИЛИ констант Socket::AI_*.
Socket.getaddrinfo("www.ruby-lang.org", "http", nil, :STREAM)
#=> [["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68", 2, 1, 6]] # PF_INET/SOCK_STREAM/IPPROTO_TCP
Socket.getaddrinfo("localhost", nil)
#=> [["AF_INET", 0, "localhost", "127.0.0.1", 2, 1, 6], # PF_INET/SOCK_STREAM/IPPROTO_TCP
# ["AF_INET", 0, "localhost", "127.0.0.1", 2, 2, 17], # PF_INET/SOCK_DGRAM/IPPROTO_UDP
# ["AF_INET", 0, "localhost", "127.0.0.1", 2, 3, 0]] # PF_INET/SOCK_RAW/IPPROTO_IP
reverse_lookup определяет форму третьего элемента и должен быть одним из следующих. Если reverse_lookup пропущен, значение по умолчанию равно nil.
+true+, +:hostname+: hostname is obtained from numeric address using reverse lookup, which may take a time. +false+, +:numeric+: hostname is the same as numeric address. +nil+: obey to the current +do_not_reverse_lookup+ flag.
Если предпочтительнее объект Addrinfo, используйте Addrinfo.getaddrinfo.
static VALUE
sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
{
VALUE addr, family;
struct hostent *h;
char **pch;
VALUE ary, names;
int t = AF_INET;
rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead.");
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
t = AF_INET6;
}
#endif
h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
extern int h_errno;
rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
rb_ary_push(ary, rb_str_new2(h->h_name));
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
for (pch = h->h_aliases; *pch; pch++) {
rb_ary_push(names, rb_str_new2(*pch));
}
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
#endif
return ary;
} Используйте Addrinfo#getnameinfo вместо этого. Этот метод устарел по следующим причинам:
-
Нестандартное представление адреса: 4/16-байтовая двоичная строка для представления адреса IPv4/IPv6.
-
gethostbyaddr() может занимать много времени и блокировать другие потоки. (GVL не может быть освобожден, поскольку gethostbyname() не является потокобезопасным.)
-
Этот метод использует функцию gethostbyname(), уже удаленную из POSIX.
Этот метод получает информацию о хосте для address.
p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
#=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
["localhost", [], 2, "\x7F\x00\x00\x01"]
p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
#=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
struct rb_addrinfo *res =
rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME);
return rsock_make_hostent(host, res, sock_sockaddr);
} Используйте Addrinfo.getaddrinfo вместо этого. Этот метод устарел по следующим причинам:
-
3-й элемент результата — это семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Нестандартное представление адреса: 4/16-байтовая двоичная строка для представления адреса IPv4/IPv6.
-
gethostbyname() может занимать много времени и блокировать другие потоки. (GVL не может быть освобожден, поскольку gethostbyname() не является потокобезопасным.)
-
Этот метод использует функцию gethostbyname(), уже удаленную из POSIX.
Этот метод получает информацию о хосте для hostname.
p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
static VALUE
sock_gethostname(VALUE obj)
{
#if defined(NI_MAXHOST)
# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST
#elif defined(HOST_NAME_MAX)
# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX
#else
# define RUBY_MAX_HOST_NAME_LEN 1024
#endif
long len = RUBY_MAX_HOST_NAME_LEN;
VALUE name;
name = rb_str_new(0, len);
while (gethostname(RSTRING_PTR(name), len) < 0) {
int e = errno;
switch (e) {
case ENAMETOOLONG:
#ifdef __linux__
case EINVAL:
/* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
#endif
break;
default:
rb_syserr_fail(e, "gethostname(3)");
}
rb_str_modify_expand(name, len);
len += len;
}
rb_str_resize(name, strlen(RSTRING_PTR(name)));
return name;
} Возвращает имя хоста.
p Socket.gethostname #=> "hal"
Обратите внимание, что не гарантируется возможность преобразования в IP-адрес с помощью gethostbyname, getaddrinfo и т.д. Если вам нужен локальный IP-адрес, используйте Socket.ip_address_list.
static VALUE
socket_s_getifaddrs(VALUE self)
{
return rsock_getifaddrs();
} Возвращает массив адресов интерфейсов. Элемент массива является экземпляром Socket::Ifaddr.
Этот метод может использоваться для поиска интерфейсов с поддержкой многоадресной рассылки:
pp Socket.getifaddrs.reject {|ifaddr|
!ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
}.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
#=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
# ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
Пример результата на GNU/Linux:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
Пример результата на FreeBSD:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
static VALUE
sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
{
VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
char hbuf[1024], pbuf[1024];
int fl;
struct rb_addrinfo *res = NULL;
struct addrinfo hints, *r;
int error, saved_errno;
union_sockaddr ss;
struct sockaddr *sap;
socklen_t salen;
sa = flags = Qnil;
rb_scan_args(argc, argv, "11", &sa, &flags);
fl = 0;
if (!NIL_P(flags)) {
fl = NUM2INT(flags);
}
tmp = rb_check_sockaddr_string_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
rb_raise(rb_eTypeError, "sockaddr length too big");
}
memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
}
sap = &ss.addr;
salen = RSTRING_SOCKLEN(sa);
goto call_nameinfo;
}
tmp = rb_check_array_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
MEMZERO(&hints, struct addrinfo, 1);
if (RARRAY_LEN(sa) == 3) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 2);
}
else if (RARRAY_LEN(sa) >= 4) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 3);
if (NIL_P(host)) {
host = RARRAY_AREF(sa, 2);
}
else {
/*
* 4th element holds numeric form, don't resolve.
* see rsock_ipaddr().
*/
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
hints.ai_flags |= AI_NUMERICHOST;
#endif
}
}
else {
rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
RARRAY_LEN(sa));
}
hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
/* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
res = rsock_getaddrinfo(host, port, &hints, 0);
sap = res->ai->ai_addr;
salen = res->ai->ai_addrlen;
}
else {
rb_raise(rb_eTypeError, "expecting String or Array");
}
call_nameinfo:
error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf), fl);
if (error) goto error_exit_name;
if (res) {
for (r = res->ai->ai_next; r; r = r->ai_next) {
char hbuf2[1024], pbuf2[1024];
sap = r->ai_addr;
salen = r->ai_addrlen;
error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
pbuf2, sizeof(pbuf2), fl);
if (error) goto error_exit_name;
if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
rb_freeaddrinfo(res);
rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
}
}
rb_freeaddrinfo(res);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
error_exit_name:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_resolution_error("getnameinfo", error);
UNREACHABLE_RETURN(Qnil);
} Получает информацию об имени для sockaddr.
sockaddr должен быть одним из следующих.
-
упакованная строка sockaddr, например
Socket.sockaddr_in(80, “127.0.0.1”) -
массив из 3 элементов, например [“AF_INET”, 80, “127.0.0.1”]
-
массив из 4 элементов, например [“AF_INET”, 80, игнорируется, “127.0.0.1”]
flags должен быть побитовым ИЛИ констант Socket::NI_*.
Примечание: последняя форма совместима с IPSocket#addr и IPSocket#peeraddr.
Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"]) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"]
Если предпочтительнее объект Addrinfo, используйте Addrinfo#getnameinfo.
static VALUE
sock_s_getservbyname(int argc, VALUE *argv, VALUE _)
{
VALUE service, proto;
struct servent *sp;
long port;
const char *servicename, *protoname = "tcp";
rb_scan_args(argc, argv, "11", &service, &proto);
StringValue(service);
if (!NIL_P(proto)) StringValue(proto);
servicename = StringValueCStr(service);
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyname(servicename, protoname);
if (sp) {
port = ntohs(sp->s_port);
}
else {
char *end;
port = STRTOUL(servicename, &end, 0);
if (*end != '\0') {
rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
}
}
return INT2FIX(port);
} Получает номер порта для service_name.
Если protocol_name не указан, предполагается “tcp”.
Socket.getservbyname("smtp") #=> 25
Socket.getservbyname("shell") #=> 514
Socket.getservbyname("syslog", "udp") #=> 514
static VALUE
sock_s_getservbyport(int argc, VALUE *argv, VALUE _)
{
VALUE port, proto;
struct servent *sp;
long portnum;
const char *protoname = "tcp";
rb_scan_args(argc, argv, "11", &port, &proto);
portnum = NUM2LONG(port);
if (portnum != (uint16_t)portnum) {
const char *s = portnum > 0 ? "big" : "small";
rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
}
if (!NIL_P(proto)) protoname = StringValueCStr(proto);
sp = getservbyport((int)htons((uint16_t)portnum), protoname);
if (!sp) {
rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
}
return rb_str_new2(sp->s_name);
} Получает номер порта для port.
Если protocol_name не указан, предполагается “tcp”.
Socket.getservbyport(80) #=> "www" Socket.getservbyport(514, "tcp") #=> "shell" Socket.getservbyport(514, "udp") #=> "syslog"
static VALUE
socket_s_ip_address_list(VALUE self)
{
#if defined(HAVE_GETIFADDRS)
struct ifaddrs *ifp = NULL;
struct ifaddrs *p;
int ret;
VALUE list;
ret = getifaddrs(&ifp);
if (ret == -1) {
rb_sys_fail("getifaddrs");
}
list = rb_ary_new();
for (p = ifp; p; p = p->ifa_next) {
if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) {
struct sockaddr *addr = p->ifa_addr;
#if defined(AF_INET6) && defined(__sun)
/*
* OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local
* address with sin6_scope_id == 0.
* So fill it from the interface name (ifa_name).
*/
struct sockaddr_in6 addr6;
if (addr->sa_family == AF_INET6) {
socklen_t len = (socklen_t)sizeof(struct sockaddr_in6);
memcpy(&addr6, addr, len);
addr = (struct sockaddr *)&addr6;
if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
addr6.sin6_scope_id == 0) {
unsigned int ifindex = if_nametoindex(p->ifa_name);
if (ifindex != 0) {
addr6.sin6_scope_id = ifindex;
}
}
}
#endif
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
}
freeifaddrs(ifp);
return list;
#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
/* Solaris if_tcp(7P) */
int fd = -1;
int ret;
struct lifnum ln;
struct lifconf lc;
const char *reason = NULL;
int save_errno;
int i;
VALUE list = Qnil;
lc.lifc_buf = NULL;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
memset(&ln, 0, sizeof(ln));
ln.lifn_family = AF_UNSPEC;
ret = ioctl(fd, SIOCGLIFNUM, &ln);
if (ret == -1) {
reason = "SIOCGLIFNUM";
goto finish;
}
memset(&lc, 0, sizeof(lc));
lc.lifc_family = AF_UNSPEC;
lc.lifc_flags = 0;
lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
lc.lifc_req = xmalloc(lc.lifc_len);
ret = ioctl(fd, SIOCGLIFCONF, &lc);
if (ret == -1) {
reason = "SIOCGLIFCONF";
goto finish;
}
list = rb_ary_new();
for (i = 0; i < ln.lifn_count; i++) {
struct lifreq *req = &lc.lifc_req[i];
if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
if (req->lifr_addr.ss_family == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id == 0) {
struct lifreq req2;
memcpy(req2.lifr_name, req->lifr_name, LIFNAMSIZ);
ret = ioctl(fd, SIOCGLIFINDEX, &req2);
if (ret == -1) {
reason = "SIOCGLIFINDEX";
goto finish;
}
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index;
}
rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen));
}
}
finish:
save_errno = errno;
xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
struct ifconf conf;
struct ifreq *req;
VALUE list = Qnil;
const char *reason = NULL;
int save_errno;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
retry:
conf.ifc_len = bufsize;
conf.ifc_req = (struct ifreq *)buf;
/* fprintf(stderr, "bufsize: %d\n", bufsize); */
ret = ioctl(fd, SIOCGIFCONF, &conf);
if (ret == -1) {
reason = "SIOCGIFCONF";
goto finish;
}
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
if (bufsize < conf.ifc_len) {
/* NetBSD returns required size for all interfaces. */
bufsize = conf.ifc_len + EXTRA_SPACE;
}
else {
bufsize = bufsize << 1;
}
if (buf == initbuf)
buf = NULL;
buf = xrealloc(buf, bufsize);
goto retry;
}
close(fd);
fd = -1;
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
(sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
finish:
save_errno = errno;
if (buf != initbuf)
xfree(buf);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_unicast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
int dummy1;
int dummy2;
int dummy3;
long dummy4;
long dummy5;
long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_anycast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_addresses_st *Next;
void *dummy1;
ip_adapter_unicast_address_t *FirstUnicastAddress;
ip_adapter_anycast_address_t *FirstAnycastAddress;
void *dummy2;
void *dummy3;
void *dummy4;
void *dummy5;
void *dummy6;
BYTE dummy7[8];
DWORD dummy8;
DWORD dummy9;
DWORD dummy10;
DWORD IfType;
int OperStatus;
DWORD dummy12;
DWORD dummy13[16];
void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
GetAdaptersAddresses_t pGetAdaptersAddresses;
ULONG len;
DWORD ret;
ip_adapter_addresses_t *adapters;
VALUE list;
h = LoadLibrary("iphlpapi.dll");
if (!h)
rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
FreeLibrary(h);
rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
ip_adapter_unicast_address_t *uni;
ip_adapter_anycast_address_t *any;
if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
continue;
for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
}
for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
}
}
FreeLibrary(h);
return list;
#endif
} Возвращает локальные IP-адреса в виде массива.
Массив содержит объекты Addrinfo.
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
#<Addrinfo: 192.168.0.128>,
#<Addrinfo: ::1>,
...] static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
VALUE domain, type, protocol;
int fd;
int d, t;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
return rsock_init_sock(sock, fd);
} Создает новый объект сокета.
domain должен быть областью связи, такой как: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, таким как: :STREAM, :DGRAM, :RAW и т.д.
protocol является необязательным и должен быть протоколом, определенным в домене. Если протокол не указан, внутренне используется 0.
Socket.new(:INET, :STREAM) # TCP socket Socket.new(:INET, :DGRAM) # UDP socket Socket.new(:UNIX, :STREAM) # UNIX stream socket Socket.new(:UNIX, :DGRAM) # UNIX datagram socket
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
return addr;
} Упаковывает port и host как строку sockaddr AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
return addr;
} Упаковывает path как строку sockaddr AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создает пару сокетов, соединенных друг с другом.
domain должен быть областью связи, такой как: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, таким как: :STREAM, :DGRAM, :RAW и т.д.
protocol должен быть протоколом, определенным в домене, по умолчанию равен 0 для домена.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
return addr;
} Упаковывает port и host в строку sockaddr AF_INET/AF_INET6.
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
struct sockaddr_un sockaddr;
VALUE addr;
StringValue(path);
INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
(size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
}
memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
return addr;
} Упаковывает path в строку sockaddr AF_UNIX.
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
VALUE domain, type, protocol;
int d, t, p, sp[2];
int ret;
VALUE s1, s2, r;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
p = NUM2INT(protocol);
ret = rsock_socketpair(d, t, p, sp);
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
r = rb_assoc_new(s1, s2);
if (rb_block_given_p()) {
return rb_ensure(pair_yield, r, io_close, s1);
}
return r;
} Создаёт пару сокетов, соединённых друг с другом.
domain должен быть домен связи, например: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
protocol должен быть протоколом, определённым в домене, по умолчанию равен 0 для данного домена.
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
# File ext/socket/lib/socket.rb, line 628
def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_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, 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
if block_given?
begin
yield ret
ensure
ret.close
end
else
ret
end
end создаёт новый объект сокета, подключённый к хосту:порт с использованием 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 861
def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
tcp_server_sockets(host, port) {|sockets|
accept_loop(sockets, &b)
}
end создаёт сервер TCP/IP на port и вызывает блок для каждого принятого подключения. Блок вызывается с сокетом и адресом клиента как объектом Addrinfo.
Если задан host, он используется с port для определения адресов сервера.
Сокет не закрывается при возврате из блока. Поэтому приложение должно закрыть его явно.
Этот метод вызывает блок последовательно. Это означает, что следующее подключение не принимается, пока блок не вернётся. Таким образом, для одновременного обслуживания нескольких клиентов необходимо использовать механизм параллельности, например, поток.
Обратите внимание, что используется Addrinfo.getaddrinfo для определения адресов сокета сервера. Когда Addrinfo.getaddrinfo возвращает два или более адреса, например, IPv4 и IPv6 адрес, все они используются. Socket.tcp_server_loop имеет успех, если хотя бы один сокет может быть использован.
# Sequential echo server.
# It services only one client at a time.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# Threaded echo server
# It services multiple clients at a time.
# Note that it may accept connections too much.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
Thread.new {
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
}
# File ext/socket/lib/socket.rb, line 761
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 1031
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 1004
def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
loop {
readable, _, _ = IO.select(sockets)
udp_server_recv(readable, &b)
}
end Запуск цикла сервера UDP/IP на заданных сокетах.
Возвращаемое значение Socket.udp_server_sockets подходит для аргумента.
Вызывает блок для каждого полученного сообщения.
# File ext/socket/lib/socket.rb, line 977
def self.udp_server_recv(sockets)
sockets.each {|r|
msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock(exception: false)
next if msg == :wait_readable
ai = r.local_address
if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
}
else
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
r.send reply_msg, 0, sender_addrinfo
}
end
}
end Приём UDP/IP пакетов с заданных sockets. Для каждого полученного пакета вызывается блок.
Блок получает msg и msg_src. msg — строка, которая является полезной нагрузкой принятого пакета. msg_src — объект Socket::UDPSource, который используется для ответа.
Socket.udp_server_loop может быть реализован с помощью этого метода следующим образом.
udp_server_sockets(host, port) {|sockets|
loop {
readable, _, _ = IO.select(sockets)
udp_server_recv(readable) {|msg, msg_src| ... }
}
} # File ext/socket/lib/socket.rb, line 889
def self.udp_server_sockets(host=nil, port)
last_error = nil
sockets = []
ipv6_recvpktinfo = nil
if defined? Socket::AncillaryData
if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
elsif defined? Socket::IPV6_PKTINFO # RFC 2292
ipv6_recvpktinfo = Socket::IPV6_PKTINFO
end
end
local_addrs = Socket.ip_address_list
ip_list = []
Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
if ai.ipv4? && ai.ip_address == "0.0.0.0"
local_addrs.each {|a|
next unless a.ipv4?
ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
}
elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
local_addrs.each {|a|
next unless a.ipv6?
ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
}
else
ip_list << ai
end
}
ip_list.uniq!(&:to_sockaddr)
if port == 0
sockets = ip_sockets_port0(ip_list, false)
else
ip_list.each {|ip|
ai = Addrinfo.udp(ip.ip_address, port)
begin
s = ai.bind
rescue SystemCallError
last_error = $!
next
end
sockets << s
}
if sockets.empty?
raise last_error
end
end
sockets.each {|s|
ai = s.local_address
if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
end
}
if block_given?
begin
yield sockets
ensure
sockets.each(&:close) if sockets
end
else
sockets
end
end Создаёт сокеты UDP/IP для UDP-сервера.
Если блок не задан, возвращает массив сокетов.
Если задан блок, блок вызывается с сокетами. Возвращается значение блока. Сокеты закрываются при возврате из этого метода.
Если port равен нулю, выбирается какой-либо порт. Но выбранный порт используется для всех сокетов.
# UDP/IP echo server
Socket.udp_server_sockets(0) {|sockets|
p sockets.first.local_address.ip_port #=> 32963
Socket.udp_server_loop_on(sockets) {|msg, msg_src|
msg_src.reply msg
}
}
# File ext/socket/lib/socket.rb, line 1081
def self.unix(path) # :yield: socket
addr = Addrinfo.unix(path)
sock = addr.connect
if block_given?
begin
yield sock
ensure
sock.close
end
else
sock
end
end создаёт новый сокет, подключённый к пути с использованием сокета UNIX.
Если задан блок, блок вызывается с сокетом. Возвращается значение блока. Сокет закрывается при возврате из этого метода.
Если блок не задан, возвращается сокет.
# talk to /tmp/sock socket.
Socket.unix("/tmp/sock") {|sock|
t = Thread.new { IO.copy_stream(sock, STDOUT) }
IO.copy_stream(STDIN, sock)
t.join
}
# File ext/socket/lib/socket.rb, line 1168
def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
unix_server_socket(path) {|serv|
accept_loop(serv, &b)
}
end создаёт сервер сокета UNIX по path. Вызывает блок для каждого принятого сокета.
Если задан host, он используется с port для определения портов сервера.
Сокет не закрывается при возврате из блока. Приложение должно закрыть его явно.
Этот метод сначала удаляет файл сокета, указанный path, если это файл сокета и он принадлежит пользователю приложения. Это безопасно только в том случае, если каталог path не изменяется злонамеренным пользователем. Поэтому не используйте /tmp/malicious-users-directory/socket. Обратите внимание, что /tmp/socket и /tmp/your-private-directory/socket безопасны при условии, что /tmp имеет атрибут «sticky bit».
# Sequential echo server.
# It services only one client at a time.
Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# File ext/socket/lib/socket.rb, line 1111
def self.unix_server_socket(path)
unless unix_socket_abstract_name?(path)
begin
st = File.lstat(path)
rescue Errno::ENOENT
end
if st&.socket? && st.owned?
File.unlink path
end
end
s = Addrinfo.unix(path).listen
if block_given?
begin
yield s
ensure
s.close
unless unix_socket_abstract_name?(path)
File.unlink path
end
end
else
s
end
end создаёт сокет сервера UNIX по path
Если блок не задан, возвращает сокет, ожидающий подключения.
Если задан блок, он вызывается с сокетом, и возвращается значение блока. После выхода из блока сокет закрывается, а файл сокета удаляется.
socket = Socket.unix_server_socket("/tmp/s")
p socket #=> #<Socket:fd 3>
p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
Socket.unix_server_socket("/tmp/sock") {|s|
p s #=> #<Socket:fd 3>
p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
}
static VALUE
sock_s_unpack_sockaddr_in(VALUE self, VALUE addr)
{
struct sockaddr_in * sockaddr;
VALUE host;
sockaddr = (struct sockaddr_in*)SockAddrStringValuePtr(addr);
if (RSTRING_LEN(addr) <
(char*)&((struct sockaddr *)sockaddr)->sa_family +
sizeof(((struct sockaddr *)sockaddr)->sa_family) -
(char*)sockaddr)
rb_raise(rb_eArgError, "too short sockaddr");
if (((struct sockaddr *)sockaddr)->sa_family != AF_INET
#ifdef INET6
&& ((struct sockaddr *)sockaddr)->sa_family != AF_INET6
#endif
) {
#ifdef INET6
rb_raise(rb_eArgError, "not an AF_INET/AF_INET6 sockaddr");
#else
rb_raise(rb_eArgError, "not an AF_INET sockaddr");
#endif
}
host = rsock_make_ipaddr((struct sockaddr*)sockaddr, RSTRING_SOCKLEN(addr));
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
} Распаковывает sockaddr в порт и ip_address.
sockaddr должен быть строкой или addrinfo для AF_INET/AF_INET6.
sockaddr = Socket.sockaddr_in(80, "127.0.0.1") p sockaddr #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" p Socket.unpack_sockaddr_in(sockaddr) #=> [80, "127.0.0.1"]
static VALUE
sock_s_unpack_sockaddr_un(VALUE self, VALUE addr)
{
struct sockaddr_un * sockaddr;
VALUE path;
sockaddr = (struct sockaddr_un*)SockAddrStringValuePtr(addr);
if (RSTRING_LEN(addr) <
(char*)&((struct sockaddr *)sockaddr)->sa_family +
sizeof(((struct sockaddr *)sockaddr)->sa_family) -
(char*)sockaddr)
rb_raise(rb_eArgError, "too short sockaddr");
if (((struct sockaddr *)sockaddr)->sa_family != AF_UNIX) {
rb_raise(rb_eArgError, "not an AF_UNIX sockaddr");
}
if (sizeof(struct sockaddr_un) < (size_t)RSTRING_LEN(addr)) {
rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un));
}
path = rsock_unixpath_str(sockaddr, RSTRING_SOCKLEN(addr));
return path;
} Распаковывает sockaddr в путь.
sockaddr должен быть строкой или addrinfo для AF_UNIX.
sockaddr = Socket.sockaddr_un("/tmp/sock")
p Socket.unpack_sockaddr_un(sockaddr) #=> "/tmp/sock"
Приватные методы класса
# File ext/socket/lib/socket.rb, line 1139 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
Публичные методы экземпляра
static VALUE
sock_accept(VALUE server)
{
union_sockaddr buffer;
socklen_t length = (socklen_t)sizeof(buffer);
VALUE peer = rsock_s_accept(rb_cSocket, server, &buffer.addr, &length);
return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
} Принимает следующее соединение. Возвращает новый объект Socket и объект Addrinfo.
serv = Socket.new(:INET, :STREAM, 0) serv.listen(5) c = Socket.new(:INET, :STREAM, 0) c.connect(serv.connect_address) p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>]
# File ext/socket/lib/socket.rb, line 598 def accept_nonblock(exception: true) __accept_nonblock(exception) end
Принимает входящее соединение, используя accept(2) после установки O_NONBLOCK для базового файлового дескриптора. Он возвращает массив, содержащий принятый сокет для входящего соединения, client_socket, и Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
begin # emulate blocking accept
client_socket, client_addrinfo = socket.accept_nonblock
rescue IO::WaitReadable, Errno::EINTR
IO.select([socket])
retry
end
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
Обратитесь к Socket#accept для получения информации об исключениях, которые могут быть вызваны, если вызов accept_nonblock завершится неудачей.
Socket#accept_nonblock может вызвать любую ошибку, соответствующую сбою accept(2), включая Errno::EWOULDBLOCK.
Если исключение является Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED или Errno::EPROTO, оно расширяется IO::WaitReadable. Поэтому IO::WaitReadable может использоваться для обработки исключений при повторной попытке accept_nonblock.
Указав аргумент ключевого слова exception для false, вы можете указать, что accept_nonblock не должен вызывать исключение IO::WaitReadable, а возвращать символ :wait_readable вместо этого.
См. также
static VALUE
sock_bind(VALUE sock, VALUE addr)
{
VALUE rai;
rb_io_t *fptr;
SockAddrStringValueWithAddrinfo(addr, rai);
GetOpenFile(sock, fptr);
if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0)
rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai);
return INT2FIX(0);
} Привязывает к указанному локальному адресу.
Параметр
-
local_sockaddr-structsockaddr, содержащийся в строке или объектеAddrinfo
Пример
require 'socket'
# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("127.0.0.1", 2222))
p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
# use struct sockaddr
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
Исключения в Unix-подобных системах
В Unix-подобных системах могут быть вызваны следующие системные исключения, если вызов bind завершится неудачей:
-
Errno::EACCES - указанный sockaddr защищен, и текущий пользователь не имеет разрешения на привязку к нему
-
Errno::EADDRINUSE - указанный sockaddr уже используется
-
Errno::EADDRNOTAVAIL - указанный sockaddr недоступен с локальной машины
-
Errno::EAFNOSUPPORT - указанный sockaddr не является допустимым адресом для семейства вызывающего
socket -
Errno::EBADF - указанный sockaddr не является допустимым файловым дескриптором
-
Errno::EFAULT - к аргументу sockaddr невозможно получить доступ
-
Errno::EINVAL -
socketуже привязан к адресу, и протокол не поддерживает привязку к новому sockaddr илиsocketбыл закрыт. -
Errno::EINVAL - длина адреса не является допустимой длиной для семейства адресов
-
Errno::ENAMETOOLONG - длина разрешенного пути имени превысила PATH_MAX
-
Errno::ENOBUFS - доступно недостаточно буферного пространства
-
Errno::ENOSR - для завершения операции не хватало ресурсов STREAMS
-
Errno::ENOTSOCK -
socketне ссылается на сокет -
Errno::EOPNOTSUPP - тип сокета
socketне поддерживает привязку к адресу
В Unix-подобных системах, если семейство адресов вызывающего socket является Socket::AF_UNIX, могут быть вызваны следующие исключения, если вызов bind завершится неудачей:
-
Errno::EACCES - разрешение на поиск запрещено для компонента префиксного пути или запрещен доступ на запись к
socket -
Errno::EDESTADDRREQ - аргумент sockaddr является нулевым указателем
-
Errno::EISDIR - то же, что и Errno::EDESTADDRREQ
-
Errno::EIO - произошла ошибка ввода-вывода
-
Errno::ELOOP - при преобразовании пути имени в sockaddr было обнаружено слишком много символических ссылок
-
Errno::ENAMETOOLLONG - компонент пути имени превысил NAME_MAX символов, или весь путь имени превысил PATH_MAX символов
-
Errno::ENOENT - компонент пути имени не обозначает существующий файл или путь имени является пустой строкой
-
Errno::ENOTDIR - компонент префиксного пути пути имени в sockaddr не является каталогом
-
Errno::EROFS - имя будет находиться в файловой системе только для чтения
Исключения в Windows
В системах Windows могут быть вызваны следующие системные исключения, если вызов bind завершится неудачей:
-
Errno::ENETDOWN– сеть отключена
-
Errno::EACCES - попытка подключения сокета дейтаграммы к широковещательному адресу не удалась
-
Errno::EADDRINUSE - локальный адрес сокета уже используется
-
Errno::EADDRNOTAVAIL - указанный адрес не является допустимым адресом для этого компьютера
-
Errno::EFAULT - внутренний адрес сокета или параметр длины адреса слишком мал или не является допустимой частью адресованного пространства пользователя
-
Errno::EINVAL -
socketуже привязан к адресу -
Errno::ENOBUFS - доступно недостаточно буферного пространства
-
Errno::ENOTSOCK - аргумент
socketне ссылается на сокет
См. также
-
страницы руководства bind в Unix-подобных системах
-
функция bind в справочнике функций Winsock от Microsoft
static VALUE
sock_connect(VALUE 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, NULL);
if (n < 0) {
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
return INT2FIX(n);
} Запрашивает подключение к указанному remote_sockaddr. Возвращает 0, если успешно, в противном случае генерирует исключение.
Параметр
-
remote_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::ENAMETOOLLONG - компонент имени пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов
-
Errno::ENOENT - компонент пути не указывает на существующий файл или путь пустой
-
Errno::ENOTDIR - компонент пути префикса имени пути в sockaddr не является каталогом
Исключения Windows
На системах Windows при неудачном вызове connect могут быть сгенерированы следующие системные исключения:
-
Errno::ENETDOWN - сеть не активна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется
-
Errno::EINTR - сокет был отменён
-
Errno::EINPROGRESS - блокирующий сокет находится в процессе обработки или поставщик услуг ещё обрабатывает функцию обратного вызова. Или неблокирующий вызов connect выполняется на
socket. -
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 в справочнике Microsoft Winsock
# File ext/socket/lib/socket.rb, line 1218 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
Обратитесь к Socket#connect, чтобы узнать об исключениях, которые могут быть вызваны, если вызов connect_nonblock завершится неудачей.
Socket#connect_nonblock может вызвать любую ошибку, соответствующую ошибке connect(2), включая Errno::EINPROGRESS.
Если исключение Errno::EINPROGRESS, оно дополняется IO::WaitWritable. Таким образом, IO::WaitWritable можно использовать для перехвата исключений для повторной попытки connect_nonblock.
Указав ключевой аргумент exception для false, вы можете указать, что connect_nonblock не должен вызывать исключение IO::WaitWritable, а вместо этого вернуть символ :wait_writable.
См. также
# Socket#connect
# File ext/socket/lib/socket.rb, line 468
def ipv6only!
if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
end
end Включает параметр сокета IPV6_V6ONLY, если IPV6_V6ONLY доступен.
VALUE
rsock_sock_listen(VALUE sock, VALUE log)
{
rb_io_t *fptr;
int backlog;
backlog = NUM2INT(log);
GetOpenFile(sock, fptr);
if (listen(fptr->fd, backlog) < 0)
rb_sys_fail("listen(2)");
return INT2FIX(0);
} Прослушивает подключения, используя указанный int в качестве очереди. Вызов listen применяется только если socket имеет тип SOCK_STREAM или SOCK_SEQPACKET.
Параметр
-
backlog- максимальная длина очереди ожидающих подключений.
Пример 1
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
Пример 2 (прослушивание на произвольном порту, только системы на основе Unix):
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
Исключения для систем на основе Unix
В системах на основе Unix вышеуказанный код будет работать, так как новый sockaddr структурируется по адресу ADDR_ANY для произвольного номера порта, передаваемого ядром. Он не будет работать в Windows, так как Windows требует, чтобы socket был привязан вызовом bind, прежде чем можно вызвать listen.
Если значение backlog превышает максимальную длину очереди, зависящую от реализации, будет использоваться максимальная длина очереди реализации.
В системах на основе Unix при неудачном вызове listen могут быть подняты следующие системные исключения:
-
Errno::EBADF - аргумент socket не является допустимым дескриптором файла
-
Errno::EDESTADDRREQ - сокет не привязан к локальному адресу, и протокол не поддерживает прослушивание на не привязанном сокете
-
Errno::EINVAL - сокет уже подключен
-
Errno::ENOTSOCK - аргумент socket не относится к сокету
-
Errno::EOPNOTSUPP - протокол сокета не поддерживает listen
-
Errno::EACCES - вызывающий процесс не имеет соответствующих привилегий
-
Errno::EINVAL - сокет закрыт
-
Errno::ENOBUFS - в системе недостаточно ресурсов для выполнения вызова
Исключения для Windows
В системах Windows при неудачном вызове listen могут быть подняты следующие системные исключения:
-
Errno::ENETDOWN - сеть недоступна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется. Это обычно происходит во время выполнения bind, но может быть отложено, если вызов bind был к частично подставленному адресу (включая ADDR_ANY) и если конкретный адрес должен быть зафиксирован в момент вызова listen
-
Errno::EINPROGRESS - в Windows Sockets 1.1 вызов находится в процессе выполнения или поставщик сервиса все еще обрабатывает функцию обратного вызова
-
Errno::EINVAL - сокет не был привязан с помощью вызова bind.
-
Errno::EISCONN - сокет уже подключен
-
Errno::EMFILE - больше нет доступных дескрипторов сокетов
-
Errno::ENOBUFS - нет доступного буфера
-
Errno::ENOTSOC -
socketне является сокетом -
Errno::EOPNOTSUPP - указанный
socketне является типом, который поддерживает метод listen
См. также
-
Руководства по listen в системах на основе Unix
-
Функцию listen в справочнике Microsoft Winsock
static VALUE
sock_recvfrom(int argc, VALUE *argv, VALUE sock)
{
return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
} Принимает до maxlen байт из socket. flags равно нулю или больше из MSG_ опций. Первый элемент результата, mesg, - это полученные данные. Второй элемент, sender_addrinfo, содержит адресную информацию отправителя, специфичную для протокола.
Параметры
-
maxlen- максимальное количество байтов для приема с сокета -
flags- ноль или большеMSG_опций
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client, client_addrinfo = socket.accept
data = client.recvfrom( 20 )[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Watch this get cut short!"
socket.close
Исключения для систем на основе Unix
В системах на основе Unix при неудачном вызове recvfrom могут быть подняты следующие системные исключения:
-
Errno::EAGAIN - дескриптор файла
socketпомечен как O_NONBLOCK и данные для приема отсутствуют; илиMSG_OOBустановлено, нет данных вне очереди, и либо дескриптор файлаsocketпомечен как O_NONBLOCK, либоsocketне поддерживает блокирование для ожидания данных вне очереди -
Errno::EWOULDBLOCK - см. Errno::EAGAIN
-
Errno::EBADF -
socketне является допустимым дескриптором файла -
Errno::ECONNRESET - соединение было принудительно закрыто удаленным узлом
-
Errno::EFAULT - внутренний буфер, адрес или длина адреса сокета не могут быть доступны или записаны
-
Errno::EINTR - сигнал прервал recvfrom до того, как данные стали доступны
-
Errno::EINVAL - флаг
MSG_OOBустановлен, но данных вне очереди нет -
Errno::EIO - произошла ошибка ввода-вывода при чтении или записи на файловую систему
-
Errno::ENOBUFS - в системе недостаточно ресурсов для выполнения операции
-
Errno::ENOMEM - недостаточно памяти для выполнения запроса
-
Errno::ENOSR - недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTCONN - попытка приема на сокете в режиме соединения, который не подключен
-
Errno::ENOTSOCK -
socketне относится к сокету -
Errno::EOPNOTSUPP - указанные флаги не поддерживаются для данного типа сокета
-
Errno::ETIMEDOUT - соединение истекло во время установления соединения или из-за таймаута передачи в активном соединении
Исключения для Windows
В системах Windows при неудачном вызове recvfrom могут быть подняты следующие системные исключения:
-
Errno::ENETDOWN - сеть недоступна
-
Errno::EFAULT - внутренний буфер и параметры from сокета
socketне являются частью адресного пространства пользователя, или внутренний параметр fromlen слишком мал для размещения адреса удаленного узла -
Errno::EINTR - (блокирующий) вызов был отменен внутренним вызовом функции WinSock WSACancelBlockingCall
-
Errno::EINPROGRESS - блокирующий вызов Windows Sockets 1.1 находится в процессе выполнения, или поставщик сервиса все еще обрабатывает функцию обратного вызова
-
Errno::EINVAL - сокет
socketне был привязан с помощью вызова bind, или был указан неизвестный флаг, илиMSG_OOBбыл указан для сокета сSO_OOBINLINEактивным, или (только для сокетов потокового типа байтов) внутренний параметр len дляsocketбыл равен нулю или отрицателен -
Errno::EISCONN - сокет
socketуже подключен. Вызов recvfrom не разрешен с подключенным сокетом на сокете, ориентированном на соединение или без соединения. -
Errno::ENETRESET - соединение было разорвано из-за активности keep-alive, обнаружившей ошибку во время операции.
-
Errno::EOPNOTSUPP -
MSG_OOBбыл указан, ноsocketне является сокетом потокового типа, например, типаSOCK_STREAM. Данные OOB не поддерживаются в области связи, связанной сsocket, илиsocketявляется однонаправленным и поддерживает только операции отправки -
Errno::ESHUTDOWN -
socketбыл закрыт. Невозможно вызвать recvfrom на сокете после вызова shutdown. -
Errno::EWOULDBLOCK -
socketпомечен как неблокирующий, и вызов recvfrom заблокирует. -
Errno::EMSGSIZE - сообщение было слишком большим, чтобы поместиться в указанный буфер, и было усечено.
-
Errno::ETIMEDOUT - соединение было разорвано из-за сбоя сети или из-за того, что система на другом конце вышла из строя без предупреждения
-
Errno::ECONNRESET - виртуальный канал был сброшен удаленным узлом, выполнившим жесткое или абортивное закрытие. Приложение должно закрыть сокет; он больше не пригоден для использования. В случае сокета UDP-датаграмм эта ошибка указывает на то, что предыдущая операция отправки привела к сообщению ICMP Port Unreachable.
# File ext/socket/lib/socket.rb, line 541 def recvfrom_nonblock(len, flag = 0, str = nil, exception: true) __recvfrom_nonblock(len, flag, str, exception) end
Принимает до maxlen байтов данных от socket с помощью recvfrom(2) после установки O_NONBLOCK для базового дескриптора файла. flags — ноль или более из MSG_ опций. Первый элемент результатов, mesg, — полученные данные. Второй элемент, sender_addrinfo, содержит адресно-специфическую информацию отправителя.
Когда recvfrom(2) возвращает 0, Socket#recv_nonblock возвращает nil. В большинстве случаев это означает закрытие соединения, но для UDP-соединений это может означать получение пустого пакета, поскольку базовый API делает невозможным различение этих двух случаев.
Параметры
-
maxlen— максимальное количество байтов для приема с сокета -
flags— ноль или более изMSG_опций -
outbuf— буфер назначенияString -
opts— ключевое слово, поддерживающее «исключение: false»
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
client, client_addrinfo = socket.accept
begin # emulate blocking recvfrom
pair = client.recvfrom_nonblock(20)
rescue IO::WaitReadable
IO.select([client])
retry
end
data = pair[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Watch this get cut short!"
socket.close
Обратитесь к Socket#recvfrom для исключений, которые могут быть выброшены, если вызов recvfrom_nonblock завершается неудачно.
Socket#recvfrom_nonblock может вызывать любые ошибки, соответствующие ошибке recvfrom(2), включая Errno::EWOULDBLOCK.
Если исключение — Errno::EWOULDBLOCK или Errno::EAGAIN, оно расширяется IO::WaitReadable. Таким образом, IO::WaitReadable можно использовать для перехвата исключений для повторной попытки recvfrom_nonblock.
Указав ключевое слово exception в false, вы можете указать, что recvfrom_nonblock не должен вызывать исключение IO::WaitReadable, а вместо этого возвращать символ :wait_readable.
См. также
static VALUE
sock_sysaccept(VALUE server)
{
union_sockaddr buffer;
socklen_t length = (socklen_t)sizeof(buffer);
VALUE peer = rsock_s_accept(0, server, &buffer.addr, &length);
return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
} Принимает входящее соединение, возвращая массив, содержащий дескриптор файла (целое число) для входящего соединения, client_socket_fd, и Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client_fd, client_addrinfo = socket.sysaccept
client_socket = Socket.for_fd( client_fd )
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
Обратитесь к Socket#accept для исключений, которые могут быть выброшены, если вызов sysaccept завершается неудачно.
См. также
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.