BasicSocket 類別

BasicSocket 是所有 Socket 類別的超級類別。

公開類別方法

do_not_reverse_lookup → true 或 false 按一下以切換原始碼

取得全域 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 = bool 按一下以切換原始碼

設定全域 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;
}
for_fd(fd) → basicsocket 按一下以切換原始碼

傳回包含檔案描述符 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;
}

公開實例方法

close_read → nil 按一下以切換原始碼

使用關閉系統呼叫禁止進一步讀取。

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;
}
close_write → nil 按一下以切換原始碼

使用關閉系統呼叫禁止進一步寫入。

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;
}
connect_address() 按一下以切換原始碼

傳回適合在本地端電腦中連線的 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
do_not_reverse_lookup → true 或 false 按一下以切換原始碼

取得 basicsocketdo_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;
}
do_not_reverse_lookup = bool 按一下以切換原始碼

設定 basicsocketdo_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;
}
getpeereid → [euid, egid] 按一下以切換來源

傳回 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
}
getpeername → sockaddr 按一下以切換來源

傳回套接字的遠端地址,格式為 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);
}
getsockname → sockaddr 按一下以切換來源

傳回套接字的本機地址,格式為 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);
}
getsockopt(level, optname) → socketoption 按一下以切換來源

取得套接字選項。這些選項與通訊協定和系統有關,請參閱您當地的系統文件以取得詳細資料。選項會傳回為 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));
}
local_address → addrinfo 按一下以切換來源

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

接收訊息。

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);
}
recv_nonblock(maxlen [, flags [, buf [, options ]]]) → mesg 按一下以切換來源

在為基礎檔案描述詞設定 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(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) → [mesg, sender_addrinfo, rflags, *controls] 按一下以切換來源

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>

maxmesglenmaxcontrollen 可以為 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_nonblock(maxdatalen=nil, flags=0, maxcontrollen=nil, opts={}) → [data, sender_addrinfo, rflags, *controls] 按一下以切換來源

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
remote_address → addrinfo 按一下以切換來源

傳回一個 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);
}
send(mesg, flags [, dest_sockaddr]) → numbytes_sent 按一下以切換來源

透過 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(mesg, flags=0, dest_sockaddr=nil, *controls) → numbytes_sent 按一下以切換來源

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(mesg, flags=0, dest_sockaddr=nil, *controls, opts={}) → numbytes_sent 按一下以切換來源

sendmsg_nonblock 使用 sendmsg(2) 系統呼叫以非封鎖方式傳送訊息。

它類似於 BasicSocket#sendmsg,但會在系統呼叫之前設定非封鎖旗標,且不會重試系統呼叫。

透過指定關鍵字引數 exceptionfalse,你可以指示 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
setsockopt(level, optname, optval) 按一下以切換來源
setsockopt(socketoption)

設定 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。

  • socketoptionSocket::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([how]) → 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);
}