類別 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
公開類別方法
建立一個新的伺服器套接字,繫結到 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); }
公開實例方法
接受一個傳入連線。它會傳回一個新的 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); }
在為基礎檔案描述符設定 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
使用指定的 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); }
傳回已接受連線的檔案描述符。
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); }