Class: Socketry::UDP::Socket

Inherits:
Object
  • Object
show all
Includes:
Timeout
Defined in:
lib/socketry/udp/socket.rb

Overview

User Datagram Protocol sockets

Constant Summary

Constants included from Timeout

Timeout::DEFAULT_TIMEOUTS, Timeout::DEFAULT_TIMER

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Timeout

#clear_timeout, #lifetime, #set_timeout, #start_timer, #time_remaining

Constructor Details

#initialize(family: :ipv4, read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read], write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write], timer: Socketry::Timeout::DEFAULT_TIMER.new, resolver: Socketry::Resolver::DEFAULT_RESOLVER, socket_class: ::UDPSocket) ⇒ Socketry::UDP::Socket

Create a new UDP socket



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/socketry/udp/socket.rb', line 43

def initialize(
  family: :ipv4,
  read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read],
  write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write],
  timer: Socketry::Timeout::DEFAULT_TIMER.new,
  resolver: Socketry::Resolver::DEFAULT_RESOLVER,
  socket_class: ::UDPSocket
)
  case family
  when :ipv4
    @address_family = ::Socket::AF_INET
  when :ipv6
    @address_family = ::Socket::AF_INET6
  when ::Socket::AF_INET, ::Socket::AF_INET6
    @address_family = address_family
  else raise ArgumentError, "invalid address family: #{address_family.inspect}"
  end

  @socket        = socket_class.new(@address_family)
  @read_timeout  = read_timeout
  @write_timeout = write_timeout
  @resolver      = resolver

  start_timer(timer)
end

Instance Attribute Details

#read_timeoutObject (readonly)

Returns the value of attribute read_timeout.



10
11
12
# File 'lib/socketry/udp/socket.rb', line 10

def read_timeout
  @read_timeout
end

#resolverObject (readonly)

Returns the value of attribute resolver.



10
11
12
# File 'lib/socketry/udp/socket.rb', line 10

def resolver
  @resolver
end

#socket_classObject (readonly)

Returns the value of attribute socket_class.



10
11
12
# File 'lib/socketry/udp/socket.rb', line 10

def socket_class
  @socket_class
end

#write_timeoutObject (readonly)

Returns the value of attribute write_timeout.



10
11
12
# File 'lib/socketry/udp/socket.rb', line 10

def write_timeout
  @write_timeout
end

Class Method Details

.bind(remote_addr, remote_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER) ⇒ Socketry::UDP::Socket

Bind to the given address and port



29
30
31
# File 'lib/socketry/udp/socket.rb', line 29

def self.bind(remote_addr, remote_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
  from_addr(remote_addr, resolver: resolver).bind(remote_addr, remote_port)
end

.connect(remote_addr, remote_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER) ⇒ Socketry::UDP::Socket

Connect to the given address and port



36
37
38
# File 'lib/socketry/udp/socket.rb', line 36

def self.connect(remote_addr, remote_port, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
  from_addr(remote_addr, resolver: resolver).connect(remote_addr, remote_port)
end

.from_addr(remote_addr, resolver: Socketry::Resolver::DEFAULT_RESOLVER) ⇒ Socketry::UDP::Socket

Create a UDP socket matching the given socket’s address family

Parameters:

  • remote_addr (String)

    address to connect/bind to

Returns:



16
17
18
19
20
21
22
23
24
# File 'lib/socketry/udp/socket.rb', line 16

def self.from_addr(remote_addr, resolver: Socketry::Resolver::DEFAULT_RESOLVER)
  addr = resolver.resolve(remote_addr)
  if addr.ipv4?
    new(family: :ipv4)
  elsif addr.ipv6?
    new(family: :ipv6)
  else raise Socketry::AddressError, "unsupported IP address family: #{addr}"
  end
end

Instance Method Details

#bind(remote_addr, remote_port) ⇒ self

Bind to the given address and port

Returns:

  • (self)


72
73
74
75
76
77
78
# File 'lib/socketry/udp/socket.rb', line 72

def bind(remote_addr, remote_port)
  @socket.bind(@resolver.resolve(remote_addr), remote_port)
  self
rescue => ex
  # TODO: more specific exceptions
  raise Socketry::Error, ex.message, ex.backtrace
end

#connect(remote_addr, remote_port) ⇒ self

Create a new UDP socket

Returns:

  • (self)


83
84
85
86
87
88
89
# File 'lib/socketry/udp/socket.rb', line 83

def connect(remote_addr, remote_port)
  @socket.connect(@resolver.resolve(remote_addr), remote_port)
  self
rescue => ex
  # TODO: more specific exceptions
  raise Socketry::Error, ex.message, ex.backtrace
end

#recvfrom(maxlen, timeout: @read_timeout) ⇒ String

Perform a blocking receive

Returns:

  • (String)

    received data



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/socketry/udp/socket.rb', line 106

def recvfrom(maxlen, timeout: @read_timeout)
  set_timeout(timeout)

  begin
    while (result = recvfrom_nonblock(maxlen)) == :wait_readable
      next if @socket.wait_readable(time_remaining(timeout))
      raise Socketry::TimeoutError, "recvfrom timed out after #{timeout} seconds"
    end
  ensure
    clear_timeout(imeout)
  end

  result
end

#recvfrom_nonblock(maxlen) ⇒ String, :wait_readable

Perform a non-blocking receive

Returns:

  • (String, :wait_readable)

    received packet or indication to wait



94
95
96
97
98
99
100
101
# File 'lib/socketry/udp/socket.rb', line 94

def recvfrom_nonblock(maxlen)
  @socket.recvfrom_nonblock(maxlen)
rescue ::IO::WaitReadable
  :wait_readable
rescue => ex
  # TODO: more specific exceptions
  raise Socketry::Error, ex.message, ex.backtrace
end

#send(msg, host:, port:) ⇒ Object

Send data to the given host and port



122
123
124
125
126
127
# File 'lib/socketry/udp/socket.rb', line 122

def send(msg, host:, port:)
  @socket.send(msg, 0, @resolver.resolve(host), port)
rescue => ex
  # TODO: more specific exceptions
  raise Socketry::Error, ex.message, ex.backtrace
end