類別 TCPServer

TCPServer 代表 TCP/IP 伺服器套接字。

一個簡單的 TCP 伺服器可能看起來像

require 'socket'

server = TCPServer.new 2000 # Server bind 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'

server = TCPServer.new 2000
loop do
  Thread.start(server.accept) do |client|
    client.puts "Hello !"
    client.puts "Time is #{Time.now}"
    client.close
  end
end

公開類別方法

new([hostname,] port) → tcpserver 按一下以切換來源

建立一個新的伺服器套接字,繫結到 port

如果給定 hostname,則套接字會繫結到它。

serv = TCPServer.new("127.0.0.1", 28561)
s = serv.accept
s.puts Time.now
s.close

在內部,TCPServer.new 會呼叫 getaddrinfo() 函式來取得位址。如果 getaddrinfo() 傳回多個位址,TCPServer.new 會嘗試為每個位址建立一個伺服器套接字,並傳回第一個成功的位址。

static VALUE
tcp_svr_init(int argc, VALUE *argv, VALUE sock)
{
    VALUE hostname, port;

    rb_scan_args(argc, argv, "011", &hostname, &port);
    return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil);
}

公開實例方法

accept → tcpsocket 按一下以切換來源

接受一個傳入連線。它會傳回一個新的 TCPSocket 物件。

TCPServer.open("127.0.0.1", 14641) {|serv|
  s = serv.accept
  s.puts Time.now
  s.close
}
static VALUE
tcp_accept(VALUE server)
{
    union_sockaddr buffer;
    socklen_t length = sizeof(buffer);

    return rsock_s_accept(rb_cTCPSocket, server, &buffer.addr, &length);
}
accept_nonblock([options]) → tcpsocket 按一下以切換來源

在為基礎檔案描述符設定 O_NONBLOCK 之後,使用 accept(2) 接受傳入連線。它會傳回一個已接受的 TCPSocket,用於傳入連線。

範例

require 'socket'
serv = TCPServer.new(2202)
begin # emulate blocking accept
  sock = serv.accept_nonblock
rescue IO::WaitReadable, Errno::EINTR
  IO.select([serv])
  retry
end
# sock is an accepted socket.

請參閱 Socket#accept,以了解在呼叫 TCPServer#accept_nonblock 失敗時可能引發的例外狀況。

TCPServer#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 1319
def accept_nonblock(exception: true)
  __accept_nonblock(exception)
end
listen( int ) → 0 按一下以切換來源

使用指定的 int 作為後備,偵聽連線。只有當 socket 的類型為 SOCK_STREAM 或 SOCK_SEQPACKET 時,呼叫 listen 才有用。

參數

  • backlog - 處理中連線佇列的最大長度。

範例 1

require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
socket.bind( sockaddr )
socket.listen( 5 )

範例 2 (偵聽任意埠,僅限 unix 系統):

require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
socket.listen( 1 )

Unix 系統例外

在 unix 系統上,上述程式碼會運作,因為會在 ADDR_ANY 位址上建立一個新的 sockaddr 結構,而任意埠號則由核心處理。在 Windows 上則無法運作,因為 Windows 要求在 listen 之前,必須呼叫 bind 來繫結 socket

如果 backlog 超過實作相依的最大佇列長度,將會使用實作的最大佇列長度。

在 unix 系統上,如果呼叫 listen 失敗,可能會引發下列系統例外

  • Errno::EBADF - socket 參數不是有效的檔案描述符

  • Errno::EDESTADDRREQ - socket 未繫結到本機位址,而通訊協定不支援在未繫結的 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);
}
sysaccept → file_descriptor 按一下以切換來源

傳回已接受連線的檔案描述符。

TCPServer.open("127.0.0.1", 28561) {|serv|
  fd = serv.sysaccept
  s = IO.for_fd(fd)
  s.puts Time.now
  s.close
}
static VALUE
tcp_sysaccept(VALUE server)
{
    union_sockaddr buffer;
    socklen_t length = sizeof(buffer);

    return rsock_s_accept(0, server, &buffer.addr, &length);
}