класс 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_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
DataLink 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
-
Kernelустройство пользовательского интерфейса - AF_NS
-
Протоколы XEROX NS
- AF_OSI
-
Протоколы ISO Открытых систем
- AF_PACKET
-
Прямой доступ к уровню канала
- AF_PPP
-
Протокол точка-точка
- AF_PPPOX
-
Общий транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- AF_PUP
-
Протокол PARC Universal Packet
- AF_RDS
-
Протокол надежных дейтаграммных сокетов (RDS)
- AF_ROUTE
-
Внутренний протокол маршрутизации
- AF_SIP
-
Простой интернет-протокол
- AF_SNA
-
Протокол IBM SNA
- AF_SYSTEM
- AF_TIPC
-
Протокол TIPC, «кластерные сокеты области»
- AF_UNIX
-
Сокеты UNIX
- AF_UNSPEC
-
Неопределенный протокол, любая поддерживаемая семейство адресов
- AF_VSOCK
-
Протокол VSOCK (первоначально «VMWare VSockets») для межвиртуальной коммуникации
- AF_XDP
-
Интерфейс XDP (быстрый путь данных)
- AI_ADDRCONFIG
-
Принять только если какой-либо адрес назначен
- AI_ALL
-
Разрешить все адреса
- AI_CANONNAME
-
Заполнить каноническое имя
- AI_DEFAULT
-
Флаги по умолчанию для getaddrinfo
- AI_MASK
-
Действительная маска флагов для getaddrinfo (не для использования приложением)
- AI_NUMERICHOST
-
Запретить разрешение имени хоста
- AI_NUMERICSERV
-
Запретить разрешение имени службы
- AI_PASSIVE
-
Получить адрес для использования с bind()
- AI_V4MAPPED
-
Принять IPv4-отображенные IPv6 адреса
- AI_V4MAPPED_CFG
-
Принять IPv4-отображенные адреса, если ядро их поддерживает
- 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
-
запретить мостирование этого устройства
- IFF_DORMANT
-
драйвер сигнализирует о приостановке
- IFF_DRV_OACTIVE
-
очередь аппаратного транзита TX заполнена
- 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
-
требуются 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.
- IPPROTO_NONE
-
IP6 без следующего заголовка.
- IPPROTO_PUP
-
Протокол универсальных пакетов PARC.
- IPPROTO_RAW
-
Сырой пакет IP.
- IPPROTO_ROUTING
-
Заголовок маршрутизации IP6.
- IPPROTO_TCP
-
TCP.
- IPPROTO_TP
-
Класс 4 транспортного протокола ISO.
- IPPROTO_UDP
-
UDP.
- IPPROTO_XTP
-
Протокол транспортного ускорения.
- 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 с подстановкой по умолчанию.
- IPX_TYPE
- IP_ADD_MEMBERSHIP
-
Добавление членства в группу мультивещания.
- IP_ADD_SOURCE_MEMBERSHIP
-
Добавление членства в группу мультивещания.
- IP_BLOCK_SOURCE
-
Заблокировать пакеты IPv4 мультивещания с указанным исходным адресом.
- IP_DEFAULT_MULTICAST_LOOP
-
Стандартная петля обратной связи мультивещания.
- IP_DEFAULT_MULTICAST_TTL
-
Стандартный TTL мультивещания.
- IP_DONTFRAG
-
Не фрагментировать пакеты.
- IP_DROP_MEMBERSHIP
-
Удалить членство в группе мультивещания.
- IP_DROP_SOURCE_MEMBERSHIP
-
Удалить членство в группе мультивещания.
- IP_FREEBIND
-
Разрешить привязку к несуществующим IP-адресам.
- IP_HDRINCL
-
Заголовок включен в данные.
- IP_IPSEC_POLICY
-
Политика безопасности IPsec.
- IP_MAX_MEMBERSHIPS
-
Максимальное количество групп мультивещания, к которым может присоединиться сокет.
- IP_MINTTL
-
Минимальное значение TTL, разрешенное для полученных пакетов.
- IP_MSFILTER
-
Фильтр источников мультивещания.
- IP_MTU
-
Максимальный размер транспортного блока сокета.
- IP_MTU_DISCOVER
-
Обнаружение пути MTU.
- IP_MULTICAST_IF
-
Интерфейс IP мультивещания.
- IP_MULTICAST_LOOP
-
Петля обратной связи IP мультивещания.
- IP_MULTICAST_TTL
-
TTL IP мультивещания.
- IP_ONESBCAST
-
Принудительно задать адрес необращённой широковещательной передачи для исходящих широковещательных датаграмм.
- IP_OPTIONS
-
Опции IP, которые должны быть включены в пакеты.
- IP_PASSSEC
-
Получение контекста безопасности с датаграммой.
- IP_PKTINFO
-
Получение информации о пакете с датаграммами.
- IP_PKTOPTIONS
-
Получение опций пакета с датаграммами.
- IP_PMTUDISC_DO
-
Всегда отправлять кадр DF.
- IP_PMTUDISC_DONT
-
Никогда не отправлять кадр DF.
- IP_PMTUDISC_WANT
-
Использовать подсказки по каждому маршруту.
- IP_PORTRANGE
-
Setдиапазон портов для сокетов с неуказанными номерами портов. - 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
-
Dataзавершает подключение. - MSG_EOR
-
Dataзавершает запись. - MSG_ERRQUEUE
-
Получить сообщение из очереди ошибок.
- MSG_FASTOPEN
-
Уменьшение шага процесса рукопожатия
- MSG_FIN
- MSG_FLUSH
-
Начало последовательности удержания. Выгружается в so_temp
- MSG_HAVEMORE
-
Dataготов к чтению - MSG_HOLD
-
Удержание фрагмента в so_temp
- MSG_MORE
-
Отправитель отправит больше данных
- MSG_NOSIGNAL
-
Не генерировать SIGPIPE
- MSG_OOB
-
Processданные вне очереди - MSG_PEEK
-
Предварительный просмотр входящего сообщения
- MSG_PROXY
-
Ожидание полного запроса
- MSG_RCVMORE
-
Dataостаётся в текущем пакете - MSG_RST
- MSG_SEND
-
Отправка пакета в so_temp
- MSG_SYN
- MSG_TRUNC
-
Dataотброшено перед доставкой - 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интерфейса канала - PF_ECMA
-
Протоколы Европейских производителей компьютеров
- PF_HYLINK
-
Протокол NSC Hyperchannel
- PF_IB
-
Родная адресация InfiniBand
- PF_IMPLINK
-
Протокол ARPANET IMP
- PF_INET
-
Протокол IPv4
- PF_INET6
-
Протокол IPv6
- PF_IPX
-
Протокол IPX
- PF_ISDN
-
Интегрированная цифровая сеть с услугами
- PF_ISO
-
Протоколы ISO Открытой системы взаимосвязи
- PF_KCM
-
Интерфейс KCM (ядерный множитель подключений)
- PF_KEY
-
Протокол управления ключами, первоначально разработанный для использования с IPsec
- PF_LAT
-
Протокол локальной транспортной сети
- PF_LINK
-
Интерфейс уровня канала
- PF_LLC
-
Протокол логического управления каналом (IEEE 802.2 LLC)
- PF_LOCAL
-
Внутрисистемные протоколы
- PF_MAX
-
Максимальная семейство адресов для этой платформы
- PF_MPLS
-
Многопротокольная коммутация меток
- PF_NATM
-
Родной доступ ATM
- PF_NDRV
-
Прямой доступ к драйверу сети
- PF_NETBIOS
-
NetBIOS
- PF_NETGRAPH
-
Сокеты Netgraph
- PF_NETLINK
-
Kernelинтерфейс устройства пользователя - PF_NS
-
Протоколы XEROX NS
- PF_OSI
-
Протоколы ISO Открытой системы взаимосвязи
- PF_PACKET
-
Прямой доступ к каналу связи
- PF_PIP
- PF_PPP
-
Протокол точка-точка
- PF_PPPOX
-
Обобщённый транспортный уровень PPP для настройки туннелей L2 (L2TP и PPPoE)
- PF_PUP
-
Протокол PARC Universal Packet
- PF_RDS
-
Протокол надёжных сокетов дейтаграмм (RDS)
- PF_ROUTE
-
Внутренний протокол маршрутизации
- PF_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 (быстрый путь данных)
- 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_CLOEXEC
-
Setфлаг close-on-exec (FD_CLOEXEC) для нового дескриптора файла. - SOCK_DGRAM
-
Сокет дейтаграмм обеспечивает обмен сообщениями без установления соединения и без гарантии доставки
- SOCK_NONBLOCK
-
Setфлаг состояния файла 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
-
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_INCOMING_CPU
-
Получение процессора, подключённого к сокету (Linux 3.19)
- SO_INCOMING_NAPI_ID
-
Получение идентификатора napi, присоединённого к очереди RX (Linux 4.12)
- SO_KEEPALIVE
-
Поддерживать соединения живыми
- SO_LINGER
-
Ожидать при закрытии, если присутствуют данные
- SO_LOCK_FILTER
-
Заблокировать фильтр, присоединённый к сокету (Linux 3.9)
- SO_MAC_EXEMPT
-
Исключение обязательного управления доступом для незамеченных партнёров
- SO_MARK
-
Setметку для маршрутизации на основе меток (Linux 2.6.25) - SO_MAX_PACING_RATE
-
Ограничить скорость, вычисленную транспортным уровнем. [байты в секунду] (Linux 3.13)
- SO_NKE
-
Установить расширение сокетного уровня Сети
Kernel - 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_RTABLE
-
Setтаблицу маршрутизации для этого сокета (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
-
Setсвязанную таблицу маршрутизации для сокета (FreeBSD) - 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_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 Transactions (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
-
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
-
Не выполнять push для последнего блока записи
- TCP_QUEUE_SEQ
-
Последовательность очереди для режима ремонта (Linux 3.5, glibc 2.18)
- TCP_QUICKACK
-
Включить режим quickack (Linux 2.4.4, glibc 2.3)
- TCP_REPAIR
-
Режим ремонта (Linux 3.5, glibc 2.18)
- TCP_REPAIR_OPTIONS
-
Параметры для режима ремонта (Linux 3.5, glibc 2.18)
- TCP_REPAIR_QUEUE
-
Очередь для режима ремонта (Linux 3.5, glibc 2.18)
- TCP_SYNCNT
-
Количество повторных передач SYN перед разрывом соединения (Linux 2.4, glibc 2.2)
- TCP_THIN_DUPACK
-
Обработка дублированных подтверждений для тонких потоков (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 803
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 вместо этого. Этот метод устарел по следующим причинам:
-
Третий элемент результата — семейство адресов первого адреса. Семейства адресов остальных адресов не возвращаются.
-
Нестандартное представление адреса: двоичная строка из 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_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)
/* 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;
if (lc.lifc_buf != NULL)
xfree(lc.lifc_req);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#elif defined(SIOCGIFCONF)
int fd = -1;
int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
char initbuf[4096+EXTRA_SPACE];
char *buf = initbuf;
int bufsize;
struct ifconf conf;
struct ifreq *req;
VALUE list = Qnil;
const char *reason = NULL;
int save_errno;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
rb_sys_fail("socket(2)");
bufsize = sizeof(initbuf);
buf = initbuf;
retry:
conf.ifc_len = bufsize;
conf.ifc_req = (struct ifreq *)buf;
/* fprintf(stderr, "bufsize: %d\n", bufsize); */
ret = ioctl(fd, SIOCGIFCONF, &conf);
if (ret == -1) {
reason = "SIOCGIFCONF";
goto finish;
}
/* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
if (bufsize - EXTRA_SPACE < conf.ifc_len) {
if (bufsize < conf.ifc_len) {
/* NetBSD returns required size for all interfaces. */
bufsize = conf.ifc_len + EXTRA_SPACE;
}
else {
bufsize = bufsize << 1;
}
if (buf == initbuf)
buf = NULL;
buf = xrealloc(buf, bufsize);
goto retry;
}
close(fd);
fd = -1;
list = rb_ary_new();
req = conf.ifc_req;
while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
struct sockaddr *addr = &req->ifr_addr;
if (IS_IP_FAMILY(addr->sa_family)) {
rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
}
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
# define _SIZEOF_ADDR_IFREQ(r) \
(sizeof(struct ifreq) + \
(sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
(r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
0))
# endif
req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
}
finish:
save_errno = errno;
if (buf != initbuf)
xfree(buf);
if (fd != -1)
close(fd);
errno = save_errno;
if (reason)
rb_syserr_fail(save_errno, reason);
return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
typedef struct ip_adapter_unicast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_unicast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
int dummy1;
int dummy2;
int dummy3;
long dummy4;
long dummy5;
long dummy6;
} ip_adapter_unicast_address_t;
typedef struct ip_adapter_anycast_address_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_anycast_address_st *Next;
struct {
struct sockaddr *lpSockaddr;
int iSockaddrLength;
} Address;
} ip_adapter_anycast_address_t;
typedef struct ip_adapter_addresses_st {
unsigned LONG_LONG dummy0;
struct ip_adapter_addresses_st *Next;
void *dummy1;
ip_adapter_unicast_address_t *FirstUnicastAddress;
ip_adapter_anycast_address_t *FirstAnycastAddress;
void *dummy2;
void *dummy3;
void *dummy4;
void *dummy5;
void *dummy6;
BYTE dummy7[8];
DWORD dummy8;
DWORD dummy9;
DWORD dummy10;
DWORD IfType;
int OperStatus;
DWORD dummy12;
DWORD dummy13[16];
void *dummy14;
} ip_adapter_addresses_t;
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
HMODULE h;
GetAdaptersAddresses_t pGetAdaptersAddresses;
ULONG len;
DWORD ret;
ip_adapter_addresses_t *adapters;
VALUE list;
h = LoadLibrary("iphlpapi.dll");
if (!h)
rb_notimplement();
pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
if (!pGetAdaptersAddresses) {
FreeLibrary(h);
rb_notimplement();
}
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
if (ret != ERROR_SUCCESS) {
errno = rb_w32_map_errno(ret);
FreeLibrary(h);
rb_sys_fail("GetAdaptersAddresses");
}
list = rb_ary_new();
for (; adapters; adapters = adapters->Next) {
ip_adapter_unicast_address_t *uni;
ip_adapter_anycast_address_t *any;
if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */
continue;
for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
}
for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
}
}
FreeLibrary(h);
return list;
#endif
} Возвращает локальные IP-адреса в виде массива.
Массив содержит объекты Addrinfo.
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
#<Addrinfo: 192.168.0.128>,
#<Addrinfo: ::1>,
...] static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
VALUE domain, type, protocol;
int fd;
int d, t;
rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
if (NIL_P(protocol))
protocol = INT2FIX(0);
setup_domain_and_type(domain, &d, type, &t);
fd = rsock_socket(d, t, NUM2INT(protocol));
if (fd < 0) rb_sys_fail("socket(2)");
return rsock_init_sock(sock, fd);
} Создаёт новый объект сокета.
domain должно быть областью связи, например: :INET, :INET6, :UNIX и т.д.
socktype должен быть типом сокета, например: :STREAM, :DGRAM, :RAW и т.д.
protocol необязателен и должен быть протоколом, определённым в области. Если protocol не задан, внутри используется 0.
Socket.new(:INET, :STREAM) # TCP socket Socket.new(:INET, :DGRAM) # UDP socket Socket.new(:UNIX, :STREAM) # UNIX stream socket Socket.new(:UNIX, :DGRAM) # UNIX datagram socket
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
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 626
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
-
указывает таймаут в секундах.
Если задан блок, блок вызывается с сокетом. Возвращаемое значение блока возвращается. Сокет закрывается при возвращении этого метода.
Если блок не задан, сокет возвращается.
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 859
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 759
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 1029
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 1002
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 975
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 887
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 1079
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 1166
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".
# 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 1109
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 1137 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 596 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, не является допустимой длиной для семейства адресов, или вstructsockaddr указано недопустимое семейство -
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 TO
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 от Microsoft
# File ext/socket/lib/socket.rb, line 1216 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 467
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 не поддерживает прослушивание
-
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 в справочнике функций 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 539 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- словарь ключевых слов, поддерживающий ‘exception: false`
Пример
# In one file, start this first
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
client, client_addrinfo = socket.accept
begin # emulate blocking recvfrom
pair = client.recvfrom_nonblock(20)
rescue IO::WaitReadable
IO.select([client])
retry
end
data = pair[0].chomp
puts "I only received 20 bytes '#{data}'"
sleep 1
socket.close
# In another file, start this second
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(2200, 'localhost')
socket.connect(sockaddr)
socket.puts "Watch this get cut short!"
socket.close
Обратитесь к 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.