TCPServer

TCPServer (TCP/IP 서버 소켓)

TCPServerTCP/IP 서버 소켓을 나타내는 클래스예요. 클라이언트의 연결을 받아들이고(accept) 그 연결로 통신하는 역할을 해요.

출처: Ruby 3.3 API

본문

간단한 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는 각 주소에 대해 서버 소켓 생성을 시도하고 첫 번째로 성공한 것을 돌려줘요.

accept → tcpsocket

들어오는 연결을 받아들여요. 새로운 TCPSocket 객체를 돌려줘요.

TCPServer.open("127.0.0.1", 14641) {|serv|
  s = serv.accept
  s.puts Time.now
  s.close
}

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.

TCPServer#accept_nonblock이 실패하면 accept(2) 실패에 해당하는 어떤 오류도 던질 수 있어요. Errno::EWOULDBLOCK이면 재시도를 위해 IO::WaitReadable로 rescue할 수 있어요. 키워드 인자 exception: false를 주면 예외 대신 :wait_readable을 돌려주는 식으로 동작해요.

listen(int) → 0

주어진 int를 backlog(대기 큐 길이)로 사용해 연결을 기다려요. listen 호출은 소켓이 SOCK_STREAM 또는 SOCK_SEQPACKET 타입일 때만 적용돼요. backlog는 대기 중인 연결의 최대 큐 길이예요.

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)

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
}