class Socket
Class Socket 提供了对底层操作系统套接字实现的访问。它可以用于提供比特定协议套接字类更多的操作系统特定功能。
在 Socket::Constants 下定义的常量也在 Socket 下定义。 例如,Socket::AF_INET 和 Socket::Constants::AF_INET 都可以使用。 请参阅 Socket::Constants 获取常量列表。
什么是套接字?¶ ↑
套接字是双向通信通道的端点。 套接字可以在进程内、同一台机器上的进程之间或不同机器之间进行通信。 有许多类型的套接字:例如,TCPSocket、UDPSocket 或 UNIXSocket。
套接字有自己的词汇
域:协议族
类型:两个端点之间通信的类型,通常是
协议:通常是 零。 这可用于识别协议的变体。
主机名:网络接口的标识符
- 
一个字符串(主机名、IPv4 或 IPv6 地址或 broadcast,指定广播地址)
- 
一个零长度字符串,指定 INADDR_ANY
- 
一个整数(解释为以主机字节顺序的二进制地址)。 
快速开始¶ ↑
与等效的 C 编程接口相比,许多类(例如 TCPSocket、UDPSocket 或 UNIXSocket)简化了套接字的使用。
让我们以类似于 C 的方式使用 IPv4 协议创建一个 internet 套接字
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 手册页或 Windows WinSock 参考。
便捷方法¶ ↑
尽管创建套接字的一般方法是 Socket.new,但在大多数情况下,有几种套接字创建方法。
- TCP 客户端套接字
- TCP 服务器套接字
- UNIX 客户端套接字
- UNIX 服务器套接字
文档作者¶ ↑
- 
Zach Dennis 
- 
Sam Roberts 
- 
来自 The Pragmatic Bookshelf 的 Programming Ruby。 
本文档中的许多材料经 The Pragmatic Bookshelf 许可取自 Programming Ruby。
常量
- ADDRESS_FAMILIES
- AF_ALG
- 
          内核加密 API 接口 
- AF_APPLETALK
- 
          AppleTalk 协议 
- AF_ATM
- 
          异步传输模式 
- AF_AX25
- 
          AX.25 协议 
- AF_BLUETOOTH
- 
          蓝牙底层套接字协议 
- AF_CAN
- 
          控制器区域网络汽车总线协议 
- AF_CCITT
- 
          CCITT (现在的 ITU-T) 协议 
- AF_CHAOS
- 
          MIT CHAOS 协议 
- AF_CNT
- 
          计算机网络技术 
- AF_COIP
- 
          面向连接的 IP 
- AF_DATAKIT
- 
          Datakit 协议 
- AF_DEC
- 
          DECnet 协议 
- AF_DECnet
- 
          DECnet 协议 
- AF_DLI
- 
          DEC 直接数据链路接口协议 
- AF_E164
- 
          CCITT (ITU-T) E.164 建议 
- AF_ECMA
- 
          欧洲计算机制造商协议 
- AF_HYLINK
- 
          NSC Hyperchannel 协议 
- AF_IB
- 
          InfiniBand 本机寻址 
- AF_IMPLINK
- 
          ARPANET IMP 协议 
- AF_INET
- 
          IPv4 协议 
- AF_INET6
- 
          IPv6 协议 
- AF_IPX
- 
          IPX 协议 
- AF_ISDN
- 
          综合业务数字网络 
- AF_ISO
- 
          ISO 开放系统互连协议 
- AF_KCM
- 
          KCM(内核连接多路复用器)接口 
- AF_KEY
- 
          密钥管理协议,最初为与 IPsec 一起使用而开发 
- AF_LAT
- 
          局域传输协议 
- AF_LINK
- 
          链路层接口 
- AF_LLC
- 
          逻辑链路控制(IEEE 802.2 LLC)协议 
- AF_LOCAL
- 
          主机内部协议 
- AF_MAX
- 
          此平台的最大地址族 
- AF_MPLS
- 
          多协议标签交换 
- AF_NATM
- 
          本机 ATM 访问 
- AF_NDRV
- 
          网络驱动程序原始访问 
- AF_NETBIOS
- 
          NetBIOS 
- AF_NETGRAPH
- 
          Netgraph 套接字 
- AF_NETLINK
- 
          内核用户界面设备 
- AF_NS
- 
          XEROX NS 协议 
- AF_OSI
- 
          ISO 开放系统互连协议 
- AF_PACKET
- 
          直接链路层访问 
- AF_PPP
- 
          点对点协议 
- AF_PPPOX
- 
          通用 PPP 传输层,用于设置 L2 隧道(L2TP 和 PPPoE) 
- AF_PUP
- 
          PARC 通用数据包协议 
- AF_RDS
- 
          可靠数据报套接字 (RDS) 协议 
- AF_ROUTE
- 
          内部路由协议 
- AF_SIP
- 
          简单 Internet 协议 
- 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 映射的地址 
- CONNECTION_ATTEMPT_DELAY
- EAI_ADDRFAMILY
- 
          不支持主机名的地址族 
- EAI_AGAIN
- 
          名称解析中的临时故障 
- EAI_BADFLAGS
- 
          无效标志 
- EAI_BADHINTS
- 
          提示的无效值 
- EAI_FAIL
- 
          名称解析中的不可恢复的故障 
- EAI_FAMILY
- 
          不支持的地址族 
- EAI_MAX
- 
          来自 getaddrinfo 的最大错误代码 
- EAI_MEMORY
- 
          内存分配失败 
- EAI_NODATA
- 
          没有与主机名关联的地址 
- EAI_NONAME
- 
          主机名或服务名未知 
- EAI_OVERFLOW
- 
          参数缓冲区溢出 
- EAI_PROTOCOL
- 
          解析的协议未知 
- EAI_SERVICE
- 
          套接字类型不支持服务名称 
- EAI_SOCKTYPE
- 
          不支持的套接字类型 
- EAI_SYSTEM
- 
          errno 中返回的系统错误 
- HOSTNAME_RESOLUTION_QUEUE_UPDATED
- IFF_802_1Q_VLAN
- 
          802.1Q VLAN 设备 
- 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
- 
          以太网桥接设备 
- 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
- 
          绑定主设备,负载均衡。 
- IFF_MASTER_ARPMON
- 
          绑定主设备,正在使用 ARP 监控 
- IFF_MONITOR
- 
          用户请求的监控模式 
- IFF_MULTICAST
- 
          支持多播 
- IFF_NOARP
- 
          无地址解析协议 
- IFF_NOTRAILERS
- 
          避免使用尾部 
- IFF_OACTIVE
- 
          正在传输 
- IFF_OVS_DATAPATH
- 
          设备用作 Open vSwitch 数据路径端口 
- IFF_POINTOPOINT
- 
          点对点链路 
- IFF_PORTSEL
- 
          可以设置媒体类型 
- IFF_PPROMISC
- 
          用户请求的混杂模式 
- 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
- 
          ISO 传输协议类 4 
- IPPROTO_UDP
- 
          UDP 
- IPPROTO_XTP
- 
          快速传输协议 
- IPV6_ADRESS_FORMAT
- IPV6_CHECKSUM
- 
          原始套接字的校验和偏移量 
- IPV6_DONTFRAG
- 
          不要分段数据包 
- IPV6_DSTOPTS
- 
          目标选项 
- IPV6_HOPLIMIT
- 
          跳数限制 
- IPV6_HOPOPTS
- 
          逐跳选项 
- IPV6_JOIN_GROUP
- 
          加入组会员资格 
- IPV6_LEAVE_GROUP
- 
          离开组会员资格 
- IPV6_MTU_DISCOVER
- 
          路径 MTU 发现 
- IPV6_MULTICAST_HOPS
- 
          IP6 多播跳数 
- IPV6_MULTICAST_IF
- 
          IP6 多播接口 
- IPV6_MULTICAST_LOOP
- 
          IP6 多播环回 
- IPV6_NEXTHOP
- 
          下一跳地址 
- IPV6_PATHMTU
- 
          检索当前路径 MTU 
- IPV6_PKTINFO
- 
          接收带有数据报的数据包信息 
- IPV6_RECVDSTOPTS
- 
          接收响应的所有 IP6 选项 
- IPV6_RECVERR
- 
          启用扩展的可靠错误消息传递 
- IPV6_RECVHOPLIMIT
- 
          接收带有数据报的跳数限制 
- IPV6_RECVHOPOPTS
- 
          接收逐跳选项 
- IPV6_RECVPATHMTU
- 
          接收带有数据报的当前路径 MTU 
- IPV6_RECVPKTINFO
- 
          接收目标 IP 地址和传入接口 
- IPV6_RECVRTHDR
- 
          接收路由头 
- IPV6_RECVTCLASS
- 
          接收流量类别 
- IPV6_RTHDR
- 
          允许删除粘性路由头 
- IPV6_RTHDRDSTOPTS
- 
          允许删除粘性目标选项头 
- IPV6_RTHDR_TYPE_0
- 
          路由头类型 0 
- IPV6_TCLASS
- 
          指定流量类别 
- IPV6_UNICAST_HOPS
- 
          IP6 单播跳数 
- IPV6_USE_MIN_MTU
- 
          使用最小 MTU 大小 
- IPV6_V6ONLY
- 
          仅使用通配符绑定绑定 IPv6 
- IPX_TYPE
- 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
- 
          IP 多播 TTL 
- IP_ONESBCAST
- 
          强制传出的广播数据报具有无向广播地址 
- IP_OPTIONS
- 
          要包含在数据包中的 IP 选项 
- IP_PASSSEC
- 
          使用数据报检索安全上下文 
- IP_PKTINFO
- 
          接收带有数据报的数据包信息 
- IP_PKTOPTIONS
- 
          接收带有数据报的数据包选项 
- IP_PMTUDISC_DO
- 
          始终发送 DF 帧 
- IP_PMTUDISC_DONT
- 
          从不发送 DF 帧 
- IP_PMTUDISC_WANT
- 
          使用每个路由提示 
- IP_PORTRANGE
- 
          设置未指定端口号的套接字的端口范围 
- IP_RECVDSTADDR
- 
          接收带有数据报的 IP 目标地址 
- IP_RECVERR
- 
          启用扩展的可靠错误消息传递 
- IP_RECVIF
- 
          接收带有数据报的接口信息 
- IP_RECVOPTS
- 
          接收带有数据报的所有 IP 选项 
- IP_RECVRETOPTS
- 
          接收响应的所有 IP 选项 
- IP_RECVSLLA
- 
          接收带有数据报的链路层地址 
- IP_RECVTOS
- 
          接收传入数据包的 TOS 
- IP_RECVTTL
- 
          接收带有数据报的 IP TTL 
- IP_RETOPTS
- 
          要包含在数据报中的 IP 选项 
- IP_ROUTER_ALERT
- 
          通知中转路由器更仔细地检查 IP 数据包的内容 
- IP_SENDSRCADDR
- 
          传出 UDP 数据报的源地址 
- IP_TOS
- 
          IP 服务类型 
- IP_TRANSPARENT
- 
          透明代理 
- IP_TTL
- 
          IP 生存时间 
- IP_UNBLOCK_SOURCE
- 
          取消阻止具有给定源地址的 IPv4 多播数据包 
- IP_XFRM_POLICY
- LOCAL_CONNWAIT
- 
          连接会阻塞直到被接受 
- LOCAL_CREDS
- 
          将凭据传递给接收者 
- LOCAL_PEERCRED
- 
          检索对等凭据 
- MCAST_BLOCK_SOURCE
- 
          阻止来自此源的多播数据包 
- MCAST_EXCLUDE
- 
          独占多播源过滤器 
- MCAST_INCLUDE
- 
          包含多播源过滤器 
- MCAST_JOIN_GROUP
- 
          加入多播组 
- MCAST_JOIN_SOURCE_GROUP
- 
          加入多播源组 
- MCAST_LEAVE_GROUP
- 
          离开多播组 
- MCAST_LEAVE_SOURCE_GROUP
- 
          离开多播源组 
- MCAST_MSFILTER
- 
          多播源过滤 
- MCAST_UNBLOCK_SOURCE
- 
          取消阻止来自此源的多播数据包 
- MSG_COMPAT
- 
          记录结束 
- MSG_CONFIRM
- 
          确认路径有效性 
- MSG_CTRUNC
- 
          控制数据在传递前丢失 
- MSG_DONTROUTE
- 
          发送时无需使用路由表 
- MSG_DONTWAIT
- 
          此消息应为非阻塞 
- MSG_EOF
- 
          数据完成连接 
- MSG_EOR
- 
          数据完成记录 
- MSG_ERRQUEUE
- 
          从错误队列中提取消息 
- MSG_FASTOPEN
- 
          减少握手过程的步骤 
- MSG_FIN
- MSG_FLUSH
- 
          保持序列的开始。转储到 so_temp 
- MSG_HAVEMORE
- 
          数据准备好读取 
- MSG_HOLD
- 
          在 so_temp 中保持片段 
- MSG_MORE
- 
          发送方将发送更多数据 
- MSG_NOSIGNAL
- 
          不生成 SIGPIPE 
- MSG_OOB
- 
          处理带外数据 
- MSG_PEEK
- 
          窥视传入的消息 
- MSG_PROXY
- 
          等待完整请求 
- MSG_RCVMORE
- 
          数据保留在当前数据包中 
- MSG_RST
- MSG_SEND
- 
          在 so_temp 中发送数据包 
- MSG_SYN
- MSG_TRUNC
- 
          数据在传递前被丢弃 
- MSG_WAITALL
- 
          等待完整请求或错误 
- NI_DGRAM
- 
          指定的服务是数据报服务(查找 UDP 端口) 
- NI_MAXHOST
- 
          主机名的最大长度 
- NI_MAXSERV
- 
          服务名称的最大长度 
- NI_NAMEREQD
- 
          需要名称 
- NI_NOFQDN
- 
          本地主机不需要 FQDN,仅返回本地部分 
- NI_NUMERICHOST
- 
          返回数字地址 
- NI_NUMERICSERV
- 
          将服务名称作为数字字符串返回 
- PF_ALG
- 
          内核加密 API 接口 
- PF_APPLETALK
- 
          AppleTalk 协议 
- PF_ATM
- 
          异步传输模式 
- PF_AX25
- 
          AX.25 协议 
- PF_BLUETOOTH
- 
          蓝牙底层套接字协议 
- PF_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 直接数据链路接口协议 
- 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
- 
          内核用户界面设备 
- PF_NS
- 
          XEROX NS 协议 
- PF_OSI
- 
          ISO 开放系统互连协议 
- PF_PACKET
- 
          直接链路层访问 
- PF_PIP
- 
          帮助识别 PIP 数据包 
- PF_PPP
- 
          点对点协议 
- PF_PPPOX
- 
          通用 PPP 传输层,用于设置 L2 隧道(L2TP 和 PPPoE) 
- PF_PUP
- 
          PARC 通用数据包协议 
- PF_RDS
- 
          可靠数据报套接字 (RDS) 协议 
- PF_ROUTE
- 
          内部路由协议 
- PF_RTIP
- 
          帮助识别 RTIP 数据包 
- PF_SIP
- 
          简单 Internet 协议 
- PF_SNA
- 
          IBM SNA 协议 
- PF_SYSTEM
- 
          内核事件消息 
- PF_TIPC
- 
          TIPC,“集群域套接字”协议 
- PF_UNIX
- 
          UNIX 套接字 
- PF_UNSPEC
- 
          未指定的协议,任何受支持的地址族 
- PF_VSOCK
- 
          用于虚拟机管理程序-访客通信的 VSOCK(最初为“VMWare VSockets”)协议 
- PF_XDP
- 
          XDP(快速数据路径)接口 
- PF_XTP
- 
          快速传输协议 
- RESOLUTION_DELAY
- SCM_BINTIME
- 
          时间戳(bintime) 
- SCM_CREDENTIALS
- 
          发送者的凭据 
- SCM_CREDS
- 
          进程凭据 
- SCM_RIGHTS
- 
          访问权限 
- SCM_TIMESTAMP
- 
          时间戳(timeval) 
- SCM_TIMESTAMPING
- 
          时间戳(timespec 列表)(Linux 2.6.30) 
- SCM_TIMESTAMPNS
- 
          时间规约(timespec) 
- SCM_UCRED
- 
          用户凭据 
- SCM_WIFI_STATUS
- 
          Wifi 状态(Linux 3.3) 
- SHUT_RD
- 
          关闭套接字的读取端 
- SHUT_RDWR
- 
          关闭套接字的两端 
- SHUT_WR
- 
          关闭套接字的写入端 
- SOCK_CLOEXEC
- 
          在新文件描述符上设置 close-on-exec (FD_CLOEXEC) 标志。 
- SOCK_DGRAM
- 
          数据报套接字提供无连接、不可靠的消息传递 
- SOCK_NONBLOCK
- 
          在新文件描述符引用的打开文件描述上设置 O_NONBLOCK 文件状态标志(参见 open(2))。 
- SOCK_PACKET
- 
          设备级数据包访问 
- SOCK_RAW
- 
          原始套接字提供底层访问,用于直接访问或实现网络协议 
- SOCK_RDM
- 
          可靠的数据报套接字提供可靠的消息传递 
- SOCK_SEQPACKET
- 
          顺序数据包套接字为数据报提供排序的、可靠的双向连接 
- SOCK_STREAM
- 
          流套接字为字节流提供排序的、可靠的双向连接 
- SOL_ATALK
- 
          AppleTalk 套接字选项 
- SOL_AX25
- 
          AX.25 套接字选项 
- SOL_IP
- 
          IP 套接字选项 
- SOL_IPX
- 
          IPX 套接字选项 
- SOL_SOCKET
- 
          套接字级选项 
- SOL_TCP
- 
          TCP 套接字选项 
- SOL_UDP
- 
          UDP 套接字选项 
- SOMAXCONN
- 
          可为套接字排队的最大连接请求数 
- SOPRI_BACKGROUND
- 
          后台套接字优先级 
- SOPRI_INTERACTIVE
- 
          交互式套接字优先级 
- SOPRI_NORMAL
- 
          正常套接字优先级 
- SO_ACCEPTCONN
- 
          套接字已调用 listen() 
- SO_ACCEPTFILTER
- 
          存在接受过滤器 
- SO_ALLZONES
- 
          绕过区域边界 
- SO_ATTACH_FILTER
- 
          附加接受过滤器 
- SO_BINDTODEVICE
- 
          仅从给定的接口发送数据包 
- SO_BINTIME
- 
          接收带有数据报的时间戳 (bintime) 
- SO_BPF_EXTENSIONS
- 
          查询支持的 BPF 扩展(Linux 3.14) 
- SO_BROADCAST
- 
          允许发送广播消息 
- SO_BUSY_POLL
- 
          设置低延迟轮询的阈值(以微秒为单位)(Linux 3.11) 
- SO_CONNECT_TIME
- 
          返回套接字已连接的秒数。此选项仅对面向连接的协议有效(Windows) 
- SO_DEBUG
- 
          调试信息记录 
- SO_DETACH_FILTER
- 
          分离接受过滤器 
- SO_DOMAIN
- 
          为 socket() 给定的域 (Linux 2.6.32) 
- SO_DONTROUTE
- 
          使用接口地址 
- SO_DONTTRUNC
- 
          保留未读数据 
- SO_ERROR
- 
          获取并清除错误状态 
- SO_GET_FILTER
- 
          获取由 SO_ATTACH_FILTER设置的过滤器(Linux 3.8)
- SO_INCOMING_CPU
- 
          接收附加到套接字的 CPU (Linux 3.19) 
- SO_INCOMING_NAPI_ID
- 
          接收附加到 RX 队列的 napi ID (Linux 4.12) 
- SO_KEEPALIVE
- 
          保持连接活动 
- SO_LINGER
- 
          如果存在数据,则在关闭时徘徊 
- SO_LOCK_FILTER
- 
          锁定附加到套接字的过滤器(Linux 3.9) 
- SO_MAC_EXEMPT
- 
          对未标记对等方的强制访问控制豁免 
- SO_MARK
- 
          为基于标记的路由设置标记 (Linux 2.6.25) 
- SO_MAX_PACING_RATE
- 
          限制传输层计算的速率。[每秒字节数] (Linux 3.13) 
- SO_NKE
- 
          安装套接字级网络内核扩展 
- SO_NOFCS
- 
          设置套接字的 netns (Linux 3.4) 
- SO_NOSIGPIPE
- 
          不要在 EPIPE 上发送 SIGPIPE 
- SO_NO_CHECK
- 
          禁用校验和 
- SO_NREAD
- 
          获取第一个数据包字节数 
- SO_OOBINLINE
- 
          将接收到的带外数据保留在内联 
- SO_PASSCRED
- 
          接收 SCM_CREDENTIALS消息
- SO_PASSSEC
- 
          切换安全上下文传递(Linux 2.6.18) 
- SO_PEEK_OFF
- 
          设置窥视偏移量 (Linux 3.4) 
- SO_PEERCRED
- 
          连接到此套接字的外来进程的凭据 
- SO_PEERNAME
- 
          连接用户的名称 
- SO_PEERSEC
- 
          获取安全凭据 (Linux 2.6.2) 
- SO_PRIORITY
- 
          此套接字上所有数据包的协议定义优先级 
- SO_PROTOCOL
- 
          为 socket() 给定的协议 (Linux 2.6.32) 
- SO_RCVBUF
- 
          接收缓冲区大小 
- SO_RCVBUFFORCE
- 
          接收缓冲区大小,不受 rmem_max 限制 (Linux 2.6.14) 
- SO_RCVLOWAT
- 
          接收低水位线 
- SO_RCVTIMEO
- 
          接收超时 
- SO_RECVUCRED
- 
          接收带有数据报的用户凭据 
- SO_REUSEADDR
- 
          允许本地地址重用 
- SO_REUSEPORT
- 
          允许本地地址和端口重用 
- SO_RTABLE
- 
          为此套接字设置路由表 (OpenBSD) 
- SO_RXQ_OVFL
- 
          切换丢弃的数据包数的 cmsg (Linux 2.6.33) 
- SO_SECURITY_AUTHENTICATION
- SO_SECURITY_ENCRYPTION_NETWORK
- SO_SECURITY_ENCRYPTION_TRANSPORT
- SO_SELECT_ERR_QUEUE
- 
          使 select() 检测带有 errorfds 的套接字错误队列 (Linux 3.10) 
- SO_SETFIB
- 
          为套接字设置关联的路由表 (FreeBSD) 
- SO_SNDBUF
- 
          发送缓冲区大小 
- SO_SNDBUFFORCE
- 
          发送缓冲区大小,不受 wmem_max 限制 (Linux 2.6.14) 
- SO_SNDLOWAT
- 
          发送低水位线 
- SO_SNDTIMEO
- 
          发送超时 
- SO_TIMESTAMP
- 
          接收带有数据报的时间戳(timeval) 
- SO_TIMESTAMPING
- 
          传入和传出数据包的时间戳 (Linux 2.6.30) 
- SO_TIMESTAMPNS
- 
          接收带有数据报的纳秒时间戳 (timespec) 
- SO_TYPE
- 
          获取套接字类型 
- SO_USELOOPBACK
- 
          尽可能绕过硬件 
- SO_USER_COOKIE
- 
          主要为 ipfw 目的设置一个标识符 
- SO_WANTMORE
- 
          当更多数据准备就绪时给出提示 
- SO_WANTOOBFLAG
- 
          在接收时,MSG_FLAG 中需要 OOB 数据 
- SO_WIFI_STATUS
- 
          切换 wifi 状态的 cmsg (Linux 3.3) 
- TCP_CONGESTION
- 
          TCP 拥塞控制算法(Linux 2.6.13,glibc 2.6) 
- TCP_CONNECTION_INFO
- 
          检索有关此套接字的信息 (macOS) 
- TCP_COOKIE_TRANSACTIONS
- 
          TCP Cookie 事务 (Linux 2.6.33, glibc 2.18) 
- TCP_CORK
- 
          不要发送部分帧(Linux 2.2,glibc 2.2) 
- TCP_DEFER_ACCEPT
- 
          在数据准备就绪之前不要通知侦听套接字 (Linux 2.4, glibc 2.2) 
- TCP_FASTOPEN
- 
          减少握手过程的步骤 (Linux 3.7, glibc 2.18) 
- TCP_INFO
- 
          检索有关此套接字的信息 (Linux 2.4, glibc 2.2) 
- TCP_KEEPALIVE
- 
          发送 keepalive 探测之前的空闲时间 (macOS) 
- TCP_KEEPCNT
- 
          在丢弃连接之前允许的最大 keepalive 探测次数 (Linux 2.4, glibc 2.2) 
- TCP_KEEPIDLE
- 
          发送 keepalive 探测之前的空闲时间 (Linux 2.4, glibc 2.2) 
- TCP_KEEPINTVL
- 
          keepalive 探测之间的时间 (Linux 2.4, glibc 2.2) 
- TCP_LINGER2
- 
          孤立的 FIN_WAIT2 套接字的生存期 (Linux 2.4, glibc 2.2) 
- TCP_MAXSEG
- 
          设置最大段大小 
- TCP_MD5SIG
- 
          使用 MD5 摘要(RFC2385,Linux 2.6.20,glibc 2.7) 
- TCP_NODELAY
- 
          不要延迟发送以合并数据包 
- TCP_NOOPT
- 
          不要使用 TCP 选项 
- TCP_NOPUSH
- 
          不要推送写入的最后一个块 
- TCP_QUEUE_SEQ
- 
          用于修复模式的队列顺序 (Linux 3.5, glibc 2.18) 
- TCP_QUICKACK
- 
          启用 quickack 模式 (Linux 2.4.4, glibc 2.3) 
- TCP_REPAIR
- 
          修复模式 (Linux 3.5, glibc 2.18) 
- TCP_REPAIR_OPTIONS
- 
          修复模式的选项 (Linux 3.5, glibc 2.18) 
- TCP_REPAIR_QUEUE
- 
          用于修复模式的队列 (Linux 3.5, glibc 2.18) 
- TCP_SYNCNT
- 
          在丢弃连接之前的 SYN 重传次数 (Linux 2.4, glibc 2.2) 
- TCP_THIN_DUPACK
- 
          用于细流的重复确认处理 (Linux 2.6.34, glibc 2.18) 
- TCP_THIN_LINEAR_TIMEOUTS
- 
          用于细流的线性超时 (Linux 2.6.34, glibc 2.18) 
- TCP_TIMESTAMP
- 
          TCP 时间戳 (Linux 3.9, glibc 2.18) 
- TCP_USER_TIMEOUT
- 
          TCP 连接中止之前的最大超时时间 (Linux 2.6.37, glibc 2.18) 
- TCP_WINDOW_CLAMP
- 
          钳制通告窗口的大小 (Linux 2.4, glibc 2.2) 
- UDP_CORK
- 
          不要发送部分帧 (Linux 2.5.44, glibc 2.11) 
公共类方法
来源
# File ext/socket/lib/socket.rb, line 1216 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 应该是 family 中定义的协议,并且对于 family 默认为 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() 可能需要很长时间,并且可能会阻塞其他线程。(由于 gethostbyname() 不是线程安全的,因此无法释放 GVL。) 
- 
此方法使用 gethostbyname() 函数,该函数已从 POSIX 中删除。 
此方法获取 address 的主机信息。
p Socket.gethostbyaddr([221,186,184,68].pack("CCCC"))
#=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"]
p Socket.gethostbyaddr([127,0,0,1].pack("CCCC"))
["localhost", [], 2, "\x7F\x00\x00\x01"]
p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16))
#=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10,
     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"]
        来源
static VALUE
sock_s_gethostbyname(VALUE obj, VALUE host)
{
    rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
    struct rb_addrinfo *res =
        rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME);
    return rsock_make_hostent(host, res, sock_sockaddr);
}
          改用 Addrinfo.getaddrinfo。此方法因以下原因而被弃用
- 
结果的第 3 个元素是第一个地址的地址族。其余地址的地址族未返回。 
- 
不常见的地址表示形式:4/16 字节的二进制字符串表示 IPv4/IPv6 地址。 
- 
gethostbyname() 可能需要很长时间,并且可能会阻塞其他线程。(由于 gethostbyname() 不是线程安全的,因此无法释放 GVL。) 
- 
此方法使用 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"
请注意,不保证能够使用 gethostbyname、getaddrinfo 等转换为 IP 地址。如果需要本地 IP 地址,请使用 Socket.ip_address_list。
来源
static VALUE
socket_s_getifaddrs(VALUE self)
{
    return rsock_getifaddrs();
}
          返回接口地址数组。数组的元素是 Socket::Ifaddr 的一个实例。
此方法可用于查找启用多播的接口
pp Socket.getifaddrs.reject {|ifaddr| !ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0) }.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] } #=> [["eth0", 2, #<Addrinfo: 221.186.184.67>], # ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
GNU/Linux 上的示例结果
pp Socket.getifaddrs #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
FreeBSD 上的示例结果
pp Socket.getifaddrs #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
来源
static VALUE
sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
{
    VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp;
    char hbuf[1024], pbuf[1024];
    int fl;
    struct rb_addrinfo *res = NULL;
    struct addrinfo hints, *r;
    int error, saved_errno;
    union_sockaddr ss;
    struct sockaddr *sap;
    socklen_t salen;
    sa = flags = Qnil;
    rb_scan_args(argc, argv, "11", &sa, &flags);
    fl = 0;
    if (!NIL_P(flags)) {
        fl = NUM2INT(flags);
    }
    tmp = rb_check_sockaddr_string_type(sa);
    if (!NIL_P(tmp)) {
        sa = tmp;
        if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) {
            rb_raise(rb_eTypeError, "sockaddr length too big");
        }
        memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa));
        if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) {
            rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
        }
        sap = &ss.addr;
        salen = RSTRING_SOCKLEN(sa);
        goto call_nameinfo;
    }
    tmp = rb_check_array_type(sa);
    if (!NIL_P(tmp)) {
        sa = tmp;
        MEMZERO(&hints, struct addrinfo, 1);
        if (RARRAY_LEN(sa) == 3) {
            af = RARRAY_AREF(sa, 0);
            port = RARRAY_AREF(sa, 1);
            host = RARRAY_AREF(sa, 2);
        }
        else if (RARRAY_LEN(sa) >= 4) {
            af = RARRAY_AREF(sa, 0);
            port = RARRAY_AREF(sa, 1);
            host = RARRAY_AREF(sa, 3);
            if (NIL_P(host)) {
                host = RARRAY_AREF(sa, 2);
            }
            else {
                /*
                 * 4th element holds numeric form, don't resolve.
                 * see rsock_ipaddr().
                 */
#ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */
                hints.ai_flags |= AI_NUMERICHOST;
#endif
            }
        }
        else {
            rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given",
                     RARRAY_LEN(sa));
        }
        hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
        /* af */
        hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
        res = rsock_getaddrinfo(host, port, &hints, 0);
        sap = res->ai->ai_addr;
        salen = res->ai->ai_addrlen;
    }
    else {
        rb_raise(rb_eTypeError, "expecting String or Array");
    }
  call_nameinfo:
    error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf),
                           pbuf, sizeof(pbuf), fl);
    if (error) goto error_exit_name;
    if (res) {
        for (r = res->ai->ai_next; r; r = r->ai_next) {
            char hbuf2[1024], pbuf2[1024];
            sap = r->ai_addr;
            salen = r->ai_addrlen;
            error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2),
                                   pbuf2, sizeof(pbuf2), fl);
            if (error) goto error_exit_name;
            if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) {
                rb_freeaddrinfo(res);
                rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename");
            }
        }
        rb_freeaddrinfo(res);
    }
    return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
  error_exit_name:
    saved_errno = errno;
    if (res) rb_freeaddrinfo(res);
    errno = saved_errno;
    rsock_raise_resolution_error("getnameinfo", error);
    UNREACHABLE_RETURN(Qnil);
}
          获取 sockaddr 的名称信息。
sockaddr 应该是以下之一:
- 
打包的 sockaddr 字符串,例如 Socket.sockaddr_in(80, “127.0.0.1”)
- 
包含 3 个元素的数组,例如 [“AF_INET”, 80, “127.0.0.1”] 
- 
包含 4 个元素的数组,例如 [“AF_INET”, 80, ignored, “127.0.0.1”] 
flags 应该是 Socket::NI_* 常量的按位或。
注意:最后一种形式与 IPSocket#addr 和 IPSocket#peeraddr 兼容。
Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"]) #=> ["localhost", "www"] Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"]
如果首选 Addrinfo 对象,请使用 Addrinfo#getnameinfo。
来源
static VALUE
sock_s_getservbyname(int argc, VALUE *argv, VALUE _)
{
    VALUE service, proto;
    struct servent *sp;
    long port;
    const char *servicename, *protoname = "tcp";
    rb_scan_args(argc, argv, "11", &service, &proto);
    StringValue(service);
    if (!NIL_P(proto)) StringValue(proto);
    servicename = StringValueCStr(service);
    if (!NIL_P(proto)) protoname = StringValueCStr(proto);
    sp = getservbyname(servicename, protoname);
    if (sp) {
        port = ntohs(sp->s_port);
    }
    else {
        char *end;
        port = STRTOUL(servicename, &end, 0);
        if (*end != '\0') {
            rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname);
        }
    }
    return INT2FIX(port);
}
          获取 service_name 的端口号。
如果未给定 protocol_name,则假定为 “tcp”。
Socket.getservbyname("smtp") #=> 25 Socket.getservbyname("shell") #=> 514 Socket.getservbyname("syslog", "udp") #=> 514
来源
static VALUE
sock_s_getservbyport(int argc, VALUE *argv, VALUE _)
{
    VALUE port, proto;
    struct servent *sp;
    long portnum;
    const char *protoname = "tcp";
    rb_scan_args(argc, argv, "11", &port, &proto);
    portnum = NUM2LONG(port);
    if (portnum != (uint16_t)portnum) {
        const char *s = portnum > 0 ? "big" : "small";
        rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s);
    }
    if (!NIL_P(proto)) protoname = StringValueCStr(proto);
    sp = getservbyport((int)htons((uint16_t)portnum), protoname);
    if (!sp) {
        rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname);
    }
    return rb_str_new2(sp->s_name);
}
          获取 port 的服务名称。
如果未给定 protocol_name,则假定为 “tcp”。
Socket.getservbyport(80) #=> "www" Socket.getservbyport(514, "tcp") #=> "shell" Socket.getservbyport(514, "udp") #=> "syslog"
来源
static VALUE
socket_s_ip_address_list(VALUE self)
{
#if defined(HAVE_GETIFADDRS)
    struct ifaddrs *ifp = NULL;
    struct ifaddrs *p;
    int ret;
    VALUE list;
    ret = getifaddrs(&ifp);
    if (ret == -1) {
        rb_sys_fail("getifaddrs");
    }
    list = rb_ary_new();
    for (p = ifp; p; p = p->ifa_next) {
        if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) {
            struct sockaddr *addr = p->ifa_addr;
#if defined(AF_INET6) && defined(__sun)
            /*
             * OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local
             * address with sin6_scope_id == 0.
             * So fill it from the interface name (ifa_name).
             */
            struct sockaddr_in6 addr6;
            if (addr->sa_family == AF_INET6) {
                socklen_t len = (socklen_t)sizeof(struct sockaddr_in6);
                memcpy(&addr6, addr, len);
                addr = (struct sockaddr *)&addr6;
                if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) &&
                    addr6.sin6_scope_id == 0) {
                    unsigned int ifindex = if_nametoindex(p->ifa_name);
                    if (ifindex != 0) {
                        addr6.sin6_scope_id = ifindex;
                    }
                }
            }
#endif
            rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
        }
    }
    freeifaddrs(ifp);
    return list;
#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
    /* Solaris if_tcp(7P) */
    int fd = -1;
    int ret;
    struct lifnum ln;
    struct lifconf lc;
    const char *reason = NULL;
    int save_errno;
    int i;
    VALUE list = Qnil;
    lc.lifc_buf = NULL;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1)
        rb_sys_fail("socket(2)");
    memset(&ln, 0, sizeof(ln));
    ln.lifn_family = AF_UNSPEC;
    ret = ioctl(fd, SIOCGLIFNUM, &ln);
    if (ret == -1) {
        reason = "SIOCGLIFNUM";
        goto finish;
    }
    memset(&lc, 0, sizeof(lc));
    lc.lifc_family = AF_UNSPEC;
    lc.lifc_flags = 0;
    lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
    lc.lifc_req = xmalloc(lc.lifc_len);
    ret = ioctl(fd, SIOCGLIFCONF, &lc);
    if (ret == -1) {
        reason = "SIOCGLIFCONF";
        goto finish;
    }
    list = rb_ary_new();
    for (i = 0; i < ln.lifn_count; i++) {
        struct lifreq *req = &lc.lifc_req[i];
        if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
            if (req->lifr_addr.ss_family == AF_INET6 &&
                IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) &&
                ((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id == 0) {
                struct lifreq req2;
                memcpy(req2.lifr_name, req->lifr_name, LIFNAMSIZ);
                ret = ioctl(fd, SIOCGLIFINDEX, &req2);
                if (ret == -1) {
                    reason = "SIOCGLIFINDEX";
                    goto finish;
                }
                ((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index;
            }
            rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen));
        }
    }
  finish:
    save_errno = errno;
    xfree(lc.lifc_req);
    if (fd != -1)
        close(fd);
    errno = save_errno;
    if (reason)
        rb_syserr_fail(save_errno, reason);
    return list;
#elif defined(SIOCGIFCONF)
    int fd = -1;
    int ret;
#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr)))
    char initbuf[4096+EXTRA_SPACE];
    char *buf = initbuf;
    int bufsize;
    struct ifconf conf;
    struct ifreq *req;
    VALUE list = Qnil;
    const char *reason = NULL;
    int save_errno;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1)
        rb_sys_fail("socket(2)");
    bufsize = sizeof(initbuf);
    buf = initbuf;
  retry:
    conf.ifc_len = bufsize;
    conf.ifc_req = (struct ifreq *)buf;
    /* fprintf(stderr, "bufsize: %d\n", bufsize); */
    ret = ioctl(fd, SIOCGIFCONF, &conf);
    if (ret == -1) {
        reason = "SIOCGIFCONF";
        goto finish;
    }
    /* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
    if (bufsize - EXTRA_SPACE < conf.ifc_len) {
        if (bufsize < conf.ifc_len) {
            /* NetBSD returns required size for all interfaces. */
            bufsize = conf.ifc_len + EXTRA_SPACE;
        }
        else {
            bufsize = bufsize << 1;
        }
        if (buf == initbuf)
            buf = NULL;
        buf = xrealloc(buf, bufsize);
        goto retry;
    }
    close(fd);
    fd = -1;
    list = rb_ary_new();
    req = conf.ifc_req;
    while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
        struct sockaddr *addr = &req->ifr_addr;
        if (IS_IP_FAMILY(addr->sa_family)) {
            rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr)));
        }
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
# ifndef _SIZEOF_ADDR_IFREQ
#  define _SIZEOF_ADDR_IFREQ(r) \
          (sizeof(struct ifreq) + \
           (sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
            (r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
            0))
# endif
        req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
#else
        req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
#endif
    }
  finish:
    save_errno = errno;
    if (buf != initbuf)
        xfree(buf);
    if (fd != -1)
        close(fd);
    errno = save_errno;
    if (reason)
        rb_syserr_fail(save_errno, reason);
    return list;
#undef EXTRA_SPACE
#elif defined(_WIN32)
    typedef struct ip_adapter_unicast_address_st {
        unsigned LONG_LONG dummy0;
        struct ip_adapter_unicast_address_st *Next;
        struct {
            struct sockaddr *lpSockaddr;
            int iSockaddrLength;
        } Address;
        int dummy1;
        int dummy2;
        int dummy3;
        long dummy4;
        long dummy5;
        long dummy6;
    } ip_adapter_unicast_address_t;
    typedef struct ip_adapter_anycast_address_st {
        unsigned LONG_LONG dummy0;
        struct ip_adapter_anycast_address_st *Next;
        struct {
            struct sockaddr *lpSockaddr;
            int iSockaddrLength;
        } Address;
    } ip_adapter_anycast_address_t;
    typedef struct ip_adapter_addresses_st {
        unsigned LONG_LONG dummy0;
        struct ip_adapter_addresses_st *Next;
        void *dummy1;
        ip_adapter_unicast_address_t *FirstUnicastAddress;
        ip_adapter_anycast_address_t *FirstAnycastAddress;
        void *dummy2;
        void *dummy3;
        void *dummy4;
        void *dummy5;
        void *dummy6;
        BYTE dummy7[8];
        DWORD dummy8;
        DWORD dummy9;
        DWORD dummy10;
        DWORD IfType;
        int OperStatus;
        DWORD dummy12;
        DWORD dummy13[16];
        void *dummy14;
    } ip_adapter_addresses_t;
    typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG);
    HMODULE h;
    GetAdaptersAddresses_t pGetAdaptersAddresses;
    ULONG len;
    DWORD ret;
    ip_adapter_addresses_t *adapters;
    VALUE list;
    h = LoadLibrary("iphlpapi.dll");
    if (!h)
        rb_notimplement();
    pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses");
    if (!pGetAdaptersAddresses) {
        FreeLibrary(h);
        rb_notimplement();
    }
    ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
    if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) {
        errno = rb_w32_map_errno(ret);
        FreeLibrary(h);
        rb_sys_fail("GetAdaptersAddresses");
    }
    adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len);
    ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len);
    if (ret != ERROR_SUCCESS) {
        errno = rb_w32_map_errno(ret);
        FreeLibrary(h);
        rb_sys_fail("GetAdaptersAddresses");
    }
    list = rb_ary_new();
    for (; adapters; adapters = adapters->Next) {
        ip_adapter_unicast_address_t *uni;
        ip_adapter_anycast_address_t *any;
        if (adapters->OperStatus != 1)  /* 1 means IfOperStatusUp */
            continue;
        for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) {
#ifndef INET6
            if (uni->Address.lpSockaddr->sa_family == AF_INET)
#else
            if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family))
#endif
                rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength));
        }
        for (any = adapters->FirstAnycastAddress; any; any = any->Next) {
#ifndef INET6
            if (any->Address.lpSockaddr->sa_family == AF_INET)
#else
            if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family))
#endif
                rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength));
        }
    }
    FreeLibrary(h);
    return list;
#endif
}
          以数组形式返回本地 IP 地址。
该数组包含 Addrinfo 对象。
pp Socket.ip_address_list
#=> [#<Addrinfo: 127.0.0.1>,
     #<Addrinfo: 192.168.0.128>,
     #<Addrinfo: ::1>,
     ...]
        来源
static VALUE
sock_initialize(int argc, VALUE *argv, VALUE sock)
{
    VALUE domain, type, protocol;
    int fd;
    int d, t;
    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);
    setup_domain_and_type(domain, &d, type, &t);
    fd = rsock_socket(d, t, NUM2INT(protocol));
    if (fd < 0) rb_sys_fail("socket(2)");
    return rsock_init_sock(sock, fd);
}
          创建一个新的套接字对象。
domain 应该是一个通信域,例如::INET、:INET6、:UNIX 等。
socktype 应该是套接字类型,例如::STREAM、:DGRAM、:RAW 等。
protocol 是可选的,应该是在域中定义的协议。如果未给定协议,则内部使用 0。
Socket.new(:INET, :STREAM) # TCP socket Socket.new(:INET, :DGRAM) # UDP socket Socket.new(:UNIX, :STREAM) # UNIX stream socket Socket.new(:UNIX, :DGRAM) # UNIX datagram socket
来源
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
    struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
    VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
    rb_freeaddrinfo(res);
    return addr;
}
          将 port 和 host 打包为 AF_INET/AF_INET6 sockaddr 字符串。
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
来源
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
    struct sockaddr_un sockaddr;
    VALUE addr;
    StringValue(path);
    INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
    if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
        rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
            (size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
    }
    memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
    addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
    return addr;
}
          将 path 打包为 AF_UNIX sockaddr 字符串。
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
来源
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
    VALUE domain, type, protocol;
    int d, t, p, sp[2];
    int ret;
    VALUE s1, s2, r;
    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);
    setup_domain_and_type(domain, &d, type, &t);
    p = NUM2INT(protocol);
    ret = rsock_socketpair(d, t, p, sp);
    if (ret < 0) {
        rb_sys_fail("socketpair(2)");
    }
    s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
    s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
    r = rb_assoc_new(s1, s2);
    if (rb_block_given_p()) {
        return rb_ensure(pair_yield, r, io_close, s1);
    }
    return r;
}
          创建一对相互连接的套接字。
domain 应该是一个通信域,例如::INET、:INET6、:UNIX 等。
socktype 应该是套接字类型,例如::STREAM、:DGRAM、:RAW 等。
protocol 应该是在域中定义的协议,默认为该域的 0。
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
来源
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 打包为 AF_INET/AF_INET6 sockaddr 字符串。
Socket.sockaddr_in(80, "127.0.0.1") #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" Socket.sockaddr_in(80, "::1") #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
来源
static VALUE
sock_s_pack_sockaddr_un(VALUE self, VALUE path)
{
    struct sockaddr_un sockaddr;
    VALUE addr;
    StringValue(path);
    INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un));
    if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) {
        rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)",
            (size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path));
    }
    memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
    addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path));
    return addr;
}
          将 path 打包为 AF_UNIX sockaddr 字符串。
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..."
来源
VALUE
rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
{
    VALUE domain, type, protocol;
    int d, t, p, sp[2];
    int ret;
    VALUE s1, s2, r;
    rb_scan_args(argc, argv, "21", &domain, &type, &protocol);
    if (NIL_P(protocol))
        protocol = INT2FIX(0);
    setup_domain_and_type(domain, &d, type, &t);
    p = NUM2INT(protocol);
    ret = rsock_socketpair(d, t, p, sp);
    if (ret < 0) {
        rb_sys_fail("socketpair(2)");
    }
    s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
    s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
    r = rb_assoc_new(s1, s2);
    if (rb_block_given_p()) {
        return rb_ensure(pair_yield, r, io_close, s1);
    }
    return r;
}
          创建一对相互连接的套接字。
domain 应该是一个通信域,例如::INET、:INET6、:UNIX 等。
socktype 应该是套接字类型,例如::STREAM、:DGRAM、:RAW 等。
protocol 应该是在域中定义的协议,默认为该域的 0。
s1, s2 = Socket.pair(:UNIX, :STREAM, 0) s1.send "a", 0 s1.send "b", 0 s1.close p s2.recv(10) #=> "ab" p s2.recv(10) #=> "" p s2.recv(10) #=> "" s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) s1.send "a", 0 s1.send "b", 0 p s2.recv(10) #=> "a" p s2.recv(10) #=> "b"
来源
# File ext/socket/lib/socket.rb, line 659 def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket sock = if fast_fallback && !(host && ip_address?(host)) tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:) else tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:) end if block_given? begin yield sock ensure sock.close end else sock end end
创建一个新的套接字对象,该对象使用 TCP/IP 连接到 host:port。
从 Ruby 3.4 开始,此方法默认按照 Happy Eyeballs Version 2(RFC 8305)算法运行。
有关 Happy Eyeballs Version 2 的详细信息,请参阅 Socket.tcp_fast_fallback=。
要使其行为与 Ruby 3.3 及更早版本相同,请显式指定选项 fast_fallback:false。或者,设置 Socket.tcp_fast_fallback=false 将禁用 Happy Eyeballs Version 2,不仅对此方法,而且对所有 Socket 全局禁用。
如果给定 local_host:local_port,则该套接字将绑定到它。
可选的最后一个参数 opts 是由哈希表示的选项。opts 可能具有以下选项:
- :resolv_timeout
- 
指定从主机名解析开始的超时时间(以秒为单位)。 
- :connect_timeout
- 
此方法会依次尝试连接到所有候选目标地址。 connect_timeout指定从连接尝试开始到最后一个候选的超时时间(以秒为单位)。
 默认情况下,所有连接尝试都会持续到超时发生。
 当显式指定fast_fallback:false时,
 将为每个连接尝试设置超时时间,并且任何超过其超时时间的连接尝试都将被取消。
- :fast_fallback
- 
启用 Happy Eyeballs Version 2 算法(默认启用)。 
如果给定了代码块,则将使用该套接字调用该代码块。返回该代码块的值。当此方法返回时,该套接字将关闭。
如果没有给出代码块,则返回该套接字。
Socket.tcp("www.ruby-lang.org", 80) {|sock| sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" sock.close_write puts sock.read }
来源
VALUE socket_s_tcp_fast_fallback(VALUE self) {
    return rb_ivar_get(rb_cSocket, tcp_fast_fallback);
}
          返回是否启用了 Happy Eyeballs Version 2(RFC 8305),该算法从 Ruby 3.4 开始在使用 TCPSocket.new 和 Socket.tcp 时提供。
如果为 true,则为 TCPSocket.new 和 Socket.tcp 启用它。(注意:在 Windows 上使用 TCPSocket.new 时不提供 Happy Eyeballs Version 2。)
如果为 false,则禁用 Happy Eyeballs Version 2。
有关 Happy Eyeballs Version 2 的详细信息,请参阅 Socket.tcp_fast_fallback=。
来源
VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) {
    rb_ivar_set(rb_cSocket, tcp_fast_fallback, value);
    return value;
}
          全局启用或禁用 Happy Eyeballs Version 2(RFC 8305),该算法从 Ruby 3.4 开始在使用 TCPSocket.new 和 Socket.tcp 时提供。
当设置为 true 时,该功能将为 'TCPSocket.new' 和 'Socket.tcp' 启用。(注意:在 Windows 上使用 TCPSocket.new 时,此功能不可用。)
当设置为 false 时,行为将恢复为 Ruby 3.3 或更早版本的行为。
如果未通过调用此方法显式设置任何值,则默认值为 true。但是,当设置环境变量 RUBY_TCP_NO_FAST_FALLBACK=1 时,默认值为 false。
要控制每个方法的设置,请为每个方法使用 fast_fallback 关键字参数。
Happy Eyeballs Version 2¶ ↑
Happy Eyeballs Version 2(RFC 8305)是一种旨在改善客户端套接字连接的算法。
它旨在通过并行(而不是串行)执行主机名解析和连接尝试来实现更可靠和高效的连接。
从 Ruby 3.4 开始,此方法使用此算法按以下方式运行:
- 
同时开始解析 IPv6 和 IPv4 地址。 
- 
开始连接到首先获得的地址之一。 
 如果首先获得 IPv4 地址,则该方法将等待 50 毫秒以进行 IPv6 名称解析,从而优先考虑 IPv6 连接。
- 
在启动连接尝试后,等待 250 毫秒以建立连接。 
 如果在此时间内未建立任何连接,则每 250 毫秒启动一个新连接
 直到建立连接或没有更多候选地址为止。
 (尽管 RFC 8305 严格规定了地址排序,
 但出于性能考虑,此方法仅在 IPv6/IPv4 地址之间交替)
- 
一旦建立连接,所有剩余的连接尝试都将被取消。 
来源
# File ext/socket/lib/socket.rb, line 1272 def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo tcp_server_sockets(host, port) {|sockets| accept_loop(sockets, &b) } end
在 port 上创建一个 TCP/IP 服务器,并为接受的每个连接调用该代码块。使用套接字和客户端地址(作为 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 1172 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
为 host 和 port 创建 TCP/IP 服务器套接字。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 677 def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) if local_host || local_port local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, timeout: resolv_timeout) resolving_family_names = local_addrinfos.map { |lai| ADDRESS_FAMILIES.key(lai.afamily) }.uniq else local_addrinfos = [] resolving_family_names = ADDRESS_FAMILIES.keys end hostname_resolution_threads = [] resolution_store = HostnameResolutionStore.new(resolving_family_names) connecting_sockets = {} is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) now = current_clock_time resolution_delay_expires_at = nil connection_attempt_delay_expires_at = nil user_specified_connect_timeout_at = nil last_error = nil if resolving_family_names.size == 1 family_name = resolving_family_names.first addrinfos = Addrinfo.getaddrinfo(host, port, family_name, :STREAM, timeout: resolv_timeout) resolution_store.add_resolved(family_name, addrinfos) hostname_resolution_result = nil hostname_resolution_notifier = nil user_specified_resolv_timeout_at = nil else hostname_resolution_result = HostnameResolutionResult.new(resolving_family_names.size) hostname_resolution_notifier = hostname_resolution_result.notifier hostname_resolution_threads.concat( resolving_family_names.map { |family| thread_args = [family, host, port, hostname_resolution_result] thread = Thread.new(*thread_args) { |*thread_args| resolve_hostname(*thread_args) } Thread.pass thread } ) user_specified_resolv_timeout_at = resolv_timeout ? now + resolv_timeout : Float::INFINITY end loop do if resolution_store.any_addrinfos? && !resolution_delay_expires_at && !connection_attempt_delay_expires_at while (addrinfo = resolution_store.get_addrinfo) if local_addrinfos.any? local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily } if local_addrinfo.nil? # Connecting addrinfoと同じアドレスファミリのLocal addrinfoがない if resolution_store.any_addrinfos? # Try other Addrinfo in next "while" next elsif connecting_sockets.any? || resolution_store.any_unresolved_family? # Exit this "while" and wait for connections to be established or hostname resolution in next loop # Or exit this "while" and wait for hostname resolution in next loop break else raise SocketError.new 'no appropriate local address' end end end begin if resolution_store.any_addrinfos? || connecting_sockets.any? || resolution_store.any_unresolved_family? socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) socket.bind(local_addrinfo) if local_addrinfo result = socket.connect_nonblock(addrinfo, exception: false) else result = socket = local_addrinfo ? addrinfo.connect_from(local_addrinfo, timeout: connect_timeout) : addrinfo.connect(timeout: connect_timeout) end if result == :wait_writable connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY if resolution_store.empty_addrinfos? user_specified_connect_timeout_at = connect_timeout ? now + connect_timeout : Float::INFINITY end connecting_sockets[socket] = addrinfo break else return socket # connection established end rescue SystemCallError => e socket&.close last_error = e if resolution_store.any_addrinfos? # Try other Addrinfo in next "while" next elsif connecting_sockets.any? || resolution_store.any_unresolved_family? # Exit this "while" and wait for connections to be established or hostname resolution in next loop # Or exit this "while" and wait for hostname resolution in next loop break else raise last_error end end end end ends_at = if resolution_store.any_addrinfos? resolution_delay_expires_at || connection_attempt_delay_expires_at else [user_specified_resolv_timeout_at, user_specified_connect_timeout_at].compact.max end hostname_resolved, writable_sockets, except_sockets = IO.select( hostname_resolution_notifier, connecting_sockets.keys, # Use errorfds to wait for non-blocking connect failures on Windows is_windows_environment ? connecting_sockets.keys : nil, second_to_timeout(current_clock_time, ends_at), ) now = current_clock_time resolution_delay_expires_at = nil if expired?(now, resolution_delay_expires_at) connection_attempt_delay_expires_at = nil if expired?(now, connection_attempt_delay_expires_at) if writable_sockets&.any? while (writable_socket = writable_sockets.pop) is_connected = is_windows_environment || ( sockopt = writable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) sockopt.int.zero? ) if is_connected connecting_sockets.delete writable_socket return writable_socket else failed_ai = connecting_sockets.delete writable_socket writable_socket.close ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) if writable_sockets.any? || connecting_sockets.any? # Try other writable socket in next "while" # Or exit this "while" and wait for connections to be established or hostname resolution in next loop elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? # Exit this "while" and try other connection attempt # Or exit this "while" and wait for hostname resolution in next loop connection_attempt_delay_expires_at = nil user_specified_connect_timeout_at = nil else raise last_error end end end end if except_sockets&.any? except_sockets.each do |except_socket| failed_ai = connecting_sockets.delete except_socket sockopt = except_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_CONNECT_TIME) except_socket.close ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) if except_sockets.any? || connecting_sockets.any? # Cleanup other except socket in next "each" # Or exit this "while" and wait for connections to be established or hostname resolution in next loop elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? # Exit this "while" and try other connection attempt # Or exit this "while" and wait for hostname resolution in next loop connection_attempt_delay_expires_at = nil user_specified_connect_timeout_at = nil else raise last_error end end end if hostname_resolved&.any? while (family_and_result = hostname_resolution_result.get) family_name, result = family_and_result if result.is_a? Exception resolution_store.add_error(family_name, result) unless (Socket.const_defined?(:EAI_ADDRFAMILY)) && (result.is_a?(Socket::ResolutionError)) && (result.error_code == Socket::EAI_ADDRFAMILY) last_error = result end else resolution_store.add_resolved(family_name, result) end end if resolution_store.resolved?(:ipv4) if resolution_store.resolved?(:ipv6) hostname_resolution_notifier = nil resolution_delay_expires_at = nil user_specified_resolv_timeout_at = nil elsif resolution_store.resolved_successfully?(:ipv4) resolution_delay_expires_at = now + RESOLUTION_DELAY end end end if resolution_store.empty_addrinfos? if connecting_sockets.empty? && resolution_store.resolved_all_families? raise last_error end if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) && (expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?) raise Errno::ETIMEDOUT, 'user specified timeout' end end end ensure hostname_resolution_threads.each do |thread| thread.exit end hostname_resolution_result&.close connecting_sockets.each_key do |connecting_socket| connecting_socket.close end end
来源
# File ext/socket/lib/socket.rb, line 1442 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
在 port 上创建一个 UDP/IP 服务器,并为每个到达的消息调用该代码块。使用消息及其源信息调用该代码块。
此方法在内部使用 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 1415 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 1388 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
从给定的 sockets 接收 UDP/IP 数据包。对于接收到的每个数据包,都会调用该代码块。
该代码块接收 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 1300 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 服务器创建 UDP/IP 套接字。
如果没有给出代码块,则返回一个套接字数组。
如果给出了代码块,则使用套接字调用该代码块。返回该代码块的值。当此方法返回时,这些套接字将关闭。
如果 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 1492 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 1579 def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo unix_server_socket(path) {|serv| accept_loop(serv, &b) } end
在 path 上创建一个 UNIX 套接字服务器。它为接受的每个套接字调用该代码块。
如果指定了 host,则将其与 port 一起使用以确定服务器端口。
当代码块返回时,套接字 不会 关闭。因此,应用程序应关闭它。
如果该文件是套接字文件并且归应用程序用户所有,则此方法会首先删除 path 指向的套接字文件。只有当 path 的目录没有被恶意用户更改时,这是安全的。因此,请不要使用 /tmp/malicious-users-directory/socket。请注意,假设 /tmp 具有粘性位,则 /tmp/socket 和 /tmp/your-private-directory/socket 是安全的。
# 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 1522 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
在 path 上创建一个 UNIX 服务器套接字。
如果没有给出代码块,则返回一个侦听套接字。
如果给出了代码块,则使用套接字调用该代码块,并返回代码块值。当代码块退出时,套接字将关闭,并且套接字文件将被删除。
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 地址。
sockaddr 应该是 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 应该是一个字符串或者一个用于 AF_UNIX 的 addrinfo。
sockaddr = Socket.sockaddr_un("/tmp/sock") p Socket.unpack_sockaddr_un(sockaddr) #=> "/tmp/sock"
私有类方法
来源
# File ext/socket/lib/socket.rb, line 960 def self.current_clock_time Process.clock_gettime(Process::CLOCK_MONOTONIC) end
来源
# File ext/socket/lib/socket.rb, line 973 def self.expired?(started_at, ends_at) second_to_timeout(started_at, ends_at)&.zero? end
来源
# File ext/socket/lib/socket.rb, line 945 def self.ip_address?(hostname) hostname.match?(IPV6_ADRESS_FORMAT) || hostname.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/) end
来源
# File ext/socket/lib/socket.rb, line 950 def self.resolve_hostname(family, host, port, hostname_resolution_result) begin resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_result.add(family, resolved_addrinfos) rescue => e hostname_resolution_result.add(family, e) end end
来源
# File ext/socket/lib/socket.rb, line 965 def self.second_to_timeout(started_at, ends_at) return nil if ends_at == Float::INFINITY || ends_at.nil? remaining = (ends_at - started_at) remaining.negative? ? 0 : remaining end
来源
# File ext/socket/lib/socket.rb, line 906 def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:) last_error = nil ret = nil local_addr_list = nil if local_host != nil || local_port != nil local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil) end Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai| if local_addr_list local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily } next unless local_addr else local_addr = nil end begin sock = local_addr ? ai.connect_from(local_addr, timeout: connect_timeout) : ai.connect(timeout: connect_timeout) rescue SystemCallError last_error = $! next end ret = sock break } unless ret if last_error raise last_error else raise SocketError, "no appropriate local address" end end ret end
来源
# File ext/socket/lib/socket.rb, line 1550 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
公共实例方法
来源
static VALUE
sock_accept(VALUE server)
{
    union_sockaddr buffer;
    socklen_t length = (socklen_t)sizeof(buffer);
    VALUE peer = rsock_s_accept(rb_cSocket, server, &buffer.addr, &length);
    return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length));
}
          接受下一个连接。返回一个新的 Socket 对象和 Addrinfo 对象。
serv = Socket.new(:INET, :STREAM, 0) serv.listen(5) c = Socket.new(:INET, :STREAM, 0) c.connect(serv.connect_address) p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>]
来源
# File ext/socket/lib/socket.rb, line 598 def accept_nonblock(exception: true) __accept_nonblock(exception) end
在为底层文件描述符设置 O_NONBLOCK 后,使用 accept(2) 接受传入的连接。它返回一个数组,其中包含传入连接的已接受套接字 client_socket,以及一个 Addrinfo 对象 client_addrinfo。
示例¶ ↑
# In one script, start this first require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(2200, 'localhost') socket.bind(sockaddr) socket.listen(5) begin # emulate blocking accept client_socket, client_addrinfo = socket.accept_nonblock rescue IO::WaitReadable, Errno::EINTR IO.select([socket]) retry end puts "The client said, '#{client_socket.readline.chomp}'" client_socket.puts "Hello from script one!" socket.close # In another script, start this second require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(2200, 'localhost') socket.connect(sockaddr) socket.puts "Hello from script 2." puts "The server said, '#{socket.readline.chomp}'" socket.close
有关调用 accept_nonblock 失败时可能抛出的异常,请参阅 Socket#accept。
Socket#accept_nonblock 可能会引发任何与 accept(2) 失败相对应的错误,包括 Errno::EWOULDBLOCK。
如果异常是 Errno::EWOULDBLOCK、Errno::EAGAIN、Errno::ECONNABORTED 或 Errno::EPROTO,它将被 IO::WaitReadable 扩展。因此,可以使用 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- 包含在字符串或Addrinfo对象中的structsockaddr
示例¶ ↑
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 - 发生 i/o 错误 
- 
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参数不引用套接字
参见¶ ↑
- 
基于 Unix 系统的 bind 手册页 
- 
Microsoft 的 Winsock 函数参考中的 bind 函数 
来源
static VALUE
sock_connect(VALUE self, VALUE addr)
{
    VALUE rai;
    SockAddrStringValueWithAddrinfo(addr, rai);
    addr = rb_str_new4(addr);
    int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT);
    if (result < 0) {
        rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
    }
    return INT2FIX(result);
}
          请求在给定的 remote_sockaddr 上建立连接。如果成功则返回 0,否则会引发异常。
参数¶ ↑
- 
remote_sockaddr- 包含在字符串或Addrinfo对象中的structsockaddr
示例:¶ ↑
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) socket.connect( sockaddr ) socket.write( "GET / HTTP/1.0\r\n\r\n" ) results = socket.read
基于 Unix 的异常¶ ↑
在基于 Unix 的系统中,如果调用 connect 失败,可能会引发以下系统异常
- 
Errno::EACCES - 拒绝前缀路径的组件的搜索权限,或者拒绝写入 socket的权限
- 
Errno::EADDRINUSE - sockaddr 已在使用中 
- 
Errno::EADDRNOTAVAIL - 指定的 sockaddr 无法从本地计算机获得 
- 
Errno::EAFNOSUPPORT - 指定的 sockaddr 对于指定 socket的地址系列来说不是有效的地址
- 
Errno::EALREADY - 指定的套接字的连接已在进行中 
- 
Errno::EBADF - socket不是有效的文件描述符
- 
Errno::ECONNREFUSED - 目标 sockaddr 没有监听连接,拒绝了连接请求 
- 
Errno::ECONNRESET - 远程主机重置了连接请求 
- 
Errno::EFAULT - 无法访问 sockaddr 
- 
Errno::EHOSTUNREACH - 无法到达目标主机(可能是因为主机已关闭或远程路由器无法到达它) 
- 
Errno::EINPROGRESS - 为 socket设置了 O_NONBLOCK,并且无法立即建立连接;连接将异步建立
- 
Errno::EINTR - 尝试建立连接被捕获到的信号传递中断;连接将异步建立 
- 
Errno::EISCONN - 指定的 socket已连接
- 
Errno::EINVAL - 用于 sockaddr 的地址长度对于地址系列来说不是有效的长度,或者 sockaddr 中存在无效的系列 
- 
Errno::ENAMETOOLONG - 解析的路径名长度超过了 PATH_MAX 
- 
Errno::ENETDOWN - 用于到达目标的本地接口已关闭 
- 
Errno::ENETUNREACH - 不存在到达网络的路由 
- 
Errno::ENOBUFS - 没有可用的缓冲区空间 
- 
Errno::ENOSR - 没有足够的 STREAMS 资源可用来完成操作 
- 
Errno::ENOTSOCK - socket参数不引用套接字
- 
Errno::EOPNOTSUPP - 调用 socket正在监听,无法连接
- 
Errno::EPROTOTYPE - sockaddr 的类型与绑定到指定对等地址的套接字类型不同 
- 
Errno::ETIMEDOUT - 尝试连接在建立连接之前超时。 
在基于 Unix 的系统中,如果调用 socket 的地址系列是 AF_UNIX,那么如果调用 connect 失败,可能会引发以下异常
- 
Errno::EIO - 在读取或写入文件系统时发生 i/o 错误 
- 
Errno::ELOOP - 在转换 sockaddr 中的路径名时,遇到了太多的符号链接 
- 
Errno::ENAMETOOLLONG - 路径名的组件超出了 NAME_MAX 个字符,或者整个路径名超出了 PATH_MAX 个字符 
- 
Errno::ENOENT - 路径名的组件未命名为现有文件,或者路径名为空字符串 
- 
Errno::ENOTDIR - sockaddr 中路径名路径前缀的组件不是目录 
Windows 异常¶ ↑
在 Windows 系统上,如果调用 connect 失败,可能会引发以下系统异常
- 
Errno::ENETDOWN - 网络已关闭 
- 
Errno::EADDRINUSE - 套接字的本地地址已在使用中 
- 
Errno::EINTR - 套接字被取消 
- 
Errno::EINPROGRESS - 阻塞套接字正在进行中,或者服务提供商仍在处理回调函数。或者在 socket上正在进行非阻塞连接调用。
- 
Errno::EALREADY - 请参阅 Errno::EINVAL 
- 
Errno::EADDRNOTAVAIL - 远程地址不是有效地址,例如 ADDR_ANY TODO 检查 ADDRANY TO INADDR_ANY
- 
Errno::EAFNOSUPPORT - 指定系列中的地址不能与此 socket一起使用
- 
Errno::ECONNREFUSED - 目标 sockaddr 没有监听连接,拒绝了连接请求 
- 
Errno::EFAULT - 套接字的内部地址或地址长度参数太小,或者不是用户空间地址的有效部分 
- 
Errno::EINVAL - socket是一个监听套接字
- 
Errno::EISCONN - socket已连接
- 
Errno::ENETUNREACH - 此时无法从此主机到达网络 
- 
Errno::EHOSTUNREACH - 不存在到达网络的路由 
- 
Errno::ENOBUFS - 没有可用的缓冲区空间 
- 
Errno::ENOTSOCK - socket参数不引用套接字
- 
Errno::ETIMEDOUT - 尝试连接在建立连接之前超时。 
- 
Errno::EWOULDBLOCK - 该套接字被标记为非阻塞,并且无法立即完成连接 
- 
Errno::EACCES - 尝试将数据报套接字连接到广播地址失败 
参见¶ ↑
- 
基于 Unix 系统的 connect 手册页 
- 
Microsoft 的 Winsock 函数参考中的 connect 函数 
来源
# File ext/socket/lib/socket.rb, line 1629 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
在为底层文件描述符设置 O_NONBLOCK 后,请求在给定的 remote_sockaddr 上建立连接。如果成功则返回 0,否则会引发异常。
参数¶ ↑
- 
remote_sockaddr- 包含在字符串或Addrinfo对象中的structsockaddr
示例:¶ ↑
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(80, 'www.google.com') begin # emulate blocking connect socket.connect_nonblock(sockaddr) rescue IO::WaitWritable IO.select(nil, [socket]) # wait 3-way handshake completion begin socket.connect_nonblock(sockaddr) # check connection failure rescue Errno::EISCONN end end socket.write("GET / HTTP/1.0\r\n\r\n") results = socket.read
有关调用 connect_nonblock 失败时可能抛出的异常,请参阅 Socket#connect。
Socket#connect_nonblock 可能会引发任何与 connect(2) 失败相对应的错误,包括 Errno::EINPROGRESS。
如果异常是 Errno::EINPROGRESS,它将被 IO::WaitWritable 扩展。因此,可以使用 IO::WaitWritable 来捕获异常以重试 connect_nonblock。
通过将关键字参数 exception 指定为 false,可以指示 connect_nonblock 不应引发 IO::WaitWritable 异常,而是返回符号 :wait_writable。
参见¶ ↑
来源
# File ext/socket/lib/socket.rb, line 468 def ipv6only! if defined? Socket::IPV6_V6ONLY self.setsockopt(:IPV6, :V6ONLY, 1) end end
如果 IPV6_V6ONLY 可用,则启用套接字选项 IPV6_V6ONLY。
来源
VALUE
rsock_sock_listen(VALUE sock, VALUE log)
{
    rb_io_t *fptr;
    int backlog;
    backlog = NUM2INT(log);
    GetOpenFile(sock, fptr);
    if (listen(fptr->fd, backlog) < 0)
        rb_sys_fail("listen(2)");
    return INT2FIX(0);
}
          使用指定的 int 作为积压值来监听连接。只有当 socket 的类型为 SOCK_STREAM 或 SOCK_SEQPACKET 时,调用 listen 才适用。
参数¶ ↑
- 
backlog- 待处理连接队列的最大长度。
示例 1¶ ↑
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
示例 2(监听任意端口,仅限基于 Unix 的系统):¶ ↑
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
基于 Unix 的异常¶ ↑
在基于 Unix 的系统中,上述方法可以工作,因为会在 ADDR_ANY 地址上创建一个新的 sockaddr 结构,使用内核传递的任意端口号。这在 Windows 上不起作用,因为 Windows 要求在 listen 之前通过调用 bind 来绑定 socket。
如果 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 期间发生,但如果对部分通配符地址(涉及 ADDR_ANY)调用 bind 并且需要在调用 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 方法的类型
参见¶ ↑
- 
基于 Unix 系统的 listen 手册页 
- 
Microsoft Winsock 函数参考中的 listen 函数 
来源
static VALUE
sock_recvfrom(int argc, VALUE *argv, VALUE sock)
{
    return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET);
}
          从 socket 接收最多 maxlen 个字节。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 - 在从文件系统读取或写入时发生 i/o 错误 
- 
Errno::ENOBUFS - 系统中没有足够的资源来执行操作 
- 
Errno::ENOMEM - 没有足够的内存来满足请求 
- 
Errno::ENOSR - 没有足够的 STREAMS 资源可用来完成操作 
- 
Errno::ENOTCONN - 尝试在未连接的连接模式套接字上接收 
- 
Errno::ENOTSOCK - socket不引用套接字
- 
Errno::EOPNOTSUPP - 此套接字类型不支持指定的标志 
- 
Errno::ETIMEDOUT - 连接在连接建立期间或由于活动连接上的传输超时而超时 
Windows 异常¶ ↑
在 Windows 系统中,如果调用 recvfrom 失败,可能会引发以下系统异常
- 
Errno::ENETDOWN - 网络已关闭 
- 
Errno::EFAULT - socket上的内部缓冲区和 from 参数不是用户地址空间的一部分,或者内部 fromlen 参数太小,无法容纳对等地址
- 
Errno::EINTR - (阻塞)调用被对 WinSock 函数 WSACancelBlockingCall 的内部调用取消 
- 
Errno::EINPROGRESS - 阻塞的 Windows Sockets 1.1 调用正在进行中,或者服务提供程序仍在处理回调函数 
- 
Errno::EINVAL - socket尚未通过调用 bind 进行绑定,或者指定了未知标志,或者为启用了SO_OOBINLINE的套接字指定了MSG_OOB,或者(仅对于字节流式套接字)socket上的内部 len 参数为零或负数
- 
Errno::EISCONN - socket已连接。不允许在面向连接或无连接的套接字上的已连接套接字上调用 recvfrom。
- 
Errno::ENETRESET - 由于保持活动状态检测到操作正在进行时发生故障,连接已断开。 
- 
Errno::EOPNOTSUPP - 指定了 MSG_OOB,但socket不是流式类型,例如类型SOCK_STREAM。与socket关联的通信域中不支持 OOB 数据,或者socket是单向的,仅支持发送操作
- 
Errno::ESHUTDOWN - socket已关闭。在调用 shutdown 后,无法在套接字上调用 recvfrom。
- 
Errno::EWOULDBLOCK - socket被标记为非阻塞,并且调用 recvfrom 将会阻塞。
- 
Errno::EMSGSIZE - 消息太大,无法放入指定的缓冲区中,已被截断。 
- 
Errno::ETIMEDOUT - 由于网络故障或另一端的系统在没有通知的情况下关闭,连接已断开 
- 
Errno::ECONNRESET - 远程端执行硬关闭或终止关闭时,虚拟电路已重置。应用程序应关闭套接字;它不再可用。在 UDP 数据报套接字上,此错误表示之前的发送操作导致 ICMP 端口不可达消息。 
来源
# File ext/socket/lib/socket.rb, line 541 def recvfrom_nonblock(len, flag = 0, str = nil, exception: true) __recvfrom_nonblock(len, flag, str, exception) end
在为底层文件描述符设置 O_NONBLOCK 后,使用 recvfrom(2) 从 socket 接收最多 maxlen 个字节。flags 是零个或多个 MSG_ 选项。结果的第一个元素 mesg 是接收到的数据。第二个元素 sender_addrinfo 包含发送方的协议特定地址信息。
当 recvfrom(2) 返回 0 时,Socket#recv_nonblock 返回 nil。在大多数情况下,这意味着连接已关闭,但对于 UDP 连接,这可能意味着接收到了空数据包,因为底层 API 无法区分这两种情况。
参数¶ ↑
- 
maxlen- 从套接字接收的最大字节数
- 
flags- 零个或多个MSG_选项
- 
outbuf- 目标String缓冲区
- 
opts- 关键字哈希,支持 'exception: false`
示例¶ ↑
# In one file, start this first require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(2200, 'localhost') socket.bind(sockaddr) socket.listen(5) client, client_addrinfo = socket.accept begin # emulate blocking recvfrom pair = client.recvfrom_nonblock(20) rescue IO::WaitReadable IO.select([client]) retry end data = pair[0].chomp puts "I only received 20 bytes '#{data}'" sleep 1 socket.close # In another file, start this second require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(2200, 'localhost') socket.connect(sockaddr) socket.puts "Watch this get cut short!" socket.close
有关调用 recvfrom_nonblock 失败时可能引发的异常,请参阅 Socket#recvfrom。
Socket#recvfrom_nonblock 可能会引发与 recvfrom(2) 失败相对应的任何错误,包括 Errno::EWOULDBLOCK。
如果异常是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,则会由 IO::WaitReadable 扩展。因此,可以使用 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
有关调用 sysaccept 失败时可能引发的异常,请参阅 Socket#accept。