UNIXServer 클래스

UNIXServer 클래스

UNIXServerUNIX 도메인 스트림 서버 소켓을 나타내는 클래스예요. UNIX 도메인 소켓은 네트워크가 아니라 같은 머신 안의 파일시스템 경로(path)로 프로세스 간 통신을 하는 방식이에요. 같은 호스트에서 실행되는 프로세스들끼리 통신할 때 TCP보다 가볍고 빠르게 쓸 수 있죠.

출처: Ruby 4.0 API — UNIXServer

Public Class Methods

new(path) → unixserver

path에 바인딩된 새 UNIX 서버 소켓을 만들어요.

require 'socket'

serv = UNIXServer.new("/tmp/sock")
s = serv.accept
p s.read

서버가 /tmp/sock 경로에 소켓 파일을 만들고, accept로 연결을 기다려요.

Public Instance Methods

accept → unixsocket

들어오는 연결을 받아들여요. 새 UNIXSocket 객체를 돌려주는 블로킹 메서드예요.

UNIXServer.open("/tmp/sock") {|serv|
  UNIXSocket.open("/tmp/sock") {|c|
    s = serv.accept
    s.puts "hi"
    s.close
    p c.read #=> "hi\n"
  }
}

accept는 연결이 실제로 들어올 때까지 블로킹되므로, 멀티클라이언트를 처리하려면 루프나 스레드와 함께 쓰는 경우가 많아요.

accept_nonblock([options]) → unixsocket

내부 파일 디스크립터에 O_NONBLOCK이 설정된 상태에서 accept(2)로 연결을 받아들여요. 연결이 아직 없으면 블로킹하지 않고 예외를 던질 수 있어요.

require 'socket'
serv = UNIXServer.new("/tmp/sock")
begin # 블로킹 accept를 흉내
  sock = serv.accept_nonblock
rescue IO::WaitReadable, Errno::EINTR
  IO.select([serv])
  retry
end
# sock is an accepted socket.

동작 방식: 실패 시 accept(2)에 대응하는 예외를 던질 수 있어요. Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO 예외는 IO::WaitReadable로 확장되므로, 위 코드처럼 rescue IO::WaitReadable로 잡아 재시도하는 패턴이 가능해요.

키워드 인자 exceptionfalse로 주면 예외 대신 :wait_readable 심볼을 돌려주도록 지정할 수 있어요. 자세한 예외 종류는 Socket#acceptUNIXServer#accept를 참고하세요.

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 )

플랫폼 차이: 유닉스 계열에서는 특정 포트 없이 listen을 호출 가능하지만, Windows에서는 listen 전에 반드시 bind로 소켓을 바인딩해야 해요. 백로그가 구현상의 최대치를 넘으면 구현의 최대치가 사용돼요.

유닉스 계열에서 listen이 실패하면 발생할 수 있는 시스템 예외는 다음과 같아요.

  • Errno::EBADF — 유효하지 않은 파일 디스크립터
  • Errno::EDESTADDRREQ — 로컬 주소에 바인딩되지 않았고, 프로토콜이 언바운드 소켓 리슨을 지원하지 않음
  • Errno::EINVAL — 이미 연결된 소켓
  • Errno::ENOTSOCK — 소켓이 아님
  • Errno::EOPNOTSUPP — 프로토콜이 listen을 지원하지 않음
  • Errno::EACCES — 적절한 권한이 없음
  • Errno::EINVAL — 소켓이 셧다운됨
  • Errno::ENOBUFS — 시스템 자원 부족

Windows에서는 다음과 같은 예외가 발생할 수 있어요.

  • Errno::ENETDOWN — 네트워크가 다운
  • Errno::EADDRINUSE — 로컬 주소가 이미 사용 중
  • Errno::EINPROGRESS — Windows Sockets 1.1 호출 진행 중
  • Errno::EINVALbind로 바인딩되지 않음
  • Errno::EISCONN — 이미 연결됨
  • Errno::EMFILE — 사용 가능한 소켓 디스크립터 없음
  • Errno::ENOBUFS — 버퍼 공간 부족
  • Errno::ENOTSOC — 소켓이 아님
  • Errno::EOPNOTSUPP — 소켓 타입이 listen을 지원하지 않음

sysaccept → file_descriptor

새 연결을 받아들이고, 그 연결의 **파일 디스크립터(정수)**를 돌려줘요. acceptUNIXSocket 객체를 돌려주는 것과 달리, 이 메서드는 원시 파일 디스크립터를 돌려주기 때문에 IO.new(fd)로 직접 IO 객체로 감싸야 해요.

UNIXServer.open("/tmp/sock") {|serv|
  UNIXSocket.open("/tmp/sock") {|c|
    fd = serv.sysaccept
    s = IO.new(fd)
    s.puts "hi"
    s.close
    p c.read #=> "hi\n"
  }
}