Socket
Socket
Socket 클래스는 운영체제의 기반 소켓 구현에 대한 접근을 제공해요. 프로토콜별 소켓 클래스보다 더 운영체제 특화된 기능을 쓸 수 있게 해 줘요.
출처: Ruby 3.3 API
본문
Socket::Constants 아래 정의된 상수들은 Socket 아래에도 정의돼 있어요. 예를 들어 Socket::AF_INET은 Socket::Constants::AF_INET처럼 쓸 수 있어요.
소켓이란?
소켓은 **양방향 통신 채널의 끝점(endpoint)**이에요. 소켓은 한 프로세스 안에서, 같은 머신의 프로세스들 사이에서, 서로 다른 머신 사이에서 통신할 수 있어요. 소켓에는 여러 타입이 있는데 TCPSocket, UDPSocket, UNIXSocket 등이 있지요.
소켓은 고유한 어휘를 가져요.
- domain(도메인): 프로토콜 계열.
Socket::PF_INET,Socket::PF_INET6,Socket::PF_UNIX등. - type(타입): 두 끝점 사이의 통신 타입. 보통
Socket::SOCK_STREAM,Socket::SOCK_DGRAM. - protocol(프로토콜): 보통 0. 프로토콜의 변형을 식별하는 데 쓸 수 있어요.
- hostname(호스트 이름): 네트워크 인터페이스의 식별자. 문자열(호스트명, IPv4·IPv6 주소, broadcast), 빈 문자열(
INADDR_ANY), 또는 정수(호스트 바이트 순서의 이진 주소)일 수 있어요.
빠른 시작 (Quick start)
TCPSocket, UDPSocket, UNIXSocket 같은 클래스들은 C 인터페이스에 비해 소켓 사용을 쉽게 만들어 줘요.
C 스타일로 IPv4 인터넷 소켓을 만들어 볼게요.
require 'socket'
s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
s.connect Socket.pack_sockaddr_in(80, 'example.com')
TCPSocket 클래스를 쓸 수도 있어요.
s = TCPSocket.new 'example.com', 80
간단한 서버는 이렇게 생겼어요.
require 'socket'
server = TCPServer.new 2000 # Server bound 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'
s = TCPSocket.new 'localhost', 2000
while line = s.gets # Read lines from socket
puts line # and print them
end
s.close # close socket when done
예외 처리
Ruby의 Socket 구현은 시스템 의존 구현이 만든 오류에 기반해 예외를 던져요. 그래서 메서드 문서가 Unix 기반 시스템 예외와 Windows 기반 예외를 분리해 설명돼 있어요. 특정 예외에 대한 더 많은 정보가 필요하면 Unix 매뉴얼 페이지나 Windows WinSock 참조를 보세요.
편의 메서드
소켓을 만드는 일반적인 방법은 Socket.new지만, 대부분의 경우를 위한 소켓 생성 메서드가 여러 개 있어요.
- TCP 클라이언트 소켓:
Socket.tcp,TCPSocket.open - TCP 서버 소켓:
Socket.tcp_server_loop,TCPServer.open - UNIX 클라이언트 소켓:
Socket.unix,UNIXSocket.open - UNIX 서버 소켓:
Socket.unix_server_loop,UNIXServer.open
Constants
Socket 아래엔 AF_*(주소 계열), AI_*(getaddrinfo 플래그), EAI_*(오류 코드), IFF_*(인터페이스 플래그), NI_*(getnameinfo 플래그), SOCK_*, PF_*, MSG_*, SO_* 등 방대한 상수가 정의돼 있어요. 전부 Socket::Constants에도 정의돼 있어요.
대표적인 것들:
- 주소 계열:
AF_INET(IPv4),AF_INET6(IPv6),AF_UNIX,AF_UNSPEC등. - getaddrinfo 플래그:
AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST,AI_ADDRCONFIG등. - getaddrinfo 오류:
EAI_AGAIN,EAI_NONAME,EAI_FAIL,EAI_SYSTEM등. - 소켓 타입:
SOCK_STREAM,SOCK_DGRAM,SOCK_RAW등.
Public Class Methods
accept_loop(*sockets) { |socket, client_addrinfo| ... }
주어진 소켓들을 통해 수락된 각 연결에 대해 소켓과 클라이언트 주소를 블록에 넘겨줘요. 인자는 소켓 목록이고, 각 인자는 소켓 또는 소켓 배열이에요. 이 메서드는 블록을 순차적으로 실행해요. 즉 블록이 반환하기 전까지 다음 연결이 수락되지 않아요. 그래서 여러 클라이언트를 동시에 서비스하려면 스레드 같은 동시성 메커니즘을 써야 해요.
getaddrinfo(nodename, servname[, family[, socktype[, protocol[, flags[, reverse_lookup]]]]]) → array
nodename:servname에 대한 주소 정보를 얻어요. Addrinfo.getaddrinfo가 같은 기능을 객체 지향 스타일로 제공해요. family는 :INET, :INET6 같은 주소 계열, socktype은 :STREAM, :DGRAM, :RAW 같은 소켓 타입, protocol은 계열에 정의된 프로토콜(기본 0), flags는 Socket::AI_* 상수의 비트 OR이에요.
Socket.getaddrinfo("www.ruby-lang.org", "http", nil, :STREAM)
#=> [["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68", 2, 1, 6]]
gethostbyaddr(address_string [, address_family]) → hostent
gethostbyname(hostname) → [official_hostname, alias_hostnames, address_family, *address_list]
호스트 정보를 얻는 구식 메서드들이에요. Addrinfo#getnameinfo나 Addrinfo.getaddrinfo를 대신 쓰세요. 이 메서드들은 gethostbyname()이 스레드 안전하지 않아 시간이 오래 걸리고 다른 스레드를 막을 수 있어요.
gethostname → hostname
호스트 이름을 돌려줘요.
p Socket.gethostname #=> "hal"
이 값이 gethostbyname·getaddrinfo 등으로 IP 주소로 변환될 수 있다는 보장은 없어요. 로컬 IP 주소가 필요하면 Socket.ip_address_list를 쓰세요.
getifaddrs → [ifaddr1, ...]
인터페이스 주소들의 배열을 돌려줘요. 배열 요소는 Socket::Ifaddr의 인스턴스예요. 멀티캐스트 가능 인터페이스를 찾는 데 쓸 수 있어요.
getnameinfo(sockaddr [, flags]) → [hostname, servicename]
sockaddr에 대한 이름 정보를 얻어요. sockaddr은 packed sockaddr 문자열, 3-요소 배열, 또는 4-요소 배열일 수 있어요. flags는 Socket::NI_* 상수의 비트 OR이에요.
Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"]
getservbyname(service_name) → port_number
getservbyport(port [, protocol_name]) → service
서비스 이름↔포트 번호를 변환해요.
ip_address_list → array
로컬 인터페이스 주소들의 배열을 돌려줘요.
new(domain, socktype [, protocol]) → socket
주어진 도메인·소켓 타입으로 새 소켓을 만들어요.
sockaddr_in(port, host) → sockaddr
sockaddr_un(path) → sockaddr
주소를 packed sockaddr 형태로 만들어요. (pack_sockaddr_in, pack_sockaddr_un도 같은 기능이에요.)
pair(domain, type, protocol) → [socket1, socket2]
socketpair(domain, type, protocol) → [socket1, socket2]
연결된 소켓 쌍을 만들어요. socketpair는 pair의 별칭이에요.
tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
연결된 TCP 클라이언트 소켓을 만들어요. 블록을 주면 소켓으로 블록을 호출하고 종료 시 닫아요.
tcp_server_loop(host=nil, port) { |socket, client_addrinfo| ... }
tcp_server_sockets(host=nil, port) { |sockets| ... }
TCP 서버를 만들고 연결을 반복해서 수락해요. tcp_server_loop는 각 연결을 블록으로 넘기고, tcp_server_sockets는 수신 대기 소켓들의 배열을 블록으로 넘겨요.
udp_server_loop(port) {|msg, msg_src| ... }
udp_server_loop_on(sockets) {|msg, msg_src| ... }
udp_server_recv(sockets) {|msg, msg_src| ... }
udp_server_sockets([host, ] port)
UDP 서버를 만들고 데이터그램을 반복 수신해요. msg_src는 발신자 주소예요.
unix(path) { |socket| ... }
unix_server_loop(path) { |socket, client_addrinfo| ... }
unix_server_socket(path) { |s| ... }
UNIX 도메인 소켓을 만들어요 — 클라이언트(unix), 서버 루프(unix_server_loop), 서버 소켓(unix_server_socket).
unix_socket_abstract_name?(path)
주어진 경로가 UNIX 소켓의 추상 이름인지 판별해요. (Linux abstract socket namespace)
unpack_sockaddr_in(sockaddr) → [port, ip_address]
unpack_sockaddr_un(sockaddr) → path
packed sockaddr을 풀어(언팩) 사람이 읽을 수 있는 형태로 돌려줘요.
Public Instance Methods
accept → [client_socket, client_addrinfo]
수신 연결을 수락해요.
accept_nonblock([options]) → [client_socket, client_addrinfo]
논블로킹으로 연결을 수락해요.
bind(local_sockaddr) → 0
소켓을 로컬 주소에 바인딩해요.
connect(remote_sockaddr) → 0
소켓을 원격 주소에 연결해요.
connect_nonblock(remote_sockaddr, [options]) → 0
논블로킹으로 연결해요.
ipv6only!()
IPv6 전용 모드를 켜요.
listen( int ) → 0
연결 수락을 위해 소켓을 수신 대기 상태로 만들어요.
recvfrom(maxlen) → [mesg, sender_addrinfo]
recvfrom_nonblock(maxlen[, flags[, outbuf[, opts]]]) → [mesg, sender_addrinfo]
데이터그램(또는 메시지)과 발신자 주소를 수신해요. _nonblock은 논블로킹 버전이에요.
sysaccept → [client_socket_fd, client_addrinfo]
연결을 수락하고 클라이언트 소켓의 파일 디스크립터를 돌려줘요.