UDPSocket 클래스

UDPSocket 클래스

UDPSocket는 UDP/IP 소켓을 나타내는 클래스예요. UDP는 연결을 유지하지 않는 비연결형 프로토콜이라, TCP 소켓과 달리 미리 상대를 정해 놓지 않고 데이터그램(datagram) 단위로 주고받는 방식이에요. 빠르고 가벼운 대신 도착 보장이 없다는 특성이 있죠.

출처: Ruby 4.0 API — UDPSocket

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)

udpsockethost: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)

udpsockethost: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이 설정된 상태(비블로킹)로 받아요.

flagsMSG_ 옵션들을 0개 이상 지정할 수 있어요. 결과의 첫 번째 원소 mesg는 받은 데이터이고, 두 번째 sender_inet_addr은 보낸 쪽 주소를 나타내는 배열이에요.

recvfrom(2)가 0을 반환하면 recvfrom_nonblocknil을 돌려줘요. 대부분 연결이 닫힌 경우지만, 내부 API 특성상 빈 패킷을 받은 경우와 구분할 수 없기 때문에 두 경우 모두 nil이 될 수 있어요.

  • maxlen — 소켓에서 받을 바이트 수
  • flagsMSG_ 옵션들
  • 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하면 블로킹처럼 동작하게 만들 수 있어요.

키워드 인자 exceptionfalse로 주면 예외를 던지는 대신 :wait_readable 심볼을 돌려주도록 지정할 수 있어요. 실패 시 던져질 수 있는 예외에 대한 자세한 내용은 Socket#recvfrom을 참고하세요.

send(mesg, flags, host, port)

udpsocket을 통해 mesg를 보내요. flagsSocket::MSG_* 상수들의 비트 OR이에요. hostport는 목적지 주소예요.

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)를 명시적으로 넘기고 있어요. 반면 목적지가 고정된 소켓이라면 connectsend "hi", 0처럼 생략할 수 있죠.