Class: CoRE::CoAP::Transmission

Inherits:
Object
  • Object
show all
Defined in:
lib/core/coap/transmission.rb

Overview

Socket abstraction.

Constant Summary collapse

DEFAULT_RECV_TIMEOUT =
2

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Transmission


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/core/coap/transmission.rb', line 10

def initialize(options = {})
  @max_retransmit   = options[:max_retransmit] || 4
  @recv_timeout     = options[:recv_timeout]   || DEFAULT_RECV_TIMEOUT
  @socket           = options[:socket]

  @retransmit       = if options[:retransmit].nil?
                        true
                      else
                        !!options[:retransmit]
                      end

  if @socket
    @socket_class   = @socket.class
    @address_family = @socket.addr.first
  else
    @socket_class   = options[:socket_class]   || Celluloid::IO::UDPSocket
    @address_family = options[:address_family] || Socket::AF_INET6
    @socket         = @socket_class.new(@address_family)
  end

  # http://lists.apple.com/archives/darwin-kernel/2014/Mar/msg00012.html
  if OS.osx? && ipv6?
    ifname  = Socket.if_up?('en1') ? 'en1' : 'en0'
    ifindex = Socket.if_nametoindex(ifname)

    s = @socket.to_io rescue @socket
    s.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_IF, [ifindex].pack('i_'))
  end

  @socket
end

Instance Attribute Details

#address_familyObject (readonly)

Returns the value of attribute address_family


8
9
10
# File 'lib/core/coap/transmission.rb', line 8

def address_family
  @address_family
end

#max_retransmitObject

Returns the value of attribute max_retransmit


7
8
9
# File 'lib/core/coap/transmission.rb', line 7

def max_retransmit
  @max_retransmit
end

#recv_timeoutObject

Returns the value of attribute recv_timeout


7
8
9
# File 'lib/core/coap/transmission.rb', line 7

def recv_timeout
  @recv_timeout
end

#socketObject (readonly)

Returns the value of attribute socket


8
9
10
# File 'lib/core/coap/transmission.rb', line 8

def socket
  @socket
end

Class Method Details

.from_host(host, options = {}) ⇒ Object

Return Transmission instance with socket matching address family.


123
124
125
126
127
128
129
130
131
132
133
# File 'lib/core/coap/transmission.rb', line 123

def from_host(host, options = {})
  if IPAddr.new(host).ipv6? 
    new(options)
  else
    new(options.merge(address_family: Socket::AF_INET))
  end
# MRI throws IPAddr::InvalidAddressError, JRuby an ArgumentError
rescue ArgumentError
  host = Resolver.address(host)
  retry
end

.request(*args) ⇒ Object

Instanciate matching Transmission and perform request.


141
142
143
# File 'lib/core/coap/transmission.rb', line 141

def request(*args)
  invoke(:request, *args)
end

.send(*args) ⇒ Object

Instanciate matching Transmission and send message.


136
137
138
# File 'lib/core/coap/transmission.rb', line 136

def send(*args)
  invoke(:send, *args)
end

Instance Method Details

#ipv6?Boolean


42
43
44
# File 'lib/core/coap/transmission.rb', line 42

def ipv6?
  @address_family == Socket::AF_INET6
end

#receive(options = {}) ⇒ Object

Receive from socket and return parsed CoAP message. (ACK is sent on CON messages.)


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
# File 'lib/core/coap/transmission.rb', line 48

def receive(options = {})
  retry_count = options[:retry_count] || 0
  timeout = (options[:timeout] || @recv_timeout) ** (retry_count + 1)

  mid   = options[:mid]
  flags = mid.nil? ? 0 : Socket::MSG_PEEK

  data = Timeout.timeout(timeout) do
    @socket.recvfrom(1152, flags)
  end

  answer = CoAP.parse(data[0].force_encoding('BINARY'))

  if mid == answer.mid
    Timeout.timeout(1) { @socket.recvfrom(1152) }
  end

  if answer.tt == :con
    message = Message.new(:ack, 0, answer.mid, nil,
      {token: answer.options[:token]})

    send(message, data[1][3])
  end

  answer
end

#request(message, host, port = CoAP::PORT) ⇒ Object

Send message (retransmit if necessary) and wait for answer. Returns answer.


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/core/coap/transmission.rb', line 77

def request(message, host, port = CoAP::PORT)
  retry_count = 0
  retransmit = @retransmit && message.tt == :con

  begin
    send(message, host, port)
    response = receive(retry_count: retry_count, mid: message.mid)
  rescue Timeout::Error
    raise unless retransmit

    retry_count += 1

    if retry_count > @max_retransmit
      raise "Maximum retransmission count of #{@max_retransmit} reached."
    end

    retry unless message.tt == :non
  end

  if seperate?(response)
    response = receive(timeout: 10, mid: message.mid)
  end

  response
end

#send(message, host, port = CoAP::PORT) ⇒ Object

Send message.


104
105
106
107
108
109
110
111
# File 'lib/core/coap/transmission.rb', line 104

def send(message, host, port = CoAP::PORT)
  message = message.to_wire if message.respond_to?(:to_wire)

  # In MRI and Rubinius, the Socket::MSG_DONTWAIT option is 64.
  # It is not defined by JRuby.
  # TODO Is it really necessary?
  @socket.send(message, 64, host, port)
end