Socket 類別
類別
Socket
提供存取底層作業系統 socket 實作。可提供比特定通訊協定的 socket 類別更特定於作業系統的功能。
在 Socket::Constants
定義的常數也在 Socket
中定義。例如,Socket::AF_INET
可用,Socket::Constants::AF_INET 也可用。有關常數清單,請參閱 Socket::Constants
。
什麼是 socket?¶ ↑
Socket 是雙向通訊通道的端點。Socket 可在處理序內、同一台電腦上的處理序之間或不同電腦之間進行通訊。有許多類型的 socket:例如 TCPSocket
、UDPSocket
或 UNIXSocket
。
Socket 有自己的詞彙
網域:通訊協定家族
類型:兩個端點之間的通訊類型,通常為
通訊協定:通常為零。這可以用於識別通訊協定的變體。
主機名稱:網路介面的識別碼
-
字串(主機名稱、IPv4 或 IPv6 位址或指定廣播位址的
broadcast
) -
指定
INADDR_ANY
的零長度字串 -
整數(解譯為主機位元組順序中的二進位位址)。
快速入門¶ ↑
許多類別,例如 TCPSocket
、UDPSocket
或 UNIXSocket
,與等效的 C 程式設計介面相比,簡化了 Socket 的使用。
讓我們使用類似 C 的方式建立一個使用 IPv4 通訊協定的網際網路 Socket
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 的一般方式是 Socket.new
,但大多數情況下都有多種建立 Socket 的方法。
- TCP 用戶端 Socket
- TCP 伺服器 Socket
- UNIX 用戶端 Socket
- UNIX 伺服器 Socket
文件由¶ ↑
-
Zach Dennis
-
Sam Roberts
-
來自 Pragmatic Bookshelf 的《Ruby 程式設計》
本文件中的許多資料經許可取自 Pragmatic Bookshelf 的《Ruby 程式設計》
常數
- AF_ALG
核心加密 API 介面
- AF_APPLETALK
AppleTalk 協定
- AF_ATM
非同步傳輸模式
- AF_AX25
AX.25 協定
- AF_BLUETOOTH
Bluetooth 低階 Socket 協定
- 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 超通道協定
- 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 Socket
- AF_NETLINK
核心使用者介面裝置
- AF_NS
XEROX NS 協定
- AF_OSI
ISO 開放系統互連協定
- AF_PACKET
直接鏈路層存取
- AF_PPP
點對點協定
- AF_PPPOX
通用 PPP 傳輸層,用於設定 L2 隧道(L2TP 和 PPPoE)
- AF_PUP
PARC Universal Packet 協定
- AF_RDS
可靠資料報套接字 (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
套接字類型不受支援
- EAI_SYSTEM
系統錯誤在 errno 中傳回
- 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
傳輸硬體佇列已滿
- IFF_DRV_RUNNING
已配置資源
- IFF_DYING
介面正在關閉
- IFF_DYNAMIC
撥接裝置,地址會變更
- IFF_EBRIDGE
乙太網路橋接裝置
- IFF_ECHO
傳送封包時會產生回音
- IFF_ISATAP
ISATAP 介面 (RFC4214)
- IFF_LINK0
每個連結層定義的位元 0
- IFF_LINK1
每個連結層定義的位元 1
- IFF_LINK2
每個連結層定義的位元 2
- IFF_LIVE_ADDR_CHANGE
執行期間硬體地址會變更
- IFF_LOOPBACK
迴路網路
- IFF_LOWER_UP
驅動程式訊號 L1 已啟動
- IFF_MACVLAN_PORT
裝置用作 macvlan 埠
- IFF_MASTER
負載平衡器的主要裝置
- IFF_MASTER_8023AD
連線的主要裝置,802.3ad。
- IFF_MASTER_ALB
連線的主要裝置,平衡 alb。
- IFF_MASTER_ARPMON
連線的主要裝置,正在使用 ARP mon
- 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
的 socket 會從所有介面接收封包,並從預設 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 Universal Packet 協定
- IPPROTO_RAW
原始 IP 封包
- IPPROTO_ROUTING
IP6 路由標頭
- IPPROTO_TCP
TCP
- IPPROTO_TP
ISO 傳輸協定類別 4
- IPPROTO_UDP
UDP
- IPPROTO_XTP
Xpress 傳輸協定
- IPV6_CHECKSUM
原始 socket 的檢查總和偏移量
- 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
Socket 可以加入的最大多播群組數
- IP_MINTTL
接收封包允許的最小 TTL
- IP_MSFILTER
多播來源過濾
- IP_MTU
Socket 的最大傳輸單位
- 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
設定未指定埠號的 Socket 的埠號範圍
- IP_RECVDSTADDR
使用資料報接收 IP 目的位址
- IP_RECVERR
啟用擴充的可靠錯誤訊息傳遞
- IP_RECVIF
使用資料報接收介面資訊
- IP_RECVOPTS
使用資料報接收所有 IP 選項
- IP_RECVRETOPTS
接收回應的所有 IP 選項
- IP_RECVSLLA
使用資料報接收鏈路層位址
- IP_RECVTOS
使用傳入封包接收 TOS
- IP_RECVTTL
使用資料報接收 IP TTL
- IP_RETOPTS
包含在資料報中的 IP 選項
- IP_ROUTER_ALERT
通知傳輸路由器更仔細地檢查 IP 封包的內容
- IP_SENDSRCADDR
傳送 UDP 資料報的來源位址
- IP_TOS
IP 服務類型
- IP_TRANSPARENT
透明代理
- IP_TTL
IP 存活時間
- IP_UNBLOCK_SOURCE
解除具有給定來源位址的 IPv4 多播封包的封鎖
- IP_XFRM_POLICY
- LOCAL_CONNWAIT
連線會封鎖,直到接受
- LOCAL_CREDS
傳遞憑證給接收者
- LOCAL_PEERCRED
擷取對等方憑證
- MCAST_BLOCK_SOURCE
封鎖來自此來源的多播封包
- MCAST_EXCLUDE
專屬多播來源篩選器
- MCAST_INCLUDE
包含式多播來源篩選器
- MCAST_JOIN_GROUP
加入多播群組
- MCAST_JOIN_SOURCE_GROUP
加入多播來源群組
- MCAST_LEAVE_GROUP
離開多播群組
- MCAST_LEAVE_SOURCE_GROUP
離開多播來源群組
- MCAST_MSFILTER
多播來源過濾
- MCAST_UNBLOCK_SOURCE
取消封鎖來自此來源的多播封包
- MSG_COMPAT
記錄結束
- MSG_CONFIRM
確認路徑有效性
- MSG_CTRUNC
傳送前控制資料遺失
- MSG_DONTROUTE
傳送時不使用路由表
- MSG_DONTWAIT
此訊息不應封鎖
- MSG_EOF
資料完成連線
- MSG_EOR
資料完成記錄
- MSG_ERRQUEUE
從錯誤佇列中擷取訊息
- MSG_FASTOPEN
減少交握程序的步驟
- MSG_FIN
- MSG_FLUSH
暫停序列的開始。傾印至 so_temp
- MSG_HAVEMORE
資料已準備好讀取
- MSG_HOLD
在 so_temp 中暫停片段
- MSG_MORE
傳送者將傳送更多資料
- MSG_NOSIGNAL
不產生 SIGPIPE
- MSG_OOB
處理帶外資料
- MSG_PEEK
窺視輸入訊息
- MSG_PROXY
等待完整要求
- MSG_RCVMORE
資料仍保留在目前的封包中
- MSG_RST
- MSG_SEND
傳送 so_temp 中的封包
- MSG_SYN
- MSG_TRUNC
傳送前資料已捨棄
- MSG_WAITALL
等待完整要求或錯誤
- NI_DGRAM
指定的服務是資料報服務(查詢 UDP 埠)
- NI_MAXHOST
主機名稱的最大長度
- NI_MAXSERV
服務名稱的最大長度
- NI_NAMEREQD
需要名稱
- NI_NOFQDN
本地主機不需要 FQDN,只傳回本地部分
- NI_NUMERICHOST
傳回數字位址
- NI_NUMERICSERV
傳回服務名稱為數字字串
- PF_ALG
核心加密 API 介面
- PF_APPLETALK
AppleTalk 協定
- PF_ATM
非同步傳輸模式
- PF_AX25
AX.25 協定
- PF_BLUETOOTH
Bluetooth 低階 Socket 協定
- 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 超通道協定
- 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 Socket
- 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 Universal Packet 協定
- PF_RDS
可靠資料報套接字 (RDS) 協定
- PF_ROUTE
內部路由協定
- PF_RTIP
協助識別 RTIP 封包
- PF_SIP
簡易網際網路協定
- PF_SNA
IBM SNA 協定
- PF_SYSTEM
核心事件訊息
- PF_TIPC
TIPC,「叢集網域套接字」協定
- PF_UNIX
UNIX 套接字
- PF_UNSPEC
未指定協定,任何受支援的地址系列
- PF_VSOCK
VSOCK(最初為「VMWare VSockets」)協定,用於管理程式-訪客通訊
- PF_XDP
XDP(快速資料路徑)介面
- PF_XTP
eXpress 傳輸協定
- SCM_BINTIME
時間戳記 (bintime)
- SCM_CREDENTIALS
傳送者的憑證
- SCM_CREDS
處理序憑證
- SCM_RIGHTS
存取權限
- SCM_TIMESTAMP
時間戳記 (timeval)
- SCM_TIMESTAMPING
時間戳記 (timespec 清單) (Linux 2.6.30)
- SCM_TIMESTAMPNS
Timespec (timespec)
- SCM_UCRED
使用者憑證
- SCM_WIFI_STATUS
Wifi 狀態 (Linux 3.3)
- SHUT_RD
關閉 socket 的讀取端
- SHUT_RDWR
關閉 socket 的兩端
- SHUT_WR
關閉 socket 的寫入端
- SOCK_CLOEXEC
設定新檔案描述符上的 close-on-exec (FD_CLOEXEC) 旗標。
- SOCK_DGRAM
資料報 socket 提供無連線、不可靠的訊息傳遞
- SOCK_NONBLOCK
設定新檔案描述符所參照的開啟檔案描述 (請參閱 open(2)) 上的 O_NONBLOCK 檔案狀態旗標。
- SOCK_PACKET
裝置層級封包存取
- SOCK_RAW
原始 socket 提供低層級存取,可直接存取或實作網路協定
- SOCK_RDM
可靠資料報 socket 提供可靠的訊息傳遞
- SOCK_SEQPACKET
循序封包 socket 提供資料報的循序、可靠雙向連線
- SOCK_STREAM
串流 socket 提供位元組串流的循序、可靠雙向連線
- SOL_ATALK
AppleTalk socket 選項
- SOL_AX25
AX.25 socket 選項
- SOL_IP
IP socket 選項
- SOL_IPX
IPX socket 選項
- SOL_SOCKET
Socket 層級選項
- SOL_TCP
TCP socket 選項
- SOL_UDP
UDP socket 選項
- SOMAXCONN
可為 socket 排隊的最大連線要求
- SOPRI_BACKGROUND
背景 socket 優先順序
- SOPRI_INTERACTIVE
互動式 socket 優先順序
- SOPRI_NORMAL
一般 socket 優先順序
- 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
設定低延遲輪詢的微秒閾值 (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
接收附加到 socket 的 CPU (Linux 3.19)
- SO_INCOMING_NAPI_ID
接收附加到 RX 佇列的 napi ID (Linux 4.12)
- SO_KEEPALIVE
保持連線
- SO_LINGER
如果資料存在,則關閉時暫留
- SO_LOCK_FILTER
鎖定附加到 socket 的過濾器 (Linux 3.9)
- SO_MAC_EXEMPT
免除未標記對等方的強制存取控制
- SO_MARK
設定標記以進行基於標記的路由 (Linux 2.6.25)
- SO_MAX_PACING_RATE
限制傳輸層計算的速率。 [每秒位元組數] (Linux 3.13)
- SO_NKE
安裝 socket 層級的網路核心擴充功能
- SO_NOFCS
設定 socket 的 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
連接到此 socket 的外部程序的憑證
- SO_PEERNAME
連線使用者的名稱
- SO_PEERSEC
取得安全憑證 (Linux 2.6.2)
- SO_PRIORITY
此 socket 上所有封包的通訊協定定義優先順序
- 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
設定此 socket 的路由表 (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 偵測 socket 錯誤佇列 (Linux 3.10)
- SO_SETFIB
設定 socket 的關聯路由表 (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
取得 socket 類型
- 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
擷取此 socket 的資訊 (macOS)
- TCP_COOKIE_TRANSACTIONS
TCP Cookie 交易(Linux 2.6.33,glibc 2.18)
- TCP_CORK
不傳送部分幀(Linux 2.2,glibc 2.2)
- TCP_DEFER_ACCEPT
在資料準備好之前,不通知監聽 socket(Linux 2.4,glibc 2.2)
- TCP_FASTOPEN
減少握手程序的步驟(Linux 3.7,glibc 2.18)
- TCP_INFO
擷取有關此 socket 的資訊(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 socket 的生命週期(Linux 2.4,glibc 2.2)
- TCP_MAXSEG
設定最大區段大小
- TCP_MD5SIG
使用 MD5 摘要(RFC2385,Linux 2.6.20,glibc 2.7)
- TCP_NODELAY
不延遲傳送以合併封包
- TCP_NOOPT
不使用 TCP 選項
- TCP_NOPUSH
不推播最後一個寫入區塊
- TCP_QUEUE_SEQ
修復模式佇列的順序(Linux 3.5,glibc 2.18)
- TCP_QUICKACK
啟用 quickack 模式(Linux 2.4.4,glibc 2.3)
- TCP_REPAIR
修復模式(Linux 3.5,glibc 2.18)
- TCP_REPAIR_OPTIONS
修復模式的選項(Linux 3.5,glibc 2.18)
- TCP_REPAIR_QUEUE
修復模式的佇列(Linux 3.5,glibc 2.18)
- TCP_SYNCNT
在中斷連線之前,SYN 重新傳送的次數(Linux 2.4,glibc 2.2)
- TCP_THIN_DUPACK
處理 thin-streams 的重複確認(Linux 2.6.34,glibc 2.18)
- TCP_THIN_LINEAR_TIMEOUTS
thin-streams 的線性逾時(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)
公開類別方法
針對透過指定 socket 接受的每個連線,提供 socket 和客戶端地址。
參數是 socket 的清單。個別參數應該是 socket 或 socket 陣列。
此方法會依序提供區塊。這表示在區塊傳回之前,不會接受下一個連線。因此,應該使用並行機制(例如執行緒)來一次服務多個客戶端。
# File ext/socket/lib/socket.rb, line 805 def self.accept_loop(*sockets) # :yield: socket, client_addrinfo sockets.flatten!(1) if sockets.empty? raise ArgumentError, "no sockets" end loop { readable, _, _ = IO.select(sockets) readable.each {|r| sock, addr = r.accept_nonblock(exception: false) next if sock == :wait_readable yield sock, addr } } end
取得 nodename:servname 的地址資訊。
請注意,Addrinfo.getaddrinfo
以物件導向的方式提供相同的功能。
family 應為地址家族,例如::INET、:INET6 等。
socktype 應為 Socket 類型,例如::STREAM、:DGRAM、:RAW 等。
protocol 應為家族中定義的協定,且預設為家族的 0。
flags 應為 Socket::AI_* 常數的位元 OR。
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_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; }
改用 Addrinfo#getnameinfo
。此方法已棄用,原因如下
-
不常見的地址表示法:4/16 位元組二進位字串表示 IPv4/IPv6 地址。
-
gethostbyaddr() 可能需要很長時間,且可能會封鎖其他執行緒。(GVL 無法釋放,因為 gethostbyname() 不是執行緒安全的。)
-
此方法使用已從 POSIX 中移除的 gethostbyname() 函式。
此方法取得 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_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.getaddrinfo
。此方法已棄用,原因如下
-
結果的第 3 個元素是第一個地址的地址家族。不會傳回其他地址的地址家族。
-
不常見的地址表示法:4/16 位元組二進位字串表示 IPv4/IPv6 地址。
-
gethostbyname() 可能需要很長時間,且可能會封鎖其他執行緒。(GVL 無法釋放,因為 gethostbyname() 不是執行緒安全的。)
-
此方法使用已從 POSIX 中移除的 gethostbyname() 函式。
此方法取得 hostname 的主機資訊。
p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\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); }
傳回主機名稱。
p Socket.gethostname #=> "hal"
請注意,無法保證可以使用 gethostbyname、getaddrinfo 等轉換為 IP 地址。如果您需要本機 IP 地址,請使用 Socket.ip_address_list
。
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; }
傳回介面地址陣列。陣列的元素是 Socket::Ifaddr
的實例。
此方法可 used to find multicast-enabled interfaces
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 socket_s_getifaddrs(VALUE self) { return rsock_getifaddrs(); }
取得 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_* 常數的按位元 OR。
注意:最後一個表單與 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_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); }
取得 service_name 的埠號。
如果未提供 protocol_name,則假設為 “tcp”。
Socket.getservbyname("smtp") #=> 25 Socket.getservbyname("shell") #=> 514 Socket.getservbyname("syslog", "udp") #=> 514
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); }
取得 port 的埠號。
如果未提供 protocol_name,則假設為 “tcp”。
Socket.getservbyport(80) #=> "www" Socket.getservbyport(514, "tcp") #=> "shell" Socket.getservbyport(514, "udp") #=> "syslog"
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); }
傳回本機 IP 位址作為陣列。
陣列包含 Addrinfo
物件。
pp Socket.ip_address_list #=> [#<Addrinfo: 127.0.0.1>, #<Addrinfo: 192.168.0.128>, #<Addrinfo: ::1>, ...]
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 }
建立新的 socket 物件。
domain 應該是通訊網域,例如::INET、:INET6、:UNIX 等。
socktype 應為 Socket 類型,例如::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_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); }
將 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_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; }
將 path 封裝為 AF_UNIX
sockaddr 字串。
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\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; }
建立一對彼此連線的 socket。
domain 應該是通訊網域,例如::INET、:INET6、:UNIX 等。
socktype 應為 Socket 類型,例如::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"
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; }
將 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_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; }
將 path 封裝為 AF_UNIX
sockaddr 字串。
Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\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; }
建立一對彼此連線的 socket。
domain 應該是通訊網域,例如::INET、:INET6、:UNIX 等。
socktype 應為 Socket 類型,例如::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"
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; }
使用 TCP/IP 建立連線至 host:port 的新 socket 物件。
如果提供了 local_host:local_port,則 socket 會繫結到該位置。
選配的最後一個引數 opts 是由雜湊表示的選項。opts 可能有下列選項
- :connect_timeout
-
指定逾時時間(單位為秒)。
如果提供了區塊,則會呼叫該區塊並傳入 socket。區塊的值會傳回。此方法傳回時,socket 會關閉。
如果沒有給定區塊,則傳回 socket。
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 628 def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket last_error = nil ret = nil local_addr_list = nil if local_host != nil || local_port != nil local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil) end Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai| if local_addr_list local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily } next unless local_addr else local_addr = nil end begin sock = local_addr ? ai.connect_from(local_addr, timeout: connect_timeout) : ai.connect(timeout: connect_timeout) rescue SystemCallError last_error = $! next end ret = sock break } unless ret if last_error raise last_error else raise SocketError, "no appropriate local address" end end if block_given? begin yield ret ensure ret.close end else ret end end
在 port 上建立 TCP/IP 伺服器,並針對每個接受的連線呼叫區塊。區塊會以 socket 和 client_address 作為 Addrinfo
物件呼叫。
如果指定 host,則會將其與 port 一起用於判斷伺服器位址。
區塊傳回時 不會 關閉 socket。因此,應用程式應明確地關閉它。
此方法會依序呼叫區塊。這表示在區塊傳回之前,不會接受下一個連線。因此,應使用並行機制(例如執行緒)來一次服務多個客戶端。
請注意,Addrinfo.getaddrinfo
用於判斷伺服器 socket 位址。當 Addrinfo.getaddrinfo
傳回兩個或以上的位址(例如 IPv4 和 IPv6 位址)時,會使用所有位址。如果至少可以使用一個 socket,則 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 861 def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo tcp_server_sockets(host, port) {|sockets| accept_loop(sockets, &b) } end
為 host 和 port 建立 TCP/IP 伺服器 socket。host 是選用的。
如果沒有給定區塊,則會傳回一個監聽 socket 陣列。
如果給定區塊,則會以 socket 呼叫區塊。會傳回區塊的值。此方法傳回時會關閉 socket。
如果 port 為 0,則會動態選擇實際埠號。但是,結果中的所有 socket 都具有相同的埠號。
# 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 761 def self.tcp_server_sockets(host=nil, port) if port == 0 sockets = tcp_server_sockets_port0(host) else last_error = nil sockets = [] begin Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai| begin s = ai.listen rescue SystemCallError last_error = $! next end sockets << s } if sockets.empty? raise last_error end rescue Exception sockets.each(&:close) raise end end if block_given? begin yield sockets ensure sockets.each(&:close) end else sockets end end
在 port 上建立 UDP/IP 伺服器,並針對每個到達的訊息呼叫區塊。區塊會以訊息及其來源資訊呼叫。
此方法會使用 port 內部配置 socket。如果指定 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 1031 def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source udp_server_sockets(host, port) {|sockets| udp_server_loop_on(sockets, &b) } end
在指定的 sockets 上執行 UDP/IP 伺服器迴圈。
適合作為引數的是 Socket.udp_server_sockets
的傳回值。
它會針對收到的每則訊息呼叫區塊。
# File ext/socket/lib/socket.rb, line 1004 def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src loop { readable, _, _ = IO.select(sockets) udp_server_recv(readable, &b) } end
從指定的 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 977 def self.udp_server_recv(sockets) sockets.each {|r| msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock(exception: false) next if msg == :wait_readable ai = r.local_address if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) } ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port) yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo } else yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| r.send reply_msg, 0, sender_addrinfo } end } end
為 UDP 伺服器建立 UDP/IP sockets。
如果沒有提供區塊,則會傳回 sockets 陣列。
如果提供了區塊,則會使用 sockets 呼叫區塊。傳回區塊的值。當此方法傳回時,會關閉 sockets。
如果 port 為零,則會選擇某個 port。但所選的 port 會用於所有 sockets。
# 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 889 def self.udp_server_sockets(host=nil, port) last_error = nil sockets = [] ipv6_recvpktinfo = nil if defined? Socket::AncillaryData if defined? Socket::IPV6_RECVPKTINFO # RFC 3542 ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO elsif defined? Socket::IPV6_PKTINFO # RFC 2292 ipv6_recvpktinfo = Socket::IPV6_PKTINFO end end local_addrs = Socket.ip_address_list ip_list = [] Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai| if ai.ipv4? && ai.ip_address == "0.0.0.0" local_addrs.each {|a| next unless a.ipv4? ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0); } elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo local_addrs.each {|a| next unless a.ipv6? ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0); } else ip_list << ai end } ip_list.uniq!(&:to_sockaddr) if port == 0 sockets = ip_sockets_port0(ip_list, false) else ip_list.each {|ip| ai = Addrinfo.udp(ip.ip_address, port) begin s = ai.bind rescue SystemCallError last_error = $! next end sockets << s } if sockets.empty? raise last_error end end sockets.each {|s| ai = s.local_address if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::" s.setsockopt(:IPV6, ipv6_recvpktinfo, 1) end } if block_given? begin yield sockets ensure sockets.each(&:close) if sockets end else sockets end end
使用 UNIX socket socket 建立連線至 path 的新 socket。
如果提供了區塊,則會呼叫該區塊並傳入 socket。區塊的值會傳回。此方法傳回時,socket 會關閉。
如果沒有給定區塊,則傳回 socket。
# 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 1081 def self.unix(path) # :yield: socket addr = Addrinfo.unix(path) sock = addr.connect if block_given? begin yield sock ensure sock.close end else sock end end
在 path 上建立 UNIX socket 伺服器。它會針對每個已接受的 socket 呼叫區塊。
如果指定了 host,則會與 port 一起使用來決定伺服器 port。
當區塊傳回時,不會關閉 socket。因此應用程式應自行關閉。
此方法會先刪除由 path 指出的 socket 檔案,如果檔案是 socket 檔案且屬於應用程式的使用者。這只有在 path 的目錄未被惡意使用者變更時才安全。因此請勿使用 /tmp/malicious-users-directory/socket。假設 /tmp 有 sticky bit,則 /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 1168 def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo unix_server_socket(path) {|serv| accept_loop(serv, &b) } end
在 path 上建立 UNIX 伺服器 socket
如果沒有提供區塊,則會傳回監聽 socket。
如果給予區塊,它會與 socket 一起呼叫,並傳回區塊值。當區塊結束時,socket 會關閉,且 socket 檔案會移除。
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> }
# File ext/socket/lib/socket.rb, line 1111 def self.unix_server_socket(path) unless unix_socket_abstract_name?(path) begin st = File.lstat(path) rescue Errno::ENOENT end if st&.socket? && st.owned? File.unlink path end end s = Addrinfo.unix(path).listen if block_given? begin yield s ensure s.close unless unix_socket_abstract_name?(path) File.unlink path end end else s end end
將 sockaddr 解壓縮到 port 和 ip_address。
sockaddr 應該是 AF_INET/AF_INET6 的字串或 addrinfo。
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_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 解壓縮到路徑。
sockaddr 應該是 AF_UNIX
的字串或 addrinfo。
sockaddr = Socket.sockaddr_un("/tmp/sock") p Socket.unpack_sockaddr_un(sockaddr) #=> "/tmp/sock"
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; }
私人類別方法
# File ext/socket/lib/socket.rb, line 1139 def unix_socket_abstract_name?(path) /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path end
公開實例方法
接受下一個連線。傳回新的 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>]
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)); }
在為基礎檔案描述設定 O_NONBLOCK 之後,使用 accept(2) 接受傳入連線。它傳回包含已接受的 socket 以供傳入連線、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
。
請參閱¶ ↑
# File ext/socket/lib/socket.rb, line 598 def accept_nonblock(exception: true) __accept_nonblock(exception) end
繫結到指定的本地端地址。
參數¶ ↑
-
local_sockaddr
- 儲存在字串或Addrinfo
物件中的struct
sockaddr
範例¶ ↑
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
未參照 socket -
Errno::EOPNOTSUPP -
socket
的 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_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); }
要求在指定的 remote_sockaddr
上建立連線。如果成功,則傳回 0,否則會引發例外。
參數¶ ↑
-
remote_sockaddr
- 儲存在字串或Addrinfo
物件中的struct
sockaddr
範例:¶ ↑
# 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 的類型與繫結至指定對等位址的 socket 不同
-
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 - 已取消 socket
-
Errno::EINPROGRESS - 阻擋式 socket 正在進行中,或服務提供者仍在處理回呼函式。或
socket
上的非阻擋式連線呼叫正在進行中。 -
Errno::EALREADY - 請參閱 Errno::EINVAL
-
Errno::EADDRNOTAVAIL - 遠端位址不是有效的位址,例如 ADDR_ANY TODO 檢查 ADDRANY 至
INADDR_ANY
-
Errno::EAFNOSUPPORT - 指定的家族中的位址無法與此
socket
一起使用 -
Errno::ECONNREFUSED - 目標 sockaddr 未監聽連線,拒絕連線要求
-
Errno::EFAULT - socket 的內部位址或位址長度參數太小,或不是使用者空間位址的有效部分
-
Errno::EINVAL -
socket
是監聽 socket -
Errno::EISCONN -
socket
已連線 -
Errno::ENETUNREACH - 目前無法從此主機連線到網路
-
Errno::EHOSTUNREACH - 沒有前往網路的路由
-
Errno::ENOBUFS - 沒有可用緩衝區空間
-
Errno::ENOTSOCK -
socket
參數未參照套接字 -
Errno::ETIMEDOUT - 在建立連線之前,嘗試連線已逾時。
-
Errno::EWOULDBLOCK - socket 標記為非封鎖,且無法立即完成連線
-
Errno::EACCES - 嘗試將資料報套接字連接到廣播地址失敗
請參閱¶ ↑
-
基於 Unix 的系統上的 connect 手冊頁面
-
Microsoft 的 Winsock 函數參考中的 connect 函數
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); }
要求在為基礎檔案描述詞設定 O_NONBLOCK 之後,對指定的 remote_sockaddr
進行連線。如果成功,傳回 0,否則會引發例外狀況。
參數¶ ↑
# +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object
範例:¶ ↑
# Pull down Google's web page require 'socket' include Socket::Constants socket = Socket.new(AF_INET, SOCK_STREAM, 0) sockaddr = Socket.sockaddr_in(80, 'www.google.com') begin # emulate blocking connect socket.connect_nonblock(sockaddr) rescue IO::WaitWritable IO.select(nil, [socket]) # wait 3-way handshake completion begin socket.connect_nonblock(sockaddr) # check connection failure rescue Errno::EISCONN end end socket.write("GET / HTTP/1.0\r\n\r\n") results = socket.read
如果呼叫 connect_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
。
請參閱¶ ↑
# Socket#connect
# File ext/socket/lib/socket.rb, line 1218 def connect_nonblock(addr, exception: true) __connect_nonblock(addr, exception) end
如果 IPV6_V6ONLY
可用,則啟用 socket 選項 IPV6_V6ONLY
。
# File ext/socket/lib/socket.rb, line 468 def ipv6only! if defined? Socket::IPV6_V6ONLY self.setsockopt(:IPV6, :V6ONLY, 1) end end
偵聽連線,使用指定的 int
作為後端。呼叫 listen 僅適用於 socket
為類型 SOCK_STREAM
或 SOCK_SEQPACKET
的情況。
參數¶ ↑
-
backlog
- 等待連線佇列的最大長度。
範例 1¶ ↑
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) socket.bind( sockaddr ) socket.listen( 5 )
範例 2(偵聽任意埠,僅限 Unix 系統):¶ ↑
require 'socket' include Socket::Constants socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) socket.listen( 1 )
Unix 系統例外¶ ↑
在 Unix 系統上,上述程式碼會運作,因為會在 ADDR_ANY 位址上建立新的 sockaddr
結構,以取得核心傳遞的任意埠號。此程式碼在 Windows 上不會運作,因為 Windows 要求 socket
在 listen 之前必須呼叫 bind 進行繫結。
如果 backlog 超過與實作相關的最大佇列長度,將會使用實作的最大佇列長度。
在 Unix 系統上,如果呼叫 listen 失敗,可能會引發下列系統例外
-
Errno::EBADF - socket 參數不是有效的檔案描述子
-
Errno::EDESTADDRREQ - socket 未繫結至本機位址,且通訊協定不支援在未繫結的 socket 上偵聽
-
Errno::EINVAL - socket 已連線
-
Errno::ENOTSOCK - socket 參數未參照 socket
-
Errno::EOPNOTSUPP - socket 通訊協定不支援 listen
-
Errno::EACCES - 呼叫處理程序沒有適當的權限
-
Errno::EINVAL - socket 已關閉
-
Errno::ENOBUFS - 系統中沒有足夠的資源來完成呼叫
Windows 例外¶ ↑
在 Windows 系統上,如果呼叫 listen 失敗,可能會引發下列系統例外
-
Errno::ENETDOWN - 網路已關閉
-
Errno::EADDRINUSE - socket 的本機地址已經在使用中。這通常會在執行 bind 時發生,但如果對 bind 的呼叫是部分萬用字元地址(包含 ADDR_ANY),且在呼叫 listen 時需要提交特定地址,則可能會延遲發生
-
Errno::EINPROGRESS - Windows Sockets 1.1 呼叫正在進行中,或服務提供者仍在處理回呼函式
-
Errno::EINVAL -
socket
尚未使用 bind 呼叫進行繫結。 -
Errno::EISCONN -
socket
已連線 -
Errno::EMFILE - 沒有更多可用的 socket 敘述符
-
Errno::ENOBUFS - 沒有可用緩衝區空間
-
Errno::ENOTSOC -
socket
不是 socket -
Errno::EOPNOTSUPP - 參照的
socket
不是支援 listen 方法的類型
請參閱¶ ↑
-
基於 Unix 的系統上的 listen 手冊頁面
-
Microsoft 的 Winsock 函式參考中的 listen 函式
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); }
從 socket
接收最多 maxlen 位元組。flags 是零個或多個 MSG_
選項。結果的第一個元素 mesg 是接收到的資料。第二個元素 sender_addrinfo 包含寄件者的通訊協定特定地址資訊。
參數¶ ↑
-
maxlen
- 從 socket 接收的最大位元組數 -
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 - 無法存取或寫入 socket 的內部緩衝區、地址或地址長度
-
Errno::EINTR - 在任何資料可用之前,訊號中斷了 recvfrom
-
Errno::EINVAL -
MSG_OOB
旗標已設定,且沒有可用的帶外資料 -
Errno::EIO - 在讀取或寫入檔案系統時發生 i/o 錯誤
-
Errno::ENOBUFS - 系統中沒有足夠的資源來執行操作
-
Errno::ENOMEM - 沒有足夠的記憶體來滿足請求
-
Errno::ENOSR - 沒有足夠的 STREAMS 資源可供完成作業
-
Errno::ENOTCONN - 嘗試在未連接的連線模式 socket 上接收
-
Errno::ENOTSOCK -
socket
未參照 socket -
Errno::EOPNOTSUPP - 此 socket 類型不支援指定的旗標
-
Errno::ETIMEDOUT - 在建立連線期間或由於活動連線上的傳輸逾時而導致連線逾時
Windows 例外¶ ↑
如果對 recvfrom 的呼叫失敗,Windows 系統可能會引發以下系統例外
-
Errno::ENETDOWN - 網路已關閉
-
Errno::EFAULT -
socket
上的內部緩衝區和來自參數不屬於使用者位址空間,或內部 fromlen 參數太小,無法容納對等位址 -
Errno::EINTR - 內部呼叫 WinSock 函式 WSACancelBlockingCall 取消了 (封鎖) 呼叫
-
Errno::EINPROGRESS - 封鎖 Windows Sockets 1.1 呼叫正在進行中,或服務提供者仍在處理回呼函式
-
Errno::EINVAL -
socket
尚未使用 bind 呼叫進行繫結,或指定了未知旗標,或為已啟用SO_OOBINLINE
的 socket 指定了MSG_OOB
,或 (僅適用於位元組串流樣式 socket)socket
上的內部 len 參數為零或負數 -
Errno::EISCONN -
socket
已連接。在面向連線或無連線的 socket 上使用已連接的 socket 呼叫 recvfrom 是不允許的。 -
Errno::ENETRESET - 由於在操作進行期間,維持連線活動偵測到故障,因此連線已中斷。
-
Errno::EOPNOTSUPP - 指定了
MSG_OOB
,但socket
不是串流樣式,例如類型SOCK_STREAM
。與socket
關聯的通訊網域不支援 OOB 資料,或socket
是單向的,只支援傳送操作 -
Errno::ESHUTDOWN -
socket
已關閉。在呼叫 shutdown 之後,無法在 socket 上呼叫 recvfrom。 -
Errno::EWOULDBLOCK -
socket
標記為非封鎖,呼叫 recvfrom 會封鎖。 -
Errno::EMSGSIZE - 訊息太大,無法放入指定的緩衝區中,因此被截斷。
-
Errno::ETIMEDOUT - 由於網路故障或另一端的系統在沒有通知的情況下關閉,因此連線已中斷
-
Errno::ECONNRESET - 虛擬電路因遠端執行硬關閉或異常關閉而重設。應用程式應關閉 socket,它不再可用。在 UDP 資料報 socket 中,此錯誤表示先前的傳送操作導致 ICMP Port Unreachable 訊息。
static VALUE sock_recvfrom(int argc, VALUE *argv, VALUE sock) { return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET); }
在為基礎檔案描述詞設定 O_NONBLOCK 之後,使用 recvfrom(2) 從 socket
接收最多 maxlen 位元組。flags 是零或多個 MSG_
選項。結果的第一個元素,mesg,是接收到的資料。第二個元素,sender_addrinfo,包含傳送者的通訊協定特定地址資訊。
當 recvfrom(2) 傳回 0 時,Socket#recv_nonblock
傳回 nil。在多數情況下,這表示連線已關閉,但對於 UDP 連線,這可能表示已收到一個空封包,因為基礎 API 無法區分這兩種情況。
參數¶ ↑
-
maxlen
- 從 socket 接收的最大位元組數 -
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
。
請參閱¶ ↑
# 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
接受一個傳入連線,傳回包含傳入連線的(整數)檔案描述詞的陣列,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 失敗時可能會引發的例外。
請參閱¶ ↑
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)); }