BasicSocket 類別
BasicSocket
是所有 Socket
類別的超級類別。
公開類別方法
取得全域 do_not_reverse_lookup
旗標。
BasicSocket.do_not_reverse_lookup #=> false
static VALUE bsock_do_not_rev_lookup(VALUE _) { return rsock_do_not_reverse_lookup?Qtrue:Qfalse; }
設定全域 do_not_reverse_lookup
旗標。
此旗標用於每個 socket 的 do_not_reverse_lookup
初始值。
s1 = TCPSocket.new("localhost", 80) p s1.do_not_reverse_lookup #=> true BasicSocket.do_not_reverse_lookup = false s2 = TCPSocket.new("localhost", 80) p s2.do_not_reverse_lookup #=> false p s1.do_not_reverse_lookup #=> true
static VALUE bsock_do_not_rev_lookup_set(VALUE self, VALUE val) { rsock_do_not_reverse_lookup = RTEST(val); return val; }
傳回包含檔案描述符 fd 的 socket 物件。
# If invoked by inetd, STDIN/STDOUT/STDERR is a socket. STDIN_SOCK = Socket.for_fd(STDIN.fileno) p STDIN_SOCK.remote_address
static VALUE bsock_s_for_fd(VALUE klass, VALUE _descriptor) { rb_io_t *fptr; int descriptor = RB_NUM2INT(_descriptor); rsock_validate_descriptor(descriptor); VALUE sock = rsock_init_sock(rb_obj_alloc(klass), descriptor); GetOpenFile(sock, fptr); return sock; }
公開實例方法
使用關閉系統呼叫禁止進一步讀取。
s1, s2 = UNIXSocket.pair s1.close_read s2.puts #=> Broken pipe (Errno::EPIPE)
static VALUE bsock_close_read(VALUE sock) { rb_io_t *fptr; GetOpenFile(sock, fptr); shutdown(fptr->fd, 0); if (!(fptr->mode & FMODE_WRITABLE)) { return rb_io_close(sock); } fptr->mode &= ~FMODE_READABLE; return Qnil; }
使用關閉系統呼叫禁止進一步寫入。
UNIXSocket.pair {|s1, s2| s1.print "ping" s1.close_write p s2.read #=> "ping" s2.print "pong" s2.close p s1.read #=> "pong" }
static VALUE bsock_close_write(VALUE sock) { rb_io_t *fptr; GetOpenFile(sock, fptr); if (!(fptr->mode & FMODE_READABLE)) { return rb_io_close(sock); } shutdown(fptr->fd, 1); fptr->mode &= ~FMODE_WRITABLE; return Qnil; }
傳回適合在本地端電腦中連線的 socket 位址。
此方法傳回 self.local_address,但有以下例外情況。
-
IPv4 未指定位址 (0.0.0.0) 會替換為 IPv4 回送位址 (127.0.0.1)。
-
IPv6 未指定位址 (::) 會替換為 IPv6 回送位址 (::1)。
如果本機地址不適合連線,SocketError
會引發。埠為 0 的 IPv4 和 IPv6 地址不適合連線。沒有路徑的 Unix 域套接字不適合連線。
Addrinfo.tcp("0.0.0.0", 0).listen {|serv| p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP> serv.connect_address.connect {|c| s, _ = serv.accept p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>] } }
# File ext/socket/lib/socket.rb, line 255 def connect_address addr = local_address afamily = addr.afamily if afamily == Socket::AF_INET raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0 if addr.ip_address == "0.0.0.0" addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol) end elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6 raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0 if addr.ip_address == "::" addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address. addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address. addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) end elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX raise SocketError, "unbound Unix socket" if addr.unix_path == "" end addr end
取得 basicsocket 的 do_not_reverse_lookup
旗標。
require 'socket' BasicSocket.do_not_reverse_lookup = false TCPSocket.open("www.ruby-lang.org", 80) {|sock| p sock.do_not_reverse_lookup #=> false } BasicSocket.do_not_reverse_lookup = true TCPSocket.open("www.ruby-lang.org", 80) {|sock| p sock.do_not_reverse_lookup #=> true }
static VALUE bsock_do_not_reverse_lookup(VALUE sock) { rb_io_t *fptr; GetOpenFile(sock, fptr); return (fptr->mode & FMODE_NOREVLOOKUP) ? Qtrue : Qfalse; }
設定 basicsocket 的 do_not_reverse_lookup
旗標。
TCPSocket.open("www.ruby-lang.org", 80) {|sock| p sock.do_not_reverse_lookup #=> true p sock.peeraddr #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] sock.do_not_reverse_lookup = false p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "54.163.249.195"] }
static VALUE bsock_do_not_reverse_lookup_set(VALUE sock, VALUE state) { rb_io_t *fptr; GetOpenFile(sock, fptr); if (RTEST(state)) { fptr->mode |= FMODE_NOREVLOOKUP; } else { fptr->mode &= ~FMODE_NOREVLOOKUP; } return sock; }
傳回 UNIX 套接字對端點的使用者和群組。結果是一個包含有效 uid 和有效 gid 的二個元素陣列。
Socket.unix_server_loop("/tmp/sock") {|s| begin euid, egid = s.getpeereid # Check the connected client is myself or not. next if euid != Process.uid # do something about my resource. ensure s.close end }
static VALUE bsock_getpeereid(VALUE self) { #if defined(HAVE_GETPEEREID) rb_io_t *fptr; uid_t euid; gid_t egid; GetOpenFile(self, fptr); if (getpeereid(fptr->fd, &euid, &egid) == -1) rb_sys_fail("getpeereid(3)"); return rb_assoc_new(UIDT2NUM(euid), GIDT2NUM(egid)); #elif defined(SO_PEERCRED) /* GNU/Linux */ rb_io_t *fptr; struct ucred cred; socklen_t len = sizeof(cred); GetOpenFile(self, fptr); if (getsockopt(fptr->fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) rb_sys_fail("getsockopt(SO_PEERCRED)"); return rb_assoc_new(UIDT2NUM(cred.uid), GIDT2NUM(cred.gid)); #elif defined(HAVE_GETPEERUCRED) /* Solaris */ rb_io_t *fptr; ucred_t *uc = NULL; VALUE ret; GetOpenFile(self, fptr); if (getpeerucred(fptr->fd, &uc) == -1) rb_sys_fail("getpeerucred(3C)"); ret = rb_assoc_new(UIDT2NUM(ucred_geteuid(uc)), GIDT2NUM(ucred_getegid(uc))); ucred_free(uc); return ret; #endif }
傳回套接字的遠端地址,格式為 sockaddr 字串。
TCPServer.open("127.0.0.1", 1440) {|serv| c = TCPSocket.new("127.0.0.1", 1440) s = serv.accept p s.getpeername #=> "\x02\x00\x82u\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" }
如果偏好使用 Addrinfo
物件而非二進位字串,請使用 BasicSocket#remote_address
。
static VALUE bsock_getpeername(VALUE sock) { union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); if (getpeername(fptr->fd, &buf.addr, &len) < 0) rb_sys_fail("getpeername(2)"); if (len0 < len) len = len0; return rb_str_new((char*)&buf, len); }
傳回套接字的本機地址,格式為 sockaddr 字串。
TCPServer.open("127.0.0.1", 15120) {|serv| p serv.getsockname #=> "\x02\x00;\x10\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" }
如果偏好使用 Addrinfo
物件而非二進位字串,請使用 BasicSocket#local_address
。
static VALUE bsock_getsockname(VALUE sock) { union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); if (getsockname(fptr->fd, &buf.addr, &len) < 0) rb_sys_fail("getsockname(2)"); if (len0 < len) len = len0; return rb_str_new((char*)&buf, len); }
取得套接字選項。這些選項與通訊協定和系統有關,請參閱您當地的系統文件以取得詳細資料。選項會傳回為 Socket::Option
物件。
參數¶ ↑
-
level
是整數,通常是 SOL_ 常數之一,例如 Socket::SOL_SOCKET,或通訊協定層級。名稱的字串或符號(可能沒有前置詞)也會被接受。 -
optname
是整數,通常是 SO_ 常數之一,例如 Socket::SO_REUSEADDR。名稱的字串或符號(可能沒有前置詞)也會被接受。
範例¶ ↑
有些套接字選項是具有布林值的整數,在這種情況下,getsockopt
可以像這樣呼叫
reuseaddr = sock.getsockopt(:SOCKET, :REUSEADDR).bool optval = sock.getsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR) optval = optval.unpack "i" reuseaddr = optval[0] == 0 ? false : true
有些套接字選項是具有數字值的整數,在這種情況下,getsockopt
可以像這樣呼叫
ipttl = sock.getsockopt(:IP, :TTL).int optval = sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL) ipttl = optval.unpack1("i")
選項值可能是結構。解碼它們很複雜,因為它涉及檢查系統標頭以確定正確的定義。範例是 +struct linger+,它在您的系統標頭中可能定義為
struct linger { int l_onoff; int l_linger; };
在此情況下,getsockopt
可以這樣呼叫
# Socket::Option knows linger structure. onoff, linger = sock.getsockopt(:SOCKET, :LINGER).linger optval = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER) onoff, linger = optval.unpack "ii" onoff = onoff == 0 ? false : true
static VALUE bsock_getsockopt(VALUE sock, VALUE lev, VALUE optname) { int level, option; socklen_t len; char *buf; rb_io_t *fptr; int family; GetOpenFile(sock, fptr); family = rsock_getfamily(fptr); level = rsock_level_arg(family, lev); option = rsock_optname_arg(family, level, optname); len = 256; #ifdef _AIX switch (option) { case SO_DEBUG: case SO_REUSEADDR: case SO_KEEPALIVE: case SO_DONTROUTE: case SO_BROADCAST: case SO_OOBINLINE: /* AIX doesn't set len for boolean options */ len = sizeof(int); } #endif buf = ALLOCA_N(char,len); rb_io_check_closed(fptr); if (getsockopt(fptr->fd, level, option, buf, &len) < 0) rsock_sys_fail_path("getsockopt(2)", fptr->pathv); return rsock_sockopt_new(family, level, option, rb_str_new(buf, len)); }
傳回 Addrinfo
物件,用於取得 getsockname 所取得的本機地址。
請注意 addrinfo.protocol 會填入 0。
TCPSocket.open("www.ruby-lang.org", 80) {|s| p s.local_address #=> #<Addrinfo: 192.168.0.129:36873 TCP> } TCPServer.open("127.0.0.1", 1512) {|serv| p serv.local_address #=> #<Addrinfo: 127.0.0.1:1512 TCP> }
static VALUE bsock_local_address(VALUE sock) { union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); if (getsockname(fptr->fd, &buf.addr, &len) < 0) rb_sys_fail("getsockname(2)"); if (len0 < len) len = len0; return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len); }
接收訊息。
maxlen 為接收的位元組最大數量。
flags 應該是 Socket::MSG_* 常數的按位元 OR。
outbuf 僅會在方法呼叫後包含接收的資料,即使它一開始並非空白。
UNIXSocket.pair {|s1, s2| s1.puts "Hello World" p s2.recv(4) #=> "Hell" p s2.recv(4, Socket::MSG_PEEK) #=> "o Wo" p s2.recv(4) #=> "o Wo" p s2.recv(10) #=> "rld\n" }
static VALUE bsock_recv(int argc, VALUE *argv, VALUE sock) { return rsock_s_recvfrom(sock, argc, argv, RECV_RECV); }
在為基礎檔案描述詞設定 O_NONBLOCK 之後,使用 recvfrom(2) 從 socket
接收最多 maxlen 個位元組。flags 為零個或多個 MSG_
選項。結果 mesg 為接收到的資料。
當 recvfrom(2) 傳回 0 時,Socket#recv_nonblock
會傳回 nil。在多數情況下,這表示連線已關閉,但對於 UDP 連線,這可能表示已接收一個空白封包,因為基礎 API 使得無法區分這兩種情況。
參數¶ ↑
-
maxlen
- 從 socket 接收的位元組數量 -
flags
- 零個或多個MSG_
選項 -
buf
- 目的String
緩衝區 -
options
- 關鍵字 hash,支援 ‘exception: false`
範例¶ ↑
serv = TCPServer.new("127.0.0.1", 0) af, port, host, addr = serv.addr c = TCPSocket.new(addr, port) s = serv.accept c.send "aaa", 0 begin # emulate blocking recv. p s.recv_nonblock(10) #=> "aaa" rescue IO::WaitReadable IO.select([s]) retry end
如果對 recv_nonblock 的呼叫失敗,請參閱 Socket#recvfrom
以了解可能會引發的例外狀況。
BasicSocket#recv_nonblock
可能引發任何與 recvfrom(2) 失敗對應的錯誤,包括 Errno::EWOULDBLOCK。
如果例外是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,它會由 IO::WaitReadable
擴充。因此,IO::WaitReadable
可用於救援例外,以重試 recv_nonblock。
透過將關鍵字參數 exception 指定為 false
,您可以指出 recv_nonblock
不應引發 IO::WaitReadable
例外,而是傳回符號 :wait_readable
。
請參閱¶ ↑
# File ext/socket/lib/socket.rb, line 376 def recv_nonblock(len, flag = 0, str = nil, exception: true) __recv_nonblock(len, flag, str, exception) end
recvmsg 以封鎖方式使用 recvmsg(2) 系統呼叫接收訊息。
maxmesglen 是要接收的 mesg 的最大長度。
flags 是 MSG_* 常數的按位元 OR,例如 Socket::MSG_PEEK。
maxcontrollen 是要接收的控制項(附屬資料)的最大長度。
opts 是選項雜湊。目前,:scm_rights=>bool 是唯一的選項。
:scm_rights 選項指定應用程式預期 SCM_RIGHTS 控制訊息。如果值為 nil 或 false,應用程式不會預期 SCM_RIGHTS 控制訊息。在這種情況下,recvmsg 會立即關閉傳遞的檔案描述符。這是預設行為。
如果 :scm_rights 值既不是 nil 也不是 false,應用程式預期 SCM_RIGHTS 控制訊息。在這種情況下,recvmsg 會為 Socket::AncillaryData#unix_rights
方法的每個檔案描述符建立 IO
物件。
傳回值是 4 個元素的陣列。
mesg 是接收訊息的字串。
sender_addrinfo 是無連線插座的傳送者插座位址。它是一個 Addrinfo
物件。對於 TCP 等連線導向插座,sender_addrinfo 取決於平台。
rflags 是接收訊息的旗標,它是 MSG_* 常數的按位元 OR,例如 Socket::MSG_TRUNC。如果系統使用 4.3BSD 樣式的舊式 recvmsg 系統呼叫,它將為 nil。
controls 是附屬資料,它是一個 Socket::AncillaryData
物件陣列,例如
#<Socket::AncillaryData: AF_UNIX SOCKET RIGHTS 7>
maxmesglen 和 maxcontrollen 可以為 nil。在這種情況下,緩衝區會持續增加,直到訊息未被截斷。在內部,會使用 MSG_PEEK。緩衝區已滿和 MSG_CTRUNC 會檢查是否截斷。
recvmsg 可用於實作 recv_io,如下所示
mesg, sender_sockaddr, rflags, *controls = sock.recvmsg(:scm_rights=>true) controls.each {|ancdata| if ancdata.cmsg_is?(:SOCKET, :RIGHTS) return ancdata.unix_rights[0] end }
# File ext/socket/lib/socket.rb, line 431 def recvmsg(dlen = nil, flags = 0, clen = nil, scm_rights: false) __recvmsg(dlen, flags, clen, scm_rights) end
recvmsg 使用 recvmsg(2) 系統呼叫以非封鎖方式接收訊息。
它類似於 BasicSocket#recvmsg
,但會在系統呼叫之前設定非封鎖旗標,且不會重試系統呼叫。
透過將關鍵字參數 exception 指定為 false
,你可以指出 recvmsg_nonblock
不應引發 IO::WaitReadable
例外,而是傳回符號 :wait_readable
。
# File ext/socket/lib/socket.rb, line 447 def recvmsg_nonblock(dlen = nil, flags = 0, clen = nil, scm_rights: false, exception: true) __recvmsg_nonblock(dlen, flags, clen, scm_rights, exception) end
傳回一個 Addrinfo
物件,用於透過 getpeername 取得的遠端位址。
請注意 addrinfo.protocol 會填入 0。
TCPSocket.open("www.ruby-lang.org", 80) {|s| p s.remote_address #=> #<Addrinfo: 221.186.184.68:80 TCP> } TCPServer.open("127.0.0.1", 1728) {|serv| c = TCPSocket.new("127.0.0.1", 1728) s = serv.accept p s.remote_address #=> #<Addrinfo: 127.0.0.1:36504 TCP> }
static VALUE bsock_remote_address(VALUE sock) { union_sockaddr buf; socklen_t len = (socklen_t)sizeof buf; socklen_t len0 = len; rb_io_t *fptr; GetOpenFile(sock, fptr); if (getpeername(fptr->fd, &buf.addr, &len) < 0) rb_sys_fail("getpeername(2)"); if (len0 < len) len = len0; return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len); }
透過 basicsocket 傳送 mesg。
mesg 應為字串。
flags 應該是 Socket::MSG_* 常數的按位元 OR。
dest_sockaddr 應為封裝的 sockaddr 字串或 addrinfo。
TCPSocket.open("localhost", 80) {|s| s.send "GET / HTTP/1.0\r\n\r\n", 0 p s.read }
VALUE rsock_bsock_send(int argc, VALUE *argv, VALUE socket) { struct rsock_send_arg arg; VALUE flags, to; rb_io_t *fptr; rb_blocking_function_t *func; const char *funcname; rb_scan_args(argc, argv, "21", &arg.mesg, &flags, &to); StringValue(arg.mesg); if (!NIL_P(to)) { SockAddrStringValue(to); to = rb_str_new4(to); arg.to = (struct sockaddr *)RSTRING_PTR(to); arg.tolen = RSTRING_SOCKLEN(to); func = rsock_sendto_blocking; funcname = "sendto(2)"; } else { func = rsock_send_blocking; funcname = "send(2)"; } RB_IO_POINTER(socket, fptr); arg.fd = fptr->fd; arg.flags = NUM2INT(flags); while (true) { #ifdef RSOCK_WAIT_BEFORE_BLOCKING rb_io_wait(socket, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); #endif ssize_t n = (ssize_t)BLOCKING_REGION_FD(func, &arg); if (n >= 0) return SSIZET2NUM(n); if (rb_io_maybe_wait_writable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) { continue; } rb_sys_fail(funcname); } }
sendmsg 使用 sendmsg(2) 系統呼叫以封鎖方式傳送訊息。
mesg 是要傳送的字串。
flags 是 MSG_* 常數的按位元 OR,例如 Socket::MSG_OOB。
dest_sockaddr 是無連線插座的目的地插座位址。它應為 sockaddr,例如 Socket.sockaddr_in
的結果。也可以使用 Addrinfo
物件。
controls 是輔助資料的清單。controls 的元素應為 Socket::AncillaryData
或 3 個元素的陣列。3 個元素的陣列應包含 cmsg_level、cmsg_type 和資料。
傳回值 numbytes_sent 是已傳送的位元組數目。
sendmsg 可用於實作 send_io,如下所示
# use Socket::AncillaryData. ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, io.fileno) sock.sendmsg("a", 0, nil, ancdata) # use 3-element array. ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack("i!")] sock.sendmsg("\0", 0, nil, ancdata)
# File ext/socket/lib/socket.rb, line 307 def sendmsg(mesg, flags = 0, dest_sockaddr = nil, *controls) __sendmsg(mesg, flags, dest_sockaddr, controls) end
sendmsg_nonblock
使用 sendmsg(2) 系統呼叫以非封鎖方式傳送訊息。
它類似於 BasicSocket#sendmsg
,但會在系統呼叫之前設定非封鎖旗標,且不會重試系統呼叫。
透過指定關鍵字引數 exception 為 false
,你可以指示 sendmsg_nonblock
不應引發 IO::WaitWritable
例外,而是回傳符號 :wait_writable
。
# File ext/socket/lib/socket.rb, line 323 def sendmsg_nonblock(mesg, flags = 0, dest_sockaddr = nil, *controls, exception: true) __sendmsg_nonblock(mesg, flags, dest_sockaddr, controls, exception) end
設定 socket 選項。這些是通訊協定和系統特定的,請參閱你的本機系統文件以取得詳細資料。
參數¶ ↑
-
level
是整數,通常是 SOL_ 常數之一,例如 Socket::SOL_SOCKET,或通訊協定層級。名稱的字串或符號(可能沒有前置詞)也會被接受。 -
optname
是整數,通常是 SO_ 常數之一,例如 Socket::SO_REUSEADDR。名稱的字串或符號(可能沒有前置詞)也會被接受。 -
optval
是選項的值,它會傳遞給底層的 setsockopt() 作為指標指向一定數量的位元組。如何執行此操作取決於類型-
整數:值會指定給 int,並傳遞指向 int 的指標,長度為 sizeof(int)。
-
true 或 false:1 或 0 (分別) 會指定給 int,而 int 會傳遞給
Integer
。請注意,必須傳遞false
,而不是nil
。 -
字串:字串的資料和長度會傳遞給 socket。
-
-
socketoption
是Socket::Option
的實例
範例¶ ↑
有些 socket 選項是具有布林值的整數,在這種情況下,setsockopt
可以像這樣呼叫
sock.setsockopt(:SOCKET, :REUSEADDR, true) sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) sock.setsockopt(Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true))
有些 socket 選項是具有數字值的整數,在這種情況下,setsockopt
可以像這樣呼叫
sock.setsockopt(:IP, :TTL, 255) sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255) sock.setsockopt(Socket::Option.int(:INET, :IP, :TTL, 255))
選項值可能是結構。傳遞它們可能會很複雜,因為它涉及檢查你的系統標頭以確定正確的定義。一個範例是 ip_mreq
,它可能在你的系統標頭中定義為
struct ip_mreq { struct in_addr imr_multiaddr; struct in_addr imr_interface; };
在這種情況下,setsockopt
可以像這樣呼叫
optval = IPAddr.new("224.0.0.251").hton + IPAddr.new(Socket::INADDR_ANY, Socket::AF_INET).hton sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, optval)
static VALUE bsock_setsockopt(int argc, VALUE *argv, VALUE sock) { VALUE lev, optname, val; int family, level, option; rb_io_t *fptr; int i; char *v; int vlen; if (argc == 1) { lev = rb_funcall(argv[0], rb_intern("level"), 0); optname = rb_funcall(argv[0], rb_intern("optname"), 0); val = rb_funcall(argv[0], rb_intern("data"), 0); } else { rb_scan_args(argc, argv, "30", &lev, &optname, &val); } GetOpenFile(sock, fptr); family = rsock_getfamily(fptr); level = rsock_level_arg(family, lev); option = rsock_optname_arg(family, level, optname); switch (TYPE(val)) { case T_FIXNUM: i = FIX2INT(val); goto numval; case T_FALSE: i = 0; goto numval; case T_TRUE: i = 1; numval: v = (char*)&i; vlen = (int)sizeof(i); break; default: StringValue(val); v = RSTRING_PTR(val); vlen = RSTRING_SOCKLEN(val); break; } rb_io_check_closed(fptr); if (setsockopt(fptr->fd, level, option, v, vlen) < 0) rsock_sys_fail_path("setsockopt(2)", fptr->pathv); return INT2FIX(0); }
呼叫 shutdown(2) 系統呼叫。
s.shutdown(Socket::SHUT_RD) 不允許進一步讀取。
s.shutdown(Socket::SHUT_WR) 不允許進一步寫入。
s.shutdown(Socket::SHUT_RDWR) 不允許進一步讀取和寫入。
how 可以是符號或字串
-
:RD、:SHUT_RD、“RD”和“SHUT_RD”會被接受為 Socket::SHUT_RD。
-
:WR、:SHUT_WR、“WR”和“SHUT_WR”會被接受為 Socket::SHUT_WR。
-
:RDWR、:SHUT_RDWR、“RDWR”和“SHUT_RDWR”會被接受為 Socket::SHUT_RDWR。
UNIXSocket.pair
{|s1, s2|s1.puts "ping" s1.shutdown(:WR) p s2.read #=> "ping\n" s2.puts "pong" s2.close p s1.read #=> "pong\n"
}
static VALUE bsock_shutdown(int argc, VALUE *argv, VALUE sock) { VALUE howto; int how; rb_io_t *fptr; rb_scan_args(argc, argv, "01", &howto); if (howto == Qnil) how = SHUT_RDWR; else { how = rsock_shutdown_how_arg(howto); if (how != SHUT_WR && how != SHUT_RD && how != SHUT_RDWR) { rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR"); } } GetOpenFile(sock, fptr); if (shutdown(fptr->fd, how) == -1) rb_sys_fail("shutdown(2)"); return INT2FIX(0); }