UDPSocket 클래스
UDPSocket 클래스
UDPSocket는 UDP/IP 소켓을 나타내는 클래스예요. UDP는 연결을 유지하지 않는 비연결형 프로토콜이라, TCP 소켓과 달리 미리 상대를 정해 놓지 않고 데이터그램(datagram) 단위로 주고받는 방식이에요. 빠르고 가벼운 대신 도착 보장이 없다는 특성이 있죠.
Public Class Methods
new([address_family]) → socket
새 UDPSocket 객체를 만들어요.
address_family는 정수, 문자열, 심볼 중 하나를 줄 수 있어요. 예: Socket::AF_INET, "AF_INET", :INET 등.
require 'socket'
UDPSocket.new #=> #<UDPSocket:fd 3>
UDPSocket.new(Socket::AF_INET6) #=> #<UDPSocket:fd 4>
각각 IPv4와 IPv6 소켓을 만들고 있죠. 아무 인자 없이 만들면 기본은 IPv4(AF_INET)예요.
Public Instance Methods
bind(host, port)
udpsocket을 host:port에 바인딩해요. 받는 쪽 소켓은 보통 이 메서드로 주소를 정해서 대기해요.
u1 = UDPSocket.new
u1.bind("127.0.0.1", 4913)
u1.send "message-to-self", 0, "127.0.0.1", 4913
p u1.recvfrom(10) #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]]
connect(host, port)
udpsocket을 host:port에 연결해요. UDP라서 실제 연결이 일어나는 건 아니지만, 이렇게 해 두면 이후 send에서 목적지 주소를 생략하고 보낼 수 있어요.
u1 = UDPSocket.new
u1.bind("127.0.0.1", 4913)
u2 = UDPSocket.new
u2.connect("127.0.0.1", 4913)
u2.send "uuuu", 0
p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
recvfrom_nonblock(maxlen [, flags [, outbuf [, options]]])
recvfrom(2)를 사용해 udpsocket에서 최대 maxlen 바이트를 받아요. 다만 내부 파일 디스크립터에 O_NONBLOCK이 설정된 상태(비블로킹)로 받아요.
flags는 MSG_ 옵션들을 0개 이상 지정할 수 있어요. 결과의 첫 번째 원소 mesg는 받은 데이터이고, 두 번째 sender_inet_addr은 보낸 쪽 주소를 나타내는 배열이에요.
recvfrom(2)가 0을 반환하면 recvfrom_nonblock은 nil을 돌려줘요. 대부분 연결이 닫힌 경우지만, 내부 API 특성상 빈 패킷을 받은 경우와 구분할 수 없기 때문에 두 경우 모두 nil이 될 수 있어요.
maxlen— 소켓에서 받을 바이트 수flags—MSG_옵션들outbuf— 받은 데이터를 담을 목적지String버퍼options— 키워드 해시로,exception: false를 지원해요
require 'socket'
s1 = UDPSocket.new
s1.bind("127.0.0.1", 0)
s2 = UDPSocket.new
s2.bind("127.0.0.1", 0)
s2.connect(*s1.addr.values_at(3,1))
s1.connect(*s2.addr.values_at(3,1))
s1.send "aaa", 0
begin # 블로킹 recvfrom을 흉내
p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]]
rescue IO::WaitReadable
IO.select([s2])
retry
end
동작 방식: 비블로킹이므로 데이터가 아직 도착하지 않았으면 Errno::EWOULDBLOCK/Errno::EAGAIN 계열 예외를 던질 수 있어요. 이 예외는 IO::WaitReadable로 확장되기 때문에, 위 예제처럼 rescue IO::WaitReadable로 잡아 IO.select로 대기한 뒤 retry하면 블로킹처럼 동작하게 만들 수 있어요.
키워드 인자 exception을 false로 주면 예외를 던지는 대신 :wait_readable 심볼을 돌려주도록 지정할 수 있어요. 실패 시 던져질 수 있는 예외에 대한 자세한 내용은 Socket#recvfrom을 참고하세요.
send(mesg, flags, host, port)
udpsocket을 통해 mesg를 보내요. flags는 Socket::MSG_* 상수들의 비트 OR이에요. host와 port는 목적지 주소예요.
u1 = UDPSocket.new
u1.bind("127.0.0.1", 4913)
u2 = UDPSocket.new
u2.send "hi", 0, "127.0.0.1", 4913
mesg, addr = u1.recvfrom(10)
u1.send mesg, 0, addr[3], addr[1]
p u2.recv(100) #=> "hi"
위 예제에서 u2가 미리 connect하지 않았기 때문에, send에 목적지(host, port)를 명시적으로 넘기고 있어요. 반면 목적지가 고정된 소켓이라면 connect 후 send "hi", 0처럼 생략할 수 있죠.