класс 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 Socket генерирует исключения на основе ошибки, сгенерированной зависимой от системы реализацией. Вот почему методы документированы таким образом, чтобы изолировать исключения систем на базе Unix от исключений на базе Windows. Если требуется больше информации об определённом исключении, обратитесь к страницам руководства Unix или справочнику WinSock Windows.
Удобные методы
Хотя общий способ создания сокета — Socket.new, для большинства случаев существуют несколько методов создания сокетов.
- TCP-сокет клиента
- TCP-сокет сервера
- UNIX-сокет клиента
- UNIX-сокет сервера
Документация составлена
-
Заком Деннисом
-
Самом Робертсом
-
из книги Программирование на Ruby от издательства The Pragmatic Bookshelf.
Большая часть материалов в этой документации взята с разрешения из книги Программирование на Ruby от издательства The Pragmatic Bookshelf.
Константы
- AF_APPLETALK
-
Протокол AppleTalk
- AF_ATM
-
Режим асинхронной передачи
- AF_AX25
-
Протокол AX.25
- AF_CCITT
-
Протоколы CCITT (теперь ITU-T)
- AF_CHAOS
-
Протоколы CHAOS (MIT)
- AF_CNT
-
Технология компьютерных сетей
- AF_COIP
-
Ориентированный на соединение IP
- AF_DATAKIT
-
Протокол Datakit
- AF_DEC
-
Протокол DECnet
- AF_DLI
-
Протокол DEC Direct Data Link Interface
- AF_E164
-
Рекомендация CCITT (ITU-T) E.164
- AF_ECMA
-
Протоколы Европейских производителей компьютеров
- AF_HYLINK
-
Протокол NSC Hyperchannel
- AF_IMPLINK
-
Протокол ARPANET IMP
- AF_INET
-
Протокол IPv4
- AF_INET6
-
Протокол IPv6
- AF_IPX
-
Протокол IPX
- AF_ISDN
-
Интегрированная цифровая сеть с услугами
- AF_ISO
-
Протоколы ISO Модель взаимодействия открытых систем
- AF_LAT
-
Протокол локальной транспортной сети
- AF_LINK
-
Интерфейс уровня канала
- AF_LOCAL
-
Внутрисетевые протоколы
- AF_MAX
-
Максимальная семейство адресов для данной платформы
- AF_NATM
-
Нативный доступ ATM
- AF_NDRV
-
Прямой доступ драйвера сети
- AF_NETBIOS
-
NetBIOS
- AF_NETGRAPH
-
Сокеты Netgraph
- AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы ISO Модель взаимодействия открытых систем
- AF_PACKET
-
Прямой доступ к уровню канала
- AF_PPP
-
Протокол точка-точка
- AF_PUP
-
Протокол PARC Universal Packet
- AF_ROUTE
-
Внутренний протокол маршрутизации
- AF_SIP
-
Простой интернет-протокол
- AF_SNA
-
Протокол IBM SNA
- AF_SYSTEM
- AF_UNIX
-
Сокеты UNIX
- AF_UNSPEC
-
Неопределенный протокол, любое поддерживаемое семейство адресов
- AI_ADDRCONFIG
-
Принять только если какой-либо адрес назначен
- AI_ALL
-
Разрешить все адреса
- AI_CANONNAME
-
Заполнить каноническое имя
- AI_DEFAULT
-
Флаги по умолчанию для getaddrinfo
- AI_MASK
-
Допустимая маска флагов для getaddrinfo (не для использования приложения)
- AI_NUMERICHOST
-
Запретить разрешение имени хоста
- AI_NUMERICSERV
-
Запретить разрешение имени службы
- AI_PASSIVE
-
Получить адрес для использования с bind()
- AI_V4MAPPED
-
Принять IPv4-отображаемые IPv6 адреса
- AI_V4MAPPED_CFG
-
Принять IPv4-отображаемые адреса, если ядро их поддерживает
- EAI_ADDRFAMILY
-
Семейство адресов не поддерживается для имени хоста
- EAI_AGAIN
-
Временный сбой при разрешении имени
- EAI_BADFLAGS
-
Неверные флаги
- EAI_BADHINTS
-
Неверное значение для подсказок
- EAI_FAIL
-
Необратимый сбой при разрешении имени
- EAI_FAMILY
-
Семейство адресов не поддерживается
- EAI_MAX
-
Максимальный код ошибки от getaddrinfo
- EAI_MEMORY
-
Ошибка выделения памяти
- EAI_NODATA
-
Нет адреса, связанного с именем хоста
- EAI_NONAME
-
Имя хоста или имя службы неизвестны
- EAI_OVERFLOW
-
Переполнение буфера аргументов
- EAI_PROTOCOL
-
Решенный протокол неизвестен
- EAI_SERVICE
-
Имя службы не поддерживается для типа сокета
- EAI_SOCKTYPE
-
Socketтип не поддерживается - EAI_SYSTEM
-
Возвращена системная ошибка в errno
- IFF_802_1Q_VLAN
-
Устройство VLAN 802.1Q
- IFF_ALLMULTI
-
Принять все пакеты с групповой адресацией
- 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
-
Запретить создание моста для этого ethernet устройства
- IFF_DORMANT
-
Драйвер сигнализирует о приостановке
- IFF_DRV_OACTIVE
-
Очередь передачи аппаратного обеспечения заполнена
- IFF_DRV_RUNNING
-
Ресурсы выделены
- IFF_DYING
-
Интерфейс завершает работу
- IFF_DYNAMIC
-
Устройство с коммутируемым подключением, с изменяемыми адресами
- IFF_EBRIDGE
-
Устройство ethernet моста
- IFF_ECHO
-
Отправленные пакеты эха
- IFF_ISATAP
-
Интерфейс ISATAP (RFC4214)
- IFF_LINK0
-
Бит 0, определенный для уровня канала
- IFF_LINK1
-
Бит 1, определенный для уровня канала
- IFF_LINK2
-
Бит 2, определенный для уровня канала
- IFF_LIVE_ADDR_CHANGE
-
Изменение аппаратного адреса во время работы
- IFF_LOOPBACK
-
Петля обратной связи
- IFF_LOWER_UP
-
Драйвер сигнализирует о поднятии L1
- IFF_MACVLAN_PORT
-
Устройство, используемое в качестве порта macvlan
- IFF_MASTER
-
Мастер балансировщика нагрузки
- IFF_MASTER_8023AD
-
Мастер группы связывания, 802.3ad.
- IFF_MASTER_ALB
-
Мастер группы связывания, балансировка ALB.
- IFF_MASTER_ARPMON
-
Мастер группы связывания, используется ARP мониторинг
- IFF_MONITOR
-
Режим мониторинга по запросу пользователя
- IFF_MULTICAST
-
Поддержка групповой адресации
- IFF_NOARP
-
Отсутствует протокол разрешения адресов
- IFF_NOTRAILERS
-
Избегать использования трейлеров
- IFF_OACTIVE
-
Процесс передачи данных
- IFF_OVS_DATAPATH
-
Устройство, используемое в качестве порта Open vSwitch datapath
- IFF_POINTOPOINT
-
Точечное подключение
- IFF_PORTSEL
-
Можно установить тип среды
- IFF_PPROMISC
-
Режим promiscuous по запросу пользователя
- IFF_PROMISC
-
Принять все пакеты
- IFF_RENAMING
-
Интерфейс переименовывается
- IFF_ROUTE
-
Установка записи маршрутизации
- IFF_RUNNING
-
Ресурсы выделены
- IFF_SIMPLEX
-
Не может принять собственные трансляции
- IFF_SLAVE
-
Раб балансировщика нагрузки
- IFF_SLAVE_INACTIVE
-
Раб группы связывания не является текущим активным
- IFF_SLAVE_NEEDARP
-
Требуются ARPs для проверки
- IFF_SMART
-
Интерфейс управляет собственными маршрутами
- IFF_STATICARP
-
Статический ARP
- IFF_SUPP_NOFCS
-
Отправка пользовательского FCS
- IFF_TEAM_PORT
-
Используется как порт команды
- IFF_TX_SKB_SHARING
-
Использование совместно skb при передаче
- IFF_UNICAST_FLT
-
Фильтрация одноадресных пакетов
- IFF_UP
-
Интерфейс активен
- IFF_VOLATILE
-
Переменные флаги
- IFF_WAN_HDLC
-
Устройство WAN HDLC
- IFF_XMIT_DST_RELEASE
-
dev_hard_start_xmit() разрешено освободить skb->dst
- IFNAMSIZ
-
Максимальный размер имени интерфейса
- IF_NAMESIZE
-
Максимальный размер имени интерфейса
- INADDR_ALLHOSTS_GROUP
-
Группа с групповой адресацией для всех систем в данном подмножестве
- INADDR_ANY
-
Сокет, привязанный к
INADDR_ANY, получает пакеты со всех интерфейсов и отправляет с помощью IP адреса по умолчанию - INADDR_BROADCAST
-
Адрес широковещательной передачи в сети
- INADDR_LOOPBACK
-
Адрес обратной связи
- INADDR_MAX_LOCAL_GROUP
-
Последняя локальная группа с групповой адресацией
- INADDR_NONE
-
Маска бита для соответствия отсутствию действительного IP-адреса
- INADDR_UNSPEC_GROUP
-
Зарезервированная группа с групповой адресацией
- INET6_ADDRSTRLEN
-
Максимальная длина строки IPv6 адреса
- INET_ADDRSTRLEN
-
Максимальная длина строки IPv4 адреса
- IPPORT_RESERVED
-
Минимальный адрес по умолчанию для bind или connect
- IPPORT_USERRESERVED
-
Максимальный адрес по умолчанию для bind или connect
- IPPROTO_AH
-
IP6 заголовок аутентификации
- IPPROTO_BIP
- IPPROTO_DSTOPTS
-
IP6 опция назначения
- IPPROTO_EGP
-
Протокол внешних шлюзов
- IPPROTO_EON
-
ISO cnlp
- IPPROTO_ESP
-
IP6 Encapsulated Security Payload
- IPPROTO_FRAGMENT
-
Заголовок фрагментации IP6
- IPPROTO_GGP
-
Протокол шлюза к шлюзу (Gateway-to-Gateway Protocol)
- IPPROTO_HELLO
-
Протокол маршрутизации «hello»
- IPPROTO_HOPOPTS
-
Опции IP6 для перехода между узлами
- IPPROTO_ICMP
-
Протокол управляющих сообщений
- IPPROTO_ICMPV6
-
ICMP6
- IPPROTO_IDP
-
XNS IDP
- IPPROTO_IGMP
-
Протокол управления группами (Group Management Protocol)
- IPPROTO_IP
-
Протокол-заглушка для IP
- IPPROTO_IPV6
-
Заголовок IP6
- IPPROTO_MAX
-
Максимальное значение константы IPPROTO
- IPPROTO_ND
-
Протокол Sun net disk
- IPPROTO_NONE
-
IP6 без следующего заголовка
- IPPROTO_PUP
-
Протокол PARC Universal Packet
- IPPROTO_RAW
-
Сырой пакет IP
- IPPROTO_ROUTING
-
Заголовок маршрутизации IP6
- IPPROTO_TCP
-
TCP
- IPPROTO_TP
-
Класс 4 транспортного протокола ISO
- IPPROTO_UDP
-
UDP
- IPPROTO_XTP
-
Протокол Xpress Transport
- IPV6_CHECKSUM
-
Смещение контрольной суммы для сырых сокетов
- IPV6_DONTFRAG
-
Не фрагментировать пакеты
- IPV6_DSTOPTS
-
Опция назначения
- IPV6_HOPLIMIT
-
Предел переходов
- IPV6_HOPOPTS
-
Опция перехода
- IPV6_JOIN_GROUP
-
Присоединиться к группе членства
- IPV6_LEAVE_GROUP
-
Выйти из группы членства
- IPV6_MULTICAST_HOPS
-
Переходы IP6 для многоадресной рассылки
- IPV6_MULTICAST_IF
-
Интерфейс IP6 для многоадресной рассылки
- IPV6_MULTICAST_LOOP
-
Петля обратной связи IP6 для многоадресной рассылки
- IPV6_NEXTHOP
-
Следующий хост-адрес
- IPV6_PATHMTU
-
Получить текущий максимальный размер пакета пути
- IPV6_PKTINFO
-
Получить информацию о пакете с датаграммой
- IPV6_RECVDSTOPTS
-
Получить все опции IP6 для ответа
- IPV6_RECVHOPLIMIT
-
Получить предел переходов с датаграммой
- IPV6_RECVHOPOPTS
-
Получить опции перехода
- IPV6_RECVPATHMTU
-
Получить текущий максимальный размер пакета пути с датаграммой
- IPV6_RECVPKTINFO
-
Получить целевой IP-адрес и интерфейс приема
- IPV6_RECVRTHDR
-
Получить заголовок маршрутизации
- IPV6_RECVTCLASS
-
Получить класс трафика
- IPV6_RTHDR
-
Позволяет удалить привязку к заголовкам маршрутизации
- IPV6_RTHDRDSTOPTS
-
Позволяет удалить привязку к заголовку опций назначения
- IPV6_RTHDR_TYPE_0
-
Тип заголовка маршрутизации 0
- IPV6_TCLASS
-
Указать класс трафика
- IPV6_UNICAST_HOPS
-
Переходы IP6 для одноадресной рассылки
- IPV6_USE_MIN_MTU
-
Использовать минимальный размер MTU
- IPV6_V6ONLY
-
Связать только с IPv6 при использовании маски-замены
- IPX_TYPE
- IP_ADD_MEMBERSHIP
-
Добавить членство в многоадресную группу
- IP_ADD_SOURCE_MEMBERSHIP
-
Добавить членство в многоадресную группу
- IP_BLOCK_SOURCE
-
Заблокировать пакеты IPv4 с многоадресной рассылкой с указанным адресом источника
- IP_DEFAULT_MULTICAST_LOOP
-
Стандартная петля обратной связи для многоадресной рассылки
- IP_DEFAULT_MULTICAST_TTL
-
Стандартное время жизни для многоадресной рассылки
- IP_DONTFRAG
-
Не фрагментировать пакеты
- IP_DROP_MEMBERSHIP
-
Удалить членство в многоадресной группе
- IP_DROP_SOURCE_MEMBERSHIP
-
Удалить членство в многоадресной группе
- IP_FREEBIND
-
Разрешить привязку к несуществующим IP-адресам
- IP_HDRINCL
-
Заголовок включён с данными
- IP_IPSEC_POLICY
-
Политика безопасности IPsec
- IP_MAX_MEMBERSHIPS
-
Максимальное количество многоадресных групп, которые может присоединить сокет
- IP_MINTTL
-
Минимальное время жизни, разрешённое для полученных пакетов
- IP_MSFILTER
-
Фильтр многоадресной рассылки источников
- IP_MTU
-
Максимальный размер передаваемого блока сокета
- IP_MTU_DISCOVER
-
Обнаружение максимального размера пакета пути
- IP_MULTICAST_IF
-
Интерфейс IP для многоадресной рассылки
- IP_MULTICAST_LOOP
-
Петля обратной связи IP для многоадресной рассылки
- IP_MULTICAST_TTL
-
Время жизни IP для многоадресной рассылки
- IP_ONESBCAST
-
Принудительно задать выходной широковещательный пакет с нелинейным широковещательным адресом
- IP_OPTIONS
-
Опции IP, которые должны быть включены в пакеты
- IP_PASSSEC
-
Получить контекст безопасности с датаграммой
- IP_PKTINFO
-
Получить информацию о пакете с датаграммой
- IP_PKTOPTIONS
-
Получить опции пакета с датаграммами
- IP_PMTUDISC_DO
-
Всегда отправлять кадры DF
- IP_PMTUDISC_DONT
-
Никогда не отправлять кадры DF
- IP_PMTUDISC_WANT
-
Использовать подсказки по маршруту
- IP_PORTRANGE
-
Setдиапазон портов для сокетов со значениями портов без указания - IP_RECVDSTADDR
-
Получить IP-адрес назначения с датаграммой
- IP_RECVERR
-
Включить передачу расширенных сообщений об ошибках надёжным образом
- IP_RECVIF
-
Получить информацию об интерфейсе с датаграммами
- IP_RECVOPTS
-
Получить все опции IP с датаграммой
- IP_RECVRETOPTS
-
Получить все опции IP для ответа
- IP_RECVSLLA
-
Получить адрес уровня канала связи с датаграммами
- IP_RECVTOS
-
Получить TOS с входящими пакетами
- IP_RECVTTL
-
Получить IP время жизни с датаграммами
- IP_RETOPTS
-
Опции IP, которые должны быть включены в датаграммы
- IP_ROUTER_ALERT
-
Уведомить транзитные маршрутизаторы о более тщательном рассмотрении содержимого пакета IP
- IP_SENDSRCADDR
-
Адрес источника для исходящих UDP-датаграмм
- IP_TOS
-
Тип сервиса IP
- IP_TRANSPARENT
-
Прозрачный прокси
- IP_TTL
-
Время жизни IP
- IP_UNBLOCK_SOURCE
-
Разблокировать пакеты IPv4 с многоадресной рассылкой с указанным адресом источника
- IP_XFRM_POLICY
- LOCAL_CONNWAIT
-
Блокировка соединения до подтверждения
- LOCAL_CREDS
-
Передача учетных данных получателю
- LOCAL_PEERCRED
-
Получение учетных данных удаленного пользователя
- MCAST_BLOCK_SOURCE
-
Блокировка пакетов многоадресной рассылки от данного источника
- MCAST_EXCLUDE
-
Исключительный фильтр источника многоадресной рассылки
- MCAST_INCLUDE
-
Включительно фильтр источника многоадресной рассылки
- MCAST_JOIN_GROUP
-
Присоединиться к группе многоадресной рассылки
- MCAST_JOIN_SOURCE_GROUP
-
Присоединиться к группе многоадресной рассылки источников
- MCAST_LEAVE_GROUP
-
Выйти из группы многоадресной рассылки
- MCAST_LEAVE_SOURCE_GROUP
-
Выйти из группы многоадресной рассылки источников
- MCAST_MSFILTER
-
Фильтр источника многоадресной рассылки
- MCAST_UNBLOCK_SOURCE
-
Разблокировка пакетов многоадресной рассылки от данного источника
- MSG_COMPAT
-
Конец записи
- MSG_CONFIRM
-
Подтверждение валидности пути
- MSG_CTRUNC
-
Управляющие данные утеряны до доставки
- MSG_DONTROUTE
-
Отправка без использования таблиц маршрутизации
- MSG_DONTWAIT
-
Это сообщение должно быть неблокирующим
- MSG_EOF
-
Данные завершают соединение
- MSG_EOR
-
Данные завершают запись
- MSG_ERRQUEUE
-
Получить сообщение из очереди ошибок
- MSG_FASTOPEN
-
Уменьшение этапов процесса рукопожатия
- MSG_FIN
- MSG_FLUSH
-
Начало последовательности удержания. Выгружается в so_temp
- MSG_HAVEMORE
-
Данные готовы к чтению
- MSG_HOLD
-
Удержание фрагмента в so_temp
- MSG_MORE
-
Отправитель отправит больше
- MSG_NOSIGNAL
-
Не генерировать SIGPIPE
- MSG_OOB
-
Processвнеочередные данные - MSG_PEEK
-
Предварительный просмотр входящего сообщения
- MSG_PROXY
-
Ожидание полного запроса
- MSG_RCVMORE
-
Данные остаются в текущем пакете
- MSG_RST
- MSG_SEND
-
Отправка пакета в so_temp
- MSG_SYN
- MSG_TRUNC
-
Данные отбрасываются перед доставкой
- MSG_WAITALL
-
Ожидание полного запроса или ошибки
- NI_DGRAM
-
Указанная служба является службой датаграмм (ищет UDP-порты)
- NI_MAXHOST
-
Максимальная длина имени хоста
- NI_MAXSERV
-
Максимальная длина имени службы
- NI_NAMEREQD
-
Требуется имя
- NI_NOFQDN
-
FQDN не требуется для локальных хостов, возвращается только локальная часть
- NI_NUMERICHOST
-
Возвратить числовой адрес
- NI_NUMERICSERV
-
Возвратить имя службы в виде числовой строки
- PF_APPLETALK
-
Протокол AppleTalk
- PF_ATM
-
Режим асинхронной передачи
- PF_AX25
-
Протокол AX.25
- PF_CCITT
-
Протоколы CCITT (сейчас ITU-T)
- PF_CHAOS
-
Протоколы MIT CHAOS
- PF_CNT
-
Технология компьютерных сетей
- PF_COIP
-
Ориентированный на соединение IP
- PF_DATAKIT
-
Протокол Datakit
- PF_DEC
-
Протокол DECnet
- PF_DLI
-
Протокол DEC Direct Data Link Interface
- PF_ECMA
-
Протоколы европейских производителей компьютеров
- PF_HYLINK
-
Протокол NSC Hyperchannel
- PF_IMPLINK
-
Протокол ARPANET IMP
- PF_INET
-
Протокол IPv4
- PF_INET6
-
Протокол IPv6
- PF_IPX
-
Протокол IPX
- PF_ISDN
-
Цифровая сеть с интеграцией служб
- PF_ISO
-
Протоколы ISO Модель взаимодействия открытых систем
- PF_KEY
- PF_LAT
-
Протокол локальной транспортной системы
- PF_LINK
-
Интерфейс уровня канала
- PF_LOCAL
-
Внутрихостовые протоколы
- PF_MAX
-
Максимальная адресная семья для данной платформы
- PF_NATM
-
Нативный доступ ATM
- PF_NDRV
-
Прямой доступ сетевого драйвера
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Сокеты Netgraph
- PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы ISO Модель взаимодействия открытых систем
- PF_PACKET
-
Прямой доступ к уровню канала связи
- PF_PIP
- PF_PPP
-
Протокол точка-точка
- PF_PUP
-
Протокол PARC Universal Packet
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_RTIP
- PF_SIP
-
Простой протокол интернета
- PF_SNA
-
Протокол IBM SNA
- PF_SYSTEM
- PF_UNIX
-
Сокеты UNIX
- PF_UNSPEC
-
Неопределённый протокол, любая поддерживаемая адресная семья
- PF_XTP
-
Протокол быстрой передачи
- SCM_BINTIME
-
Отметка времени (bintime)
- SCM_CREDENTIALS
-
Удостоверения отправителя
- SCM_CREDS
-
Processудостоверения - SCM_RIGHTS
-
Права доступа
- SCM_TIMESTAMP
-
Отметка времени (timeval)
- SCM_TIMESTAMPING
-
Отметка времени (список timespec) (Linux 2.6.30)
- SCM_TIMESTAMPNS
-
Timespec (timespec)
- SCM_UCRED
-
Удостоверения пользователя
- SCM_WIFI_STATUS
-
Состояние Wi-Fi (Linux 3.3)
- SHUT_RD
-
Отключение стороны чтения сокета
- SHUT_RDWR
-
Отключение обеих сторон сокета
- SHUT_WR
-
Отключение стороны записи сокета
- SOCK_DGRAM
-
Сокет дейтаграммы обеспечивает бессоединительную, ненадежную передачу сообщений
- SOCK_PACKET
-
Доступ к пакетам на уровне устройства
- SOCK_RAW
-
Сокет 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
-
Socketна нём был вызван 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
-
Setпорог в микросекундах для опроса с низкой задержкой (Linux 3.11) - SO_DEBUG
-
Запись отладочной информации
- SO_DETACH_FILTER
-
Отсоединить фильтр принятия
- SO_DOMAIN
-
Область, заданная для socket() (Linux 2.6.32)
- SO_DONTROUTE
-
Использовать адреса интерфейса
- SO_DONTTRUNC
-
Сохранять непрочитанные данные
- SO_ERROR
-
Получить и очистить код ошибки
- SO_GET_FILTER
-
Получить набор фильтров, установленный с помощью
SO_ATTACH_FILTER(Linux 3.8) - SO_KEEPALIVE
-
Поддерживать соединения активными
- SO_LINGER
-
Задерживаться при закрытии, если присутствуют данные
- SO_LOCK_FILTER
-
Заблокировать фильтр, присоединенный к сокету (Linux 3.9)
- SO_MAC_EXEMPT
-
Исключение контроля доступа для немаркированных узлов
- SO_MARK
-
Setметку для маршрутизации на основе меток (Linux 2.6.25) - SO_MAX_PACING_RATE
-
Ограничить скорость, вычисленную транспортным уровнем. [байт в секунду] (Linux 3.13)
- SO_NKE
-
Установить сетевое расширение уровня сокета
- SO_NOFCS
-
Setnetns сокета (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
-
Setсмещение peek (Linux 3.4) - SO_PEERCRED
-
Удостоверения удалённого процесса, подключенного к этому сокету
- SO_PEERNAME
-
Имя подключённого пользователя
- SO_PEERSEC
-
Получить удостоверения безопасности (Linux 2.6.2)
- SO_PRIORITY
-
Определённый протоколом приоритет для всех пакетов в этом сокете
- SO_PROTOCOL
-
Протокол, заданный для socket() (Linux 2.6.32)
- SO_RCVBUF
-
Размер буфера приёма
- SO_RCVBUFFORCE
-
Размер буфера приёма без ограничения rmem_max (Linux 2.6.14)
- SO_RCVLOWAT
-
Нижняя граница буфера приёма
- SO_RCVTIMEO
-
Таймаут приёма
- SO_RECVUCRED
-
Получение удостоверений пользователя с дейтаграммой
- SO_REUSEADDR
-
Разрешить повторное использование локального адреса
- SO_REUSEPORT
-
Разрешить повторное использование локального адреса и порта
- SO_RXQ_OVFL
-
Включить cmsg для числа потерянных пакетов (Linux 2.6.33)
- SO_SECURITY_AUTHENTICATION
- SO_SECURITY_ENCRYPTION_NETWORK
- SO_SECURITY_ENCRYPTION_TRANSPORT
- SO_SELECT_ERR_QUEUE
-
Сделать select() обнаруживающим очередь ошибок сокета с ошибочными файловыми дескрипторами (Linux 3.10)
- SO_SNDBUF
-
Размер буфера отправки
- SO_SNDBUFFORCE
-
Размер буфера отправки без ограничения wmem_max (Linux 2.6.14)
- SO_SNDLOWAT
-
Нижняя граница буфера отправки
- SO_SNDTIMEO
-
Таймаут отправки
- SO_TIMESTAMP
-
Получение отметки времени с дейтаграммами (timeval)
- SO_TIMESTAMPING
-
Timeмаркирование входящих и исходящих пакетов (Linux 2.6.30) - SO_TIMESTAMPNS
-
Получение отметки времени в наносекундах с дейтаграммами (timespec)
- SO_TYPE
-
Получить тип сокета
- SO_USELOOPBACK
-
Пропустить оборудование, когда это возможно
- SO_WANTMORE
-
Подсказать, когда доступны дополнительные данные
- SO_WANTOOBFLAG
-
Требуются данные OOB в MSG_FLAG при получении
- SO_WIFI_STATUS
-
Включение cmsg для статуса Wi-Fi (Linux 3.3)
- TCP_CONGESTION
-
Алгоритм управления заторами TCP (Linux 2.6.13, glibc 2.6)
- TCP_COOKIE_TRANSACTIONS
-
Транзакции TCP Cookie (Linux 2.6.33, glibc 2.18)
- TCP_CORK
-
Не отправлять частичные кадры (Linux 2.2, glibc 2.2)
- TCP_DEFER_ACCEPT
-
Не уведомлять сокет прослушивания, пока данные не будут готовы (Linux 2.4, glibc 2.2)
- TCP_FASTOPEN
-
Сокращение шагов процесса рукопожатия (Linux 3.7, glibc 2.18)
- TCP_INFO
-
Получение информации об этом сокете (Linux 2.4, glibc 2.2)
- TCP_KEEPCNT
-
Максимальное количество зондов keepalive, разрешённых перед разрывом соединения (Linux 2.4, glibc 2.2)
- TCP_KEEPIDLE
-
Время простоя перед отправкой зондов keepalive (Linux 2.4, glibc 2.2)
- TCP_KEEPINTVL
-
Timeмежду зондами keepalive (Linux 2.4, glibc 2.2) - TCP_LINGER2
-
Время жизни осиротевших сокетов FIN_WAIT2 (Linux 2.4, glibc 2.2)
- TCP_MAXSEG
-
Setмаксимальный размер сегмента - 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
-
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 800
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 same as numeric address. +nil+: obey to the current +do_not_reverse_lookup+ flag.
Если предпочтительнее объект Addrinfo, используйте Addrinfo.getaddrinfo.
static VALUE
sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _)
{
VALUE addr, family;
struct hostent *h;
char **pch;
VALUE ary, names;
int t = AF_INET;
rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead.");
rb_scan_args(argc, argv, "11", &addr, &family);
StringValue(addr);
if (!NIL_P(family)) {
t = rsock_family_arg(family);
}
#ifdef AF_INET6
else if (RSTRING_LEN(addr) == 16) {
t = AF_INET6;
}
#endif
h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t);
if (h == NULL) {
#ifdef HAVE_HSTRERROR
extern int h_errno;
rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno));
#else
rb_raise(rb_eSocket, "host not found");
#endif
}
ary = rb_ary_new();
rb_ary_push(ary, rb_str_new2(h->h_name));
names = rb_ary_new();
rb_ary_push(ary, names);
if (h->h_aliases != NULL) {
for (pch = h->h_aliases; *pch; pch++) {
rb_ary_push(names, rb_str_new2(*pch));
}
}
rb_ary_push(ary, INT2NUM(h->h_addrtype));
#ifdef h_addr
for (pch = h->h_addr_list; *pch; pch++) {
rb_ary_push(ary, rb_str_new(*pch, h->h_length));
}
#else
rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length));
#endif
return ary;
} Вместо этого используйте Addrinfo#getnameinfo. Этот метод устарел по следующим причинам:
-
Нестандартное представление адреса: 4/16-байтовая двоичная строка для представления адресов IPv4/IPv6.
-
gethostbyaddr() может занимать много времени и блокировать другие потоки. (GVL не может быть освобождён, так как gethostbyname() не потокобезопасен.)
-
Этот метод использует функцию gethostbyname(), которая уже удалена из POSIX.
Этот метод получает информацию о хосте для address.
p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
#=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
["localhost", [], 2, "\x7F\x00\x00\x01"]
p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
#=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
struct rb_addrinfo *res =
rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME);
return rsock_make_hostent(host, res, sock_sockaddr);
} Вместо этого используйте Addrinfo.getaddrinfo. Этот метод устарел по следующим причинам:
-
Третий элемент результата — семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Нестандартное представление адреса: 4/16-байтовая двоичная строка для представления адресов IPv4/IPv6.
-
gethostbyname() может занимать много времени и блокировать другие потоки. (GVL не может быть освобождён, так как gethostbyname() не потокобезопасен.)
-
Этот метод использует функцию gethostbyname(), которая уже удалена из POSIX.
Этот метод получает информацию о хосте для hostname.
p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"]
static VALUE
sock_gethostname(VALUE obj)
{
#if defined(NI_MAXHOST)
# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST
#elif defined(HOST_NAME_MAX)
# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX
#else
# define RUBY_MAX_HOST_NAME_LEN 1024
#endif
long len = RUBY_MAX_HOST_NAME_LEN;
VALUE name;
name = rb_str_new(0, len);
while (gethostname(RSTRING_PTR(name), len) < 0) {
int e = errno;
switch (e) {
case ENAMETOOLONG:
#ifdef __linux__
case EINVAL:
/* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */
#endif
break;
default:
rb_syserr_fail(e, "gethostname(3)");
}
rb_str_modify_expand(name, len);
len += len;
}
rb_str_resize(name, strlen(RSTRING_PTR(name)));
return name;
} Возвращает имя хоста.
p Socket.gethostname #=> "hal"
Обратите внимание, что нет гарантии, что преобразование в IP-адрес с помощью gethostbyname, getaddrinfo и т. д. возможно. Если вам нужен локальный IP-адрес, используйте Socket.ip_address_list.
static VALUE
socket_s_getifaddrs(VALUE self)
{
return rsock_getifaddrs();
} Возвращает массив адресов интерфейсов. Элемент массива — экземпляр Socket::Ifaddr.
Этот метод можно использовать для поиска интерфейсов с поддержкой мультикаста:
pp Socket.getifaddrs.reject {|ifaddr|
!ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
}.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
#=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
# ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
Пример результата на GNU/Linux:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
Пример результата на FreeBSD:
pp Socket.getifaddrs #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
static VALUE
sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
{
VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
char *hptr, *pptr;
char hbuf[1024], pbuf[1024];
int fl;
struct rb_addrinfo *res = NULL;
struct addrinfo hints, *r;
int error, saved_errno;
union_sockaddr ss;
struct sockaddr *sap;
socklen_t salen;
sa = flags = Qnil;
rb_scan_args(argc, argv, "11", &sa, &flags);
fl = 0;
if (!NIL_P(flags)) {
fl = NUM2INT(flags);
}
tmp = rb_check_sockaddr_string_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
rb_raise(rb_eTypeError, "sockaddr length too big");
}
memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
}
sap = &ss.addr;
salen = RSTRING_SOCKLEN(sa);
goto call_nameinfo;
}
tmp = rb_check_array_type(sa);
if (!NIL_P(tmp)) {
sa = tmp;
MEMZERO(&hints, struct addrinfo, 1);
if (RARRAY_LEN(sa) == 3) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 2);
}
else if (RARRAY_LEN(sa) >= 4) {
af = RARRAY_AREF(sa, 0);
port = RARRAY_AREF(sa, 1);
host = RARRAY_AREF(sa, 3);
if (NIL_P(host)) {
host = RARRAY_AREF(sa, 2);
}
else {
/*
* 4th element holds numeric form, don't resolve.
* see rsock_ipaddr().
*/
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
hints.ai_flags |= AI_NUMERICHOST;
#endif
}
}
else {
rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
RARRAY_LEN(sa));
}
/* host */
if (NIL_P(host)) {
hptr = NULL;
}
else {
strncpy(hbuf, StringValueCStr(host), sizeof(hbuf));
hbuf[sizeof(hbuf) - 1] = '\0';
hptr = hbuf;
}
/* port */
if (NIL_P(port)) {
strcpy(pbuf, "0");
pptr = NULL;
}
else if (FIXNUM_P(port)) {
snprintf(pbuf, sizeof(pbuf), "%ld", NUM2LONG(port));
pptr = pbuf;
}
else {
strncpy(pbuf, StringValueCStr(port), sizeof(pbuf));
pbuf[sizeof(pbuf) - 1] = '\0';
pptr = pbuf;
}
hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
/* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
error = rb_getaddrinfo(hptr, pptr, &hints, &res);
if (error) goto error_exit_addr;
sap = res->ai->ai_addr;
salen = res->ai->ai_addrlen;
}
else {
rb_raise(rb_eTypeError, "expecting String or Array");
}
call_nameinfo:
error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf), fl);
if (error) goto error_exit_name;
if (res) {
for (r = res->ai->ai_next; r; r = r->ai_next) {
char hbuf2[1024], pbuf2[1024];
sap = r->ai_addr;
salen = r->ai_addrlen;
error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
pbuf2, sizeof(pbuf2), fl);
if (error) goto error_exit_name;
if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
rb_freeaddrinfo(res);
rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
}
}
rb_freeaddrinfo(res);
}
return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
error_exit_addr:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_socket_error("getaddrinfo", error);
error_exit_name:
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
rsock_raise_socket_error("getnameinfo", error);
UNREACHABLE_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) && !defined(__hpux)
/* Solaris if_tcp(7P) */
/* HP-UX has SIOCGLIFCONF too. But it uses different struct */
int fd = -1;
int ret;
struct lifnum ln;
struct lifconf lc;
const char *reason = NULL;
int save_errno;
int i;
VALUE list = Qnil;
lc.lifc_buf = NULL;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
memset(&ln, 0, sizeof(ln));
ln.lifn_family = AF_UNSPEC;
ret = ioctl(fd, SIOCGLIFNUM, &ln);
if (ret == -1) {
reason = "SIOCGLIFNUM";
goto finish;
}
memset(&lc, 0, sizeof(lc));
lc.lifc_family = AF_UNSPEC;
lc.lifc_flags = 0;
lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
lc.lifc_req = xmalloc(lc.lifc_len);
ret = ioctl(fd, SIOCGLIFCONF, &lc);
if (ret == -1) {
reason = "SIOCGLIFCONF";
goto finish;
}
list = rb_ary_new();
for (i = 0; i < ln.lifn_count; i++) {
struct lifreq *req = &lc.lifc_req[i];
if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
if (req->lifr_addr.ss_family == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id == 0) {
struct lifreq req2;
memcpy(req2.lifr_name, req->lifr_name, LIFNAMSIZ);
ret = ioctl(fd, SIOCGLIFINDEX, &req2);
if (ret == -1) {
reason = "SIOCGLIFINDEX";
goto finish;
}
((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index;
}
rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen));
}
}
finish:
save_errno = errno;
if (lc.lifc_buf != NULL)
xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
struct ifconf conf;
struct ifreq *req;
VALUE list = Qnil;
const char *reason = NULL;
int save_errno;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
retry:
conf.ifc_len = bufsize;
conf.ifc_req = (struct ifreq *)buf;
/* fprintf(stderr, "bufsize: %d\n", bufsize); */
ret = ioctl(fd, SIOCGIFCONF, &conf);
if (ret == -1) {
reason = "SIOCGIFCONF";
goto finish;
}
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
if (bufsize < conf.ifc_len) {
/* NetBSD returns required size for all interfaces. */
bufsize = conf.ifc_len + EXTRA_SPACE;
}
else {
bufsize = bufsize << 1;
}
if (buf == initbuf)
buf = NULL;
buf = xrealloc(buf, bufsize);
goto retry;
}
close(fd);
fd = -1;
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
(sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
finish:
save_errno = errno;
if (buf != initbuf)
xfree(buf);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_unicast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
int dummy1;
int dummy2;
int dummy3;
long dummy4;
long dummy5;
long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_anycast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_addresses_st *Next;
void *dummy1;
ip_adapter_unicast_address_t *FirstUnicastAddress;
ip_adapter_anycast_address_t *FirstAnycastAddress;
void *dummy2;
void *dummy3;
void *dummy4;
void *dummy5;
void *dummy6;
BYTE dummy7[8];
DWORD dummy8;
DWORD dummy9;
DWORD dummy10;
DWORD IfType;
int OperStatus;
DWORD dummy12;
DWORD dummy13[16];
void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
GetAdaptersAddresses_t pGetAdaptersAddresses;
ULONG len;
DWORD ret;
ip_adapter_addresses_t *adapters;
VALUE list;
h = LoadLibrary("iphlpapi.dll");
if (!h)
rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
FreeLibrary(h);
rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
ip_adapter_unicast_address_t *uni;
ip_adapter_anycast_address_t *any;
if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
continue;
for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
}
for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
}
}
FreeLibrary(h);
return list;
#endif
} Возвращает локальные IP-адреса в виде массива.
Массив содержит объекты Addrinfo.
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
#<Addrinfo: 192.168.0.128>,
#<Addrinfo: ::1>,
...] static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
VALUE domain, type, protocol;
int fd;
int d, t;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
return rsock_init_sock(sock, fd);
} Создаёт новый объект сокета.
domain должен быть областью связи, например: :INET, :INET6, :UNIX и т. д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т. д.
protocol — необязательный параметр и должен быть протоколом, определённым в области. Если протокол не указан, используется 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 как строку AF_UNIX sockaddr.
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 623
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 создает новый объект сокета, подключенный к host:port используя TCP/IP.
Если local_host:local_port задан, сокет привязывается к нему.
Необязательный последний аргумент opts — это параметры, представленные в виде хэша. opts может содержать следующие параметры:
- :connect_timeout
-
указывает таймаут в секундах.
- :resolv_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 856
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 и вызывает блок для каждого принятого соединения. Блок вызывается с сокетом и client_address в виде объекта 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 756
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 1026
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 999
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 972
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 884
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 1076
def self.unix(path) # :yield: socket
addr = Addrinfo.unix(path)
sock = addr.connect
if block_given?
begin
yield sock
ensure
sock.close
end
else
sock
end
end создает новый сокет, подключенный к path используя сокет UNIX.
Если задан блок, блок вызывается с сокетом. Возвращается значение блока. Сокет закрывается, когда этот метод возвращает значение.
Если блок не задан, возвращается сокет.
# talk to /tmp/sock socket.
Socket.unix("/tmp/sock") {|sock|
t = Thread.new { IO.copy_stream(sock, STDOUT) }
IO.copy_stream(STDIN, sock)
t.join
}
# File ext/socket/lib/socket.rb, line 1163
def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
unix_server_socket(path) {|serv|
accept_loop(serv, &b)
}
end создает UNIX сокет сервер на path. Он вызывает блок для каждого принятого сокета.
Если host указан, он используется с port для определения портов сервера.
Сокет не закрывается, когда блок возвращает значение. Поэтому приложение должно закрыть его.
Этот метод сначала удаляет файл сокета, на который указывает path, если файл является файлом сокета и принадлежит пользователю приложения. Это безопасно только в том случае, если каталог path не изменяется вредоносным пользователем. Поэтому не используйте /tmp/malicious-users-directory/socket. Обратите внимание, что /tmp/socket и /tmp/your-private-directory/socket безопасны, предполагая, что /tmp имеет липкий бит.
# Sequential echo server.
# It services only one client at a time.
Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# File ext/socket/lib/socket.rb, line 1106
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 в path.
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 1134 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
Публичные методы экземпляра
static VALUE
sock_accept(VALUE sock)
{
rb_io_t *fptr;
VALUE sock2;
union_sockaddr buf;
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
sock2 = rsock_s_accept(rb_cSocket,fptr->fd,&buf.addr,&len);
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
} Принимает следующее соединение. Возвращает новый объект Socket и объект Addrinfo.
serv = Socket.new(:INET, :STREAM, 0) serv.listen(5) c = Socket.new(:INET, :STREAM, 0) c.connect(serv.connect_address) p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>]
# File ext/socket/lib/socket.rb, line 592 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-structsockaddr, содержащийся в строке илиAddrinfoобъекте
Пример:
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) socket.connect( sockaddr ) socket.write( "GET / HTTP/1.0\r\n\r\n" ) results = socket.read
Исключения в системах на основе Unix
В системах на основе Unix при неудачном вызове connect могут быть возбуждены следующие системные исключения:
-
Errno::EACCES - запрещено право доступа к компоненту префиксного пути или доступ на запись в
socketзапрещен -
Errno::EADDRINUSE -
structsockaddr уже используется -
Errno::EADDRNOTAVAIL - указанный
structsockaddr недоступен на локальном компьютере -
Errno::EAFNOSUPPORT - указанный
structsockaddr не является допустимым адресом для семейства адресов указанногоsocket -
Errno::EALREADY - подключение уже происходит для указанного сокета
-
Errno::EBADF -
socketне является допустимым дескриптором файла -
Errno::ECONNREFUSED - целевой
structsockaddr не принимал подключения и отклонил запрос на подключение -
Errno::ECONNRESET - удаленный хост прервал запрос на подключение
-
Errno::EFAULT - к
structsockaddr невозможно получить доступ -
Errno::EHOSTUNREACH - целевой хост недоступен (вероятно, из-за того, что хост неактивен или удалённый маршрутизатор не может с ним связаться)
-
Errno::EINPROGRESS - для
socketустановлен O_NONBLOCK, и подключение не может быть установлено немедленно; подключение будет установлено асинхронно -
Errno::EINTR - попытка установить подключение была прервана доставкой сигнала, который был перехвачен; подключение будет установлено асинхронно
-
Errno::EISCONN - указанный
socketуже подключен -
Errno::EINVAL - длина адреса, используемого для
structsockaddr, не является допустимой длиной для семейства адресов или в sockaddr указано некорректное семейство -
Errno::ENAMETOOLONG - длина разрешённого пути превысила PATH_MAX
-
Errno::ENETDOWN - локальный интерфейс, используемый для достижения назначения, неактивен
-
Errno::ENETUNREACH - маршрута к сети нет
-
Errno::ENOBUFS - нет доступного буферного пространства
-
Errno::ENOSR - недостаточно ресурсов STREAMS для завершения операции
-
Errno::ENOTSOCK - аргумент
socketне ссылается на сокет -
Errno::EOPNOTSUPP - вызывающий
socketпрослушивает и не может быть подключен -
Errno::EPROTOTYPE -
structsockaddr имеет тип, отличный от сокета, привязанного к указанному адресу удалённого узла -
Errno::ETIMEDOUT - попытка подключения закончилась по тайм-ауту, прежде чем подключение было установлено.
В системах на основе Unix, если семейство адресов вызывающего socket равно AF_UNIX, при неудачном вызове connect могут быть возбуждены следующие исключения:
-
Errno::EIO - произошла ошибка ввода-вывода при чтении или записи в файловую систему
-
Errno::ELOOP - при преобразовании пути в
structsockaddr встречено слишком много символьных ссылок -
Errno::ENAMETOOLLONG - компонент пути превысил NAME_MAX символов или весь путь превысил PATH_MAX символов
-
Errno::ENOENT - компонент пути не указывает на существующий файл или путь пустой
-
Errno::ENOTDIR - компонент префикса пути в
structsockaddr не является каталогом
Исключения в Windows
В системах Windows при неудачном вызове connect могут быть возбуждены следующие системные исключения:
-
Errno::ENETDOWN - сеть неактивна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется
-
Errno::EINTR - сокет был отменён
-
Errno::EINPROGRESS - заблокированный сокет выполняется или поставщик услуг всё ещё обрабатывает функцию обратного вызова. Или происходит неблокирующий вызов connect для
socket. -
Errno::EALREADY - см. Errno::EINVAL
-
Errno::EADDRNOTAVAIL - удалённый адрес не является допустимым адресом, например, ADDR_ANY TODO проверить ADDRANY ПО
INADDR_ANY -
Errno::EAFNOSUPPORT - адреса в указанном семействе не могут быть использованы с этим
socket -
Errno::ECONNREFUSED - целевой
structsockaddr не принимал подключений и отклонил запрос на подключение -
Errno::EFAULT - внутренний адрес сокета или параметр длины адреса слишком мал или не является допустимой частью адреса пользователя
-
Errno::EINVAL -
socketявляется прослушивающим сокетом -
Errno::EISCONN -
socketуже подключен -
Errno::ENETUNREACH - сеть не может быть достигнута с этого узла в данный момент
-
Errno::EHOSTUNREACH - маршрута к сети нет
-
Errno::ENOBUFS - нет доступного буферного пространства
-
Errno::ENOTSOCK - аргумент
socketне ссылается на сокет -
Errno::ETIMEDOUT - попытка подключения закончилась по тайм-ауту, прежде чем подключение было установлено.
-
Errno::EWOULDBLOCK - сокет помечен как неблокирующий, и подключение не может быть выполнено немедленно
-
Errno::EACCES - попытка подключить сокет данных к широковещательному адресу не удалась
См. также
-
Справочные страницы connect в системах на основе Unix
-
Функция connect в справочнике функций Winsock Майкрософт
# File ext/socket/lib/socket.rb, line 1213 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 463
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 - socket не привязан к локальному адресу, и протокол не поддерживает прослушивание на несвязанном сокете
-
Errno::EINVAL - socket уже подключён
-
Errno::ENOTSOCK - аргумент socket не относится к сокету
-
Errno::EOPNOTSUPP - протокол socket не поддерживает listen
-
Errno::EACCES - у вызывающего процесса нет соответствующих привилегий
-
Errno::EINVAL - socket был закрыт
-
Errno::ENOBUFS - в системе недостаточно ресурсов для завершения вызова
Исключения Windows
В системах Windows при неудачном вызове listen могут быть подняты следующие системные исключения:
-
Errno::ENETDOWN - сеть недоступна
-
Errno::EADDRINUSE - локальный адрес сокета уже используется. Это обычно происходит во время выполнения bind, но может быть отложено, если вызов bind был к частично универсальному адресу (включая ADDR_ANY) и если конкретный адрес необходимо зафиксировать во время вызова listen
-
Errno::EINPROGRESS - вызов Windows Sockets 1.1 выполняется или поставщик сервиса всё ещё обрабатывает функцию обратного вызова
-
Errno::EINVAL -
socketне был связан с помощью вызова bind. -
Errno::EISCONN -
socketуже подключён -
Errno::EMFILE - больше нет доступных дескрипторов сокетов
-
Errno::ENOBUFS - нет свободного буфера
-
Errno::ENOTSOC -
socketне является сокетом -
Errno::EOPNOTSUPP - указанный
socketне является типом, поддерживающим метод listen
См. также
-
Страницы руководства listen в системах на основе Unix
-
Функция listen в справочнике функций Winsock от Microsoft
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 535 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#recvfrom_nonblock возвращает пустую строку в качестве данных. Значение зависит от сокета: EOF для TCP, пустой пакет для UDP и т. д.
Параметры
-
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 sock)
{
rb_io_t *fptr;
VALUE sock2;
union_sockaddr buf;
socklen_t len = (socklen_t)sizeof buf;
GetOpenFile(sock, fptr);
sock2 = rsock_s_accept(0,fptr->fd,&buf.addr,&len);
return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
} Принимает входящее подключение, возвращая массив, содержащий дескриптор файла (целое число) входящего подключения, client_socket_fd, и Addrinfo, client_addrinfo.
Пример
# In one script, start this first
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )
client_fd, client_addrinfo = socket.sysaccept
client_socket = Socket.for_fd( client_fd )
puts "The client said, '#{client_socket.readline.chomp}'"
client_socket.puts "Hello from script one!"
socket.close
# In another script, start this second
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.connect( sockaddr )
socket.puts "Hello from script 2."
puts "The server said, '#{socket.readline.chomp}'"
socket.close
Обратитесь к Socket#accept для получения исключений, которые могут возникнуть при вызове sysaccept.
См. также
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.