Socket 類別

類別 Socket 提供存取底層作業系統 socket 實作。可提供比特定通訊協定的 socket 類別更特定於作業系統的功能。

Socket::Constants 定義的常數也在 Socket 中定義。例如,Socket::AF_INET 可用,Socket::Constants::AF_INET 也可用。有關常數清單,請參閱 Socket::Constants

什麼是 socket?

Socket 是雙向通訊通道的端點。Socket 可在處理序內、同一台電腦上的處理序之間或不同電腦之間進行通訊。有許多類型的 socket:例如 TCPSocketUDPSocketUNIXSocket

Socket 有自己的詞彙

網域:通訊協定家族

類型:兩個端點之間的通訊類型,通常為

通訊協定:通常為。這可以用於識別通訊協定的變體。

主機名稱:網路介面的識別碼

快速入門

許多類別,例如 TCPSocketUDPSocketUNIXSocket,與等效的 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

Socket.tcpTCPSocket.open

TCP 伺服器 Socket

Socket.tcp_server_loopTCPServer.open

UNIX 用戶端 Socket

Socket.unixUNIXSocket.open

UNIX 伺服器 Socket

Socket.unix_server_loopUNIXServer.open

文件由

本文件中的許多資料經許可取自 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

歐洲電腦製造商協定

NSC 超通道協定

AF_IB

InfiniBand 原生定址

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_LLC

邏輯鏈路控制(IEEE 802.2 LLC)協定

AF_LOCAL

主機內部協定

AF_MAX

此平台的最大地址系列

AF_MPLS

多協定標籤交換

AF_NATM

原生 ATM 存取

AF_NDRV

網路驅動程式原始存取

AF_NETBIOS

NetBIOS

AF_NETGRAPH

Netgraph Socket

核心使用者介面裝置

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_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

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

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_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_RST

MSG_SEND

傳送 so_temp 中的封包

MSG_SYN

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

歐洲電腦製造商協定

NSC 超通道協定

PF_IB

InfiniBand 原生定址

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_LLC

邏輯鏈路控制(IEEE 802.2 LLC)協定

PF_LOCAL

主機內部協定

PF_MAX

此平台的最大地址系列

PF_MPLS

多協定標籤交換

PF_NATM

原生 ATM 存取

PF_NDRV

網路驅動程式原始存取

PF_NETBIOS

NetBIOS

PF_NETGRAPH

Netgraph Socket

核心使用者介面裝置

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_AUTHENTICATION

SO_SECURITY_ENCRYPTION_NETWORK

SO_SECURITY_ENCRYPTION_NETWORK

SO_SECURITY_ENCRYPTION_TRANSPORT

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

在可能時繞過硬體

主要設定 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 交易(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)

公開類別方法

accept_loop(*sockets) { |socket, client_addrinfo| ... } 按一下以切換來源

針對透過指定 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
getaddrinfo(nodename, servname[, family[, socktype[, protocol[, flags[, reverse_lookup]]]]]) → array 按一下以切換來源

取得 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;
}
gethostbyaddr(address_string [, address_family]) → hostent 按一下以切換來源

改用 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;
}
gethostbyname(hostname) → [official_hostname, alias_hostnames, address_family, *address_list] 按一下以切換來源

改用 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);
}
gethostname → hostname 按一下以切換來源

傳回主機名稱。

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;
}
getifaddrs → [ifaddr1, ...] 按一下以切換來源

傳回介面地址陣列。陣列的元素是 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();
}
getnameinfo(sockaddr [, flags]) → [hostname, servicename] 按一下以切換來源

取得 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#addrIPSocket#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);
}
getservbyname(service_name) → port_number 按一下以切換來源
getservbyname(service_name, protocol_name) → port_number

取得 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);
}
getservbyport(port [, protocol_name]) → service 按一下以切換來源

取得 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_address_list → array 按一下以切換來源

傳回本機 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
}
new(domain, socktype [, protocol]) → socket 按一下以切換來源

建立新的 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);
}
sockaddr_in(port, host) → sockaddr 按一下以切換來源
pack_sockaddr_in(port, host) → sockaddr

porthost 封裝為 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;
}
sockaddr_un(path) → sockaddr 按一下以切換來源
pack_sockaddr_un(path) → sockaddr

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;
}
pair(domain, type, protocol) → [socket1, socket2] 按一下以切換來源
socketpair(domain, type, protocol) → [socket1, socket2]

建立一對彼此連線的 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;
}
sockaddr_in(port, host) → sockaddr 按一下以切換來源
pack_sockaddr_in(port, host) → sockaddr

porthost 封裝為 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;
}
sockaddr_un(path) → sockaddr 按一下以切換來源
pack_sockaddr_un(path) → sockaddr

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;
}
pair(domain, type, protocol) → [socket1, socket2] 按一下以切換來源
socketpair(domain, type, protocol) → [socket1, socket2]

建立一對彼此連線的 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(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... } 按一下以切換來源
tcp(host, port, local_host=nil, local_port=nil, [opts])

使用 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
tcp_server_loop(host=nil, port) { |socket, client_addrinfo| ... } 按一下以切換來源

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
tcp_server_sockets(host=nil, port) { |sockets| ... } 按一下以切換來源

hostport 建立 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
udp_server_loop(port) {|msg, msg_src| ... } 按一下以切換來源
udp_server_loop(host, port) {|msg, msg_src| ... }

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
udp_server_loop_on(sockets) {|msg, msg_src| ... } 按一下以切換原始碼

在指定的 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
udp_server_recv(sockets) {|msg, msg_src| ... } 按一下以切換原始碼

從指定的 sockets 接收 UDP/IP 封包。針對收到的每個封包,都會呼叫區塊。

區塊會接收 msgmsg_srcmsg 是字串,也就是接收封包的酬載。msg_srcSocket::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_server_sockets([host, ] port) 按一下以切換原始碼

為 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(path) { |socket| ... } 按一下以切換原始碼

使用 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
unix_server_loop(path) { |socket, client_addrinfo| ... } 按一下以切換原始碼

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
unix_server_socket(path) { |s| ... } 按一下以切換原始碼

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
unpack_sockaddr_in(sockaddr) → [port, ip_address] 按一下以切換來源

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);
}
unpack_sockaddr_un(sockaddr) → path 按一下以切換來源

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;
}

私人類別方法

unix_socket_abstract_name?(path) 按一下以切換來源
# File ext/socket/lib/socket.rb, line 1139
def unix_socket_abstract_name?(path)
  /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path
end

公開實例方法

accept → [client_socket, client_addrinfo] 按一下以切換來源

接受下一個連線。傳回新的 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));
}
accept_nonblock([options]) → [client_socket, client_addrinfo] 按一下以切換來源

在為基礎檔案描述設定 O_NONBLOCK 之後,使用 accept(2) 接受傳入連線。它傳回包含已接受的 socket 以供傳入連線、client_socketAddrinfoclient_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
bind(local_sockaddr) → 0 按一下以切換來源

繫結到指定的本地端地址。

參數

  • 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);
}
connect(remote_sockaddr) → 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);
}
connect_nonblock(remote_sockaddr, [options]) → 0 按一下以切換來源

要求在為基礎檔案描述詞設定 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
ipv6only!() 按一下以切換來源

如果 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
listen( int ) → 0 按一下以切換來源

偵聽連線,使用指定的 int 作為後端。呼叫 listen 僅適用於 socket 為類型 SOCK_STREAMSOCK_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 要求 socketlisten 之前必須呼叫 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);
}
recvfrom(maxlen) → [mesg, sender_addrinfo] 按一下以切換來源
recvfrom(maxlen, flags) → [mesg, sender_addrinfo]

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);
}
recvfrom_nonblock(maxlen[, flags[, outbuf[, opts]]]) → [mesg, sender_addrinfo] 按一下以切換來源

在為基礎檔案描述詞設定 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
sysaccept → [client_socket_fd, client_addrinfo] 按一下以切換來源

接受一個傳入連線,傳回包含傳入連線的(整數)檔案描述詞的陣列,client_socket_fd,以及一個 Addrinfoclient_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));
}