Class: OpenC3::UdpReadWriteSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/openc3/io/udp_sockets.rb

Direct Known Subclasses

UdpReadSocket, UdpWriteSocket

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bind_port = 0, bind_address = "0.0.0.0", external_port = nil, external_address = nil, multicast_interface_address = nil, ttl = 1, read_multicast = true, write_multicast = true) ⇒ UdpReadWriteSocket

Returns a new instance of UdpReadWriteSocket.

Parameters:

  • bind_port (Integer[ Port to write data out from and receive data on (0 = randomly assigned)) (defaults to: 0)

    ind_port [Integer[ Port to write data out from and receive data on (0 = randomly assigned)

  • bind_address (String) (defaults to: "0.0.0.0")

    Local address to bind to (0.0.0.0 = All local addresses)

  • external_port (Integer) (defaults to: nil)

    External port to write to

  • external_address (String) (defaults to: nil)

    External host to send data to

  • multicast_interface_address (String) (defaults to: nil)

    Local outgoing interface to send multicast packets from

  • ttl (Integer) (defaults to: 1)

    Time To Live for outgoing multicast packets

  • read_multicast (Boolean) (defaults to: true)

    Whether or not to try to read from the external address as multicast

  • write_multicast (Boolean) (defaults to: true)

    Whether or not to write to the external address as multicast



41
42
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/openc3/io/udp_sockets.rb', line 41

def initialize(
  bind_port = 0,
  bind_address = "0.0.0.0",
  external_port = nil,
  external_address = nil,
  multicast_interface_address = nil,
  ttl = 1,
  read_multicast = true,
  write_multicast = true
)

  @socket = UDPSocket.new

  # Basic setup to reuse address
  @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)

  # Bind to local address and port - This sets recv port, write_src port, recv_address, and write_src_address
  @socket.bind(bind_address, bind_port) if bind_address and bind_port

  # Default send to the specified address and port
  @socket.connect(external_address, external_port) if external_address and external_port

  # Handle multicast
  if UdpReadWriteSocket.multicast?(external_address, external_port)
    if write_multicast
      # Basic setup set time to live
      @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, ttl.to_i)

      # Set outgoing interface
      @socket.setsockopt(
        Socket::IPPROTO_IP,
        Socket::IP_MULTICAST_IF,
        IPAddr.new(multicast_interface_address).hton
      ) if multicast_interface_address
    end

    # Receive messages sent to the multicast address
    if read_multicast
      multicast_interface_address = "0.0.0.0" unless multicast_interface_address
      membership = IPAddr.new(external_address).hton + IPAddr.new(multicast_interface_address).hton
      @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, membership)
    end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Defer all methods to the UDPSocket



130
131
132
# File 'lib/openc3/io/udp_sockets.rb', line 130

def method_missing(method, *args, &block)
  @socket.__send__(method, *args, &block)
end

Class Method Details

.multicast?(host, port) ⇒ Boolean

Returns Whether the hostname is multicast.

Parameters:

  • host (String)

    Machine name or IP address

  • port (String)

    Port

Returns:

  • (Boolean)

    Whether the hostname is multicast



137
138
139
140
141
# File 'lib/openc3/io/udp_sockets.rb', line 137

def self.multicast?(host, port)
  return false if host.nil? || port.nil?

  Addrinfo.udp(host, port).ipv4_multicast?
end

Instance Method Details

#read(read_timeout = nil) ⇒ Object

Parameters:

  • read_timeout (Float) (defaults to: nil)

    Time in seconds to wait for the read to complete



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/openc3/io/udp_sockets.rb', line 114

def read(read_timeout = nil)
  data = nil
  begin
    data, _ = @socket.recvfrom_nonblock(65536)
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
    result = IO.fast_select([@socket], nil, nil, read_timeout)
    if result
      retry
    else
      raise Timeout::Error, "Read Timeout"
    end
  end
  data
end

#write(data, write_timeout = 10.0) ⇒ Object

Parameters:

  • data (String)

    Binary data to send

  • write_timeout (Float) (defaults to: 10.0)

    Time in seconds to wait for the data to send



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/openc3/io/udp_sockets.rb', line 88

def write(data, write_timeout = 10.0)
  num_bytes_to_send = data.length
  total_bytes_sent  = 0
  bytes_sent = 0
  data_to_send = data

  loop do
    begin
      bytes_sent = @socket.write_nonblock(data_to_send)
    rescue Errno::EAGAIN, Errno::EWOULDBLOCK
      result = IO.fast_select(nil, [@socket], nil, write_timeout)
      if result
        retry
      else
        raise Timeout::Error, "Write Timeout"
      end
    end
    total_bytes_sent += bytes_sent
    break if total_bytes_sent >= num_bytes_to_send

    data_to_send = data[total_bytes_sent..-1]
  end
end