mqtt 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/NEWS.md +13 -0
- data/README.md +99 -49
- data/lib/mqtt.rb +14 -1
- data/lib/mqtt/client.rb +93 -53
- data/lib/mqtt/packet.rb +6 -6
- data/lib/mqtt/patches/string_encoding.rb +4 -0
- data/lib/mqtt/sn/packet.rb +763 -0
- data/lib/mqtt/version.rb +1 -1
- data/spec/mqtt_client_spec.rb +147 -51
- data/spec/mqtt_packet_spec.rb +67 -68
- data/spec/mqtt_sn_packet_spec.rb +1721 -0
- data/spec/zz_client_integration_spec.rb +22 -1
- metadata +22 -31
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2MwODVmNmViNWEyM2ViM2NkMDk1ODIzNzg3ZGJiM2EyYTdlZTJlMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OWI1ZGM5Yzk4OWZiZDBkMDY5ZDM5NTllMWQ3YzRhYTk3NThmNDY3Ng==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDg1ZTMxZTdkNjM2ZDk2ZmU0ZGRiYzcyNWI0ZDk1MWI1MGU3OTRmMmI3Yzkx
|
10
|
+
NmRiYzQ0NmE3NDlhNDQ1MDExNmY2OGM2NzNjYWNlMTAwODc0NDRiNzM3MDU1
|
11
|
+
YWYxYTYyNDdmZTUxNjI2YjM2ODU2ZTgxMGVhNjM1NjAwOWViY2I=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YzkwNjYyYzYxODgyYzZjZWU2MGZhMzQ0YWZhNWM3MTVhNzdjNTFjMjEzYWFj
|
14
|
+
OGMzN2NlYjNmYTdjYzVmODNhZDZmZGIzMDg4ZjBjYmY5MzljNzRlODljMjI0
|
15
|
+
NTI3OGRmZDhhODA0NDA2NDI5ZTBlYzhjZWE3MWJhNGQwYzE4OWQ=
|
data/NEWS.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
Ruby MQTT NEWS
|
2
2
|
==============
|
3
3
|
|
4
|
+
Ruby MQTT Version 0.4.0 (2016-06-27)
|
5
|
+
------------------------------------
|
6
|
+
|
7
|
+
* Added puback handling for QoS level 1
|
8
|
+
* Low-level MQTT-SN packet parsing support
|
9
|
+
* Allow certs to be set directly instead of just by file
|
10
|
+
* Allow keyphrase for certs to be passed through
|
11
|
+
* Put 'disconnect' inside an 'ensure' block
|
12
|
+
* Fix for error on publish with frozen payload
|
13
|
+
* Fix for packets always getting id 1
|
14
|
+
* Improvements to tests
|
15
|
+
|
16
|
+
|
4
17
|
Ruby MQTT Version 0.3.1 (2014-10-10)
|
5
18
|
------------------------------------
|
6
19
|
|
data/README.md
CHANGED
@@ -5,6 +5,8 @@ ruby-mqtt
|
|
5
5
|
|
6
6
|
Pure Ruby gem that implements the [MQTT] protocol, a lightweight protocol for publish/subscribe messaging.
|
7
7
|
|
8
|
+
Also includes a class for parsing and generating [MQTT-SN] packets.
|
9
|
+
|
8
10
|
|
9
11
|
Table of Contents
|
10
12
|
-----------------
|
@@ -31,22 +33,23 @@ Alternatively, to use a development snapshot from GitHub using [Bundler]:
|
|
31
33
|
Quick Start
|
32
34
|
-----------
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# Publish example
|
38
|
-
MQTT::Client.connect('test.mosquitto.org') do |c|
|
39
|
-
c.publish('topic', 'message')
|
40
|
-
end
|
41
|
-
|
42
|
-
# Subscribe example
|
43
|
-
MQTT::Client.connect('test.mosquitto.org') do |c|
|
44
|
-
# If you pass a block to the get method, then it will loop
|
45
|
-
c.get('test') do |topic,message|
|
46
|
-
puts "#{topic}: #{message}"
|
47
|
-
end
|
48
|
-
end
|
36
|
+
~~~ ruby
|
37
|
+
require 'rubygems'
|
38
|
+
require 'mqtt'
|
49
39
|
|
40
|
+
# Publish example
|
41
|
+
MQTT::Client.connect('test.mosquitto.org') do |c|
|
42
|
+
c.publish('test', 'message')
|
43
|
+
end
|
44
|
+
|
45
|
+
# Subscribe example
|
46
|
+
MQTT::Client.connect('test.mosquitto.org') do |c|
|
47
|
+
# If you pass a block to the get method, then it will loop
|
48
|
+
c.get('test') do |topic,message|
|
49
|
+
puts "#{topic}: #{message}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
~~~
|
50
53
|
|
51
54
|
|
52
55
|
Library Overview
|
@@ -56,42 +59,52 @@ Library Overview
|
|
56
59
|
|
57
60
|
A new client connection can be created by passing either a [MQTT URI], a host and port or by passing a hash of attributes.
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
~~~ ruby
|
63
|
+
client = MQTT::Client.connect('mqtt://myserver.example.com')
|
64
|
+
client = MQTT::Client.connect('mqtts://user:pass@myserver.example.com')
|
65
|
+
client = MQTT::Client.connect('myserver.example.com')
|
66
|
+
client = MQTT::Client.connect('myserver.example.com', 18830)
|
67
|
+
client = MQTT::Client.connect(:host => 'myserver.example.com', :port => 1883 ... )
|
68
|
+
~~~
|
64
69
|
|
65
70
|
TLS/SSL is not enabled by default, to enabled it, pass ```:ssl => true```:
|
66
71
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
~~~ ruby
|
73
|
+
client = MQTT::Client.connect(
|
74
|
+
:host => 'test.mosquitto.org',
|
75
|
+
:port => 8883,
|
76
|
+
:ssl => true
|
77
|
+
)
|
78
|
+
~~~
|
72
79
|
|
73
80
|
Alternatively you can create a new Client object and then configure it by setting attributes. This example shows setting up client certificate based authentication:
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
+
~~~ ruby
|
83
|
+
client = MQTT::Client.new
|
84
|
+
client.host = 'myserver.example.com'
|
85
|
+
client.ssl = true
|
86
|
+
client.cert_file = path_to('client.pem')
|
87
|
+
client.key_file = path_to('client.key')
|
88
|
+
client.ca_file = path_to('root-ca.pem')
|
89
|
+
client.connect()
|
90
|
+
~~~
|
82
91
|
|
83
92
|
The connection can either be made without the use of a block:
|
84
93
|
|
85
|
-
|
86
|
-
|
87
|
-
|
94
|
+
~~~ ruby
|
95
|
+
client = MQTT::Client.connect('test.mosquitto.org')
|
96
|
+
# perform operations
|
97
|
+
client.disconnect()
|
98
|
+
~~~
|
88
99
|
|
89
100
|
Or, if using a block, with an implicit disconnection at the end of the block.
|
90
101
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
102
|
+
~~~ ruby
|
103
|
+
MQTT::Client.connect('test.mosquitto.org') do |client|
|
104
|
+
# perform operations
|
105
|
+
end
|
106
|
+
~~~
|
107
|
+
|
95
108
|
For more information, see and list of attributes for the [MQTT::Client] class and the [MQTT::Client.connect] method.
|
96
109
|
|
97
110
|
|
@@ -99,7 +112,9 @@ For more information, see and list of attributes for the [MQTT::Client] class an
|
|
99
112
|
|
100
113
|
To send a message to a topic, use the ```publish``` method:
|
101
114
|
|
102
|
-
|
115
|
+
~~~ ruby
|
116
|
+
client.publish(topic, payload, retain=false)
|
117
|
+
~~~
|
103
118
|
|
104
119
|
The method will return once the message has been sent to the MQTT server.
|
105
120
|
|
@@ -110,9 +125,11 @@ For more information see the [MQTT::Client#publish] method.
|
|
110
125
|
|
111
126
|
You can send a subscription request to the MQTT server using the subscribe method. One or more [Topic Filters] may be passed in:
|
112
127
|
|
113
|
-
|
114
|
-
|
115
|
-
|
128
|
+
~~~ ruby
|
129
|
+
client.subscribe( 'topic1' )
|
130
|
+
client.subscribe( 'topic1', 'topic2' )
|
131
|
+
client.subscribe( 'foo/#' )
|
132
|
+
~~~
|
116
133
|
|
117
134
|
For more information see the [MQTT::Client#subscribe] method.
|
118
135
|
|
@@ -121,23 +138,52 @@ For more information see the [MQTT::Client#subscribe] method.
|
|
121
138
|
|
122
139
|
To receive a message, use the get method. This method will block until a message is available. The topic is the name of the topic the message was sent to. The message is a string:
|
123
140
|
|
124
|
-
|
141
|
+
~~~ ruby
|
142
|
+
topic,message = client.get
|
143
|
+
~~~
|
125
144
|
|
126
145
|
Alternatively, you can give the get method a block, which will be called for every message received and loop forever:
|
127
146
|
|
128
|
-
|
129
|
-
|
130
|
-
|
147
|
+
~~~ ruby
|
148
|
+
client.get do |topic,message|
|
149
|
+
# Block is executed for every message received
|
150
|
+
end
|
151
|
+
~~~
|
131
152
|
|
132
153
|
For more information see the [MQTT::Client#get] method.
|
133
154
|
|
134
155
|
|
156
|
+
### Parsing and serialising of packets ###
|
157
|
+
|
158
|
+
The parsing and serialising of MQTT and MQTT-SN packets is a separate lower-level API.
|
159
|
+
You can use it to build your own clients and servers, without using any of the rest of the
|
160
|
+
code in this gem.
|
161
|
+
|
162
|
+
~~~ ruby
|
163
|
+
# Parse a string containing a binary packet into an object
|
164
|
+
packet_obj = MQTT::Packet.parse(binary_packet)
|
165
|
+
|
166
|
+
# Write a PUBACK packet to an IO handle
|
167
|
+
ios << MQTT::Packet::Puback(:id => 20)
|
168
|
+
|
169
|
+
# Write an MQTT-SN Publish packet with QoS -1 to a UDP socket
|
170
|
+
socket = UDPSocket.new
|
171
|
+
socket.connect('localhost', MQTT::SN::DEFAULT_PORT)
|
172
|
+
socket << MQTT::SN::Packet::Publish.new(
|
173
|
+
:topic_id => 'TT',
|
174
|
+
:topic_id_type => :short,
|
175
|
+
:data => "The time is: #{Time.now}",
|
176
|
+
:qos => -1
|
177
|
+
)
|
178
|
+
socket.close
|
179
|
+
~~~
|
135
180
|
|
136
181
|
Limitations
|
137
182
|
-----------
|
138
183
|
|
139
|
-
*
|
184
|
+
* QoS 2 is not currently supported by client
|
140
185
|
* Automatic re-connects to the server are not supported
|
186
|
+
* No local persistence for packets
|
141
187
|
|
142
188
|
|
143
189
|
Resources
|
@@ -146,6 +192,7 @@ Resources
|
|
146
192
|
* API Documentation: http://rubydoc.info/gems/mqtt
|
147
193
|
* Protocol Specification v3.1.1: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
|
148
194
|
* Protocol Specification v3.1: http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html
|
195
|
+
* MQTT-SN Protocol Specification v1.2: http://mqtt.org/new/wp-content/uploads/2009/06/MQTT-SN_spec_v1.2.pdf
|
149
196
|
* MQTT Homepage: http://www.mqtt.org/
|
150
197
|
* GitHub Project: http://github.com/njh/ruby-mqtt
|
151
198
|
|
@@ -153,7 +200,7 @@ Resources
|
|
153
200
|
License
|
154
201
|
-------
|
155
202
|
|
156
|
-
The
|
203
|
+
The mqtt ruby gem is licensed under the terms of the MIT license.
|
157
204
|
See the file LICENSE for details.
|
158
205
|
|
159
206
|
|
@@ -162,11 +209,14 @@ Contact
|
|
162
209
|
|
163
210
|
* Author: Nicholas J Humfrey
|
164
211
|
* Email: njh@aelius.com
|
212
|
+
* Twitter: [@njh]
|
165
213
|
* Home Page: http://www.aelius.com/njh/
|
166
214
|
|
167
215
|
|
168
216
|
|
217
|
+
[@njh]: http://twitter.com/njh
|
169
218
|
[MQTT]: http://www.mqtt.org/
|
219
|
+
[MQTT-SN]: http://mqtt.org/2013/12/mqtt-for-sensor-networks-mqtt-sn
|
170
220
|
[Rubygems]: http://rubygems.org/
|
171
221
|
[Bundler]: http://bundler.io/
|
172
222
|
[MQTT URI]: https://github.com/mqtt/mqtt.github.io/wiki/URI-Scheme
|
data/lib/mqtt.rb
CHANGED
@@ -21,7 +21,7 @@ module MQTT
|
|
21
21
|
DEFAULT_SSL_PORT = 8883
|
22
22
|
|
23
23
|
# Super-class for other MQTT related exceptions
|
24
|
-
class Exception < Exception
|
24
|
+
class Exception < ::Exception
|
25
25
|
end
|
26
26
|
|
27
27
|
# A ProtocolException will be raised if there is a
|
@@ -39,4 +39,17 @@ module MQTT
|
|
39
39
|
autoload :Packet, 'mqtt/packet'
|
40
40
|
autoload :Proxy, 'mqtt/proxy'
|
41
41
|
|
42
|
+
# MQTT-SN
|
43
|
+
module SN
|
44
|
+
|
45
|
+
# Default port number for unencrypted connections
|
46
|
+
DEFAULT_PORT = 1883
|
47
|
+
|
48
|
+
# A ProtocolException will be raised if there is a
|
49
|
+
# problem with data received from a remote host
|
50
|
+
class ProtocolException < MQTT::Exception
|
51
|
+
end
|
52
|
+
|
53
|
+
autoload :Packet, 'mqtt/sn/packet'
|
54
|
+
end
|
42
55
|
end
|
data/lib/mqtt/client.rb
CHANGED
@@ -53,7 +53,7 @@ class MQTT::Client
|
|
53
53
|
# If the Will message should be retain by the server after it is sent
|
54
54
|
attr_accessor :will_retain
|
55
55
|
|
56
|
-
#Last ping response time
|
56
|
+
# Last ping response time
|
57
57
|
attr_reader :last_ping_response
|
58
58
|
|
59
59
|
|
@@ -174,13 +174,14 @@ class MQTT::Client
|
|
174
174
|
end
|
175
175
|
|
176
176
|
# Initialise private instance variables
|
177
|
-
@
|
178
|
-
@last_pingreq = Time.now
|
177
|
+
@last_ping_request = Time.now
|
179
178
|
@last_ping_response = Time.now
|
180
179
|
@socket = nil
|
181
180
|
@read_queue = Queue.new
|
181
|
+
@pubacks = {}
|
182
182
|
@read_thread = nil
|
183
183
|
@write_semaphore = Mutex.new
|
184
|
+
@pubacks_semaphore = Mutex.new
|
184
185
|
end
|
185
186
|
|
186
187
|
# Get the OpenSSL context, that is used if SSL/TLS is enabled
|
@@ -190,12 +191,24 @@ class MQTT::Client
|
|
190
191
|
|
191
192
|
# Set a path to a file containing a PEM-format client certificate
|
192
193
|
def cert_file=(path)
|
193
|
-
|
194
|
+
self.cert = File.read(path)
|
195
|
+
end
|
196
|
+
|
197
|
+
# PEM-format client certificate
|
198
|
+
def cert=(cert)
|
199
|
+
ssl_context.cert = OpenSSL::X509::Certificate.new(cert)
|
194
200
|
end
|
195
201
|
|
196
202
|
# Set a path to a file containing a PEM-format client private key
|
197
|
-
def key_file=(
|
198
|
-
|
203
|
+
def key_file=(*args)
|
204
|
+
path, passphrase = args.flatten
|
205
|
+
ssl_context.key = OpenSSL::PKey::RSA.new(File.open(path), passphrase)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Set to a PEM-format client private key
|
209
|
+
def key=(*args)
|
210
|
+
cert, passphrase = args.flatten
|
211
|
+
ssl_context.key = OpenSSL::PKey::RSA.new(cert, passphrase)
|
199
212
|
end
|
200
213
|
|
201
214
|
# Set a path to a file containing a PEM-format CA certificate and enable peer verification
|
@@ -287,8 +300,11 @@ class MQTT::Client
|
|
287
300
|
|
288
301
|
# If a block is given, then yield and disconnect
|
289
302
|
if block_given?
|
290
|
-
|
291
|
-
|
303
|
+
begin
|
304
|
+
yield(self)
|
305
|
+
ensure
|
306
|
+
disconnect
|
307
|
+
end
|
292
308
|
end
|
293
309
|
end
|
294
310
|
|
@@ -315,23 +331,13 @@ class MQTT::Client
|
|
315
331
|
(not @socket.nil?) and (not @socket.closed?)
|
316
332
|
end
|
317
333
|
|
318
|
-
# Send a MQTT ping message to indicate that the MQTT client is alive.
|
319
|
-
#
|
320
|
-
# Note that you will not normally need to call this method
|
321
|
-
# as it is called automatically
|
322
|
-
def ping
|
323
|
-
packet = MQTT::Packet::Pingreq.new
|
324
|
-
send_packet(packet)
|
325
|
-
@last_pingreq = Time.now
|
326
|
-
end
|
327
|
-
|
328
334
|
# Publish a message on a particular topic to the MQTT server.
|
329
335
|
def publish(topic, payload='', retain=false, qos=0)
|
330
336
|
raise ArgumentError.new("Topic name cannot be nil") if topic.nil?
|
331
337
|
raise ArgumentError.new("Topic name cannot be empty") if topic.empty?
|
332
338
|
|
333
339
|
packet = MQTT::Packet::Publish.new(
|
334
|
-
:id =>
|
340
|
+
:id => next_packet_id,
|
335
341
|
:qos => qos,
|
336
342
|
:retain => retain,
|
337
343
|
:topic => topic,
|
@@ -339,14 +345,28 @@ class MQTT::Client
|
|
339
345
|
)
|
340
346
|
|
341
347
|
# Send the packet
|
342
|
-
send_packet(packet)
|
348
|
+
res = send_packet(packet)
|
349
|
+
|
350
|
+
if packet.qos > 0
|
351
|
+
Timeout.timeout(@ack_timeout) do
|
352
|
+
while connected? do
|
353
|
+
@pubacks_semaphore.synchronize do
|
354
|
+
return res if @pubacks.delete(packet.id)
|
355
|
+
end
|
356
|
+
# FIXME: make threads communicate with each other, instead of polling
|
357
|
+
# (using a pipe and select ?)
|
358
|
+
sleep 0.01
|
359
|
+
end
|
360
|
+
end
|
361
|
+
return -1
|
362
|
+
end
|
343
363
|
end
|
344
364
|
|
345
365
|
# Send a subscribe message for one or more topics on the MQTT server.
|
346
366
|
# The topics parameter should be one of the following:
|
347
|
-
# * String: subscribe to one topic with
|
348
|
-
# * Array: subscribe to multiple topics with
|
349
|
-
# * Hash: subscribe to multiple topics where the key is the topic and the value is the
|
367
|
+
# * String: subscribe to one topic with QoS 0
|
368
|
+
# * Array: subscribe to multiple topics with QoS 0
|
369
|
+
# * Hash: subscribe to multiple topics where the key is the topic and the value is the QoS level
|
350
370
|
#
|
351
371
|
# For example:
|
352
372
|
# client.subscribe( 'a/b' )
|
@@ -356,7 +376,7 @@ class MQTT::Client
|
|
356
376
|
#
|
357
377
|
def subscribe(*topics)
|
358
378
|
packet = MQTT::Packet::Subscribe.new(
|
359
|
-
:id =>
|
379
|
+
:id => next_packet_id,
|
360
380
|
:topics => topics
|
361
381
|
)
|
362
382
|
send_packet(packet)
|
@@ -374,18 +394,13 @@ class MQTT::Client
|
|
374
394
|
# end
|
375
395
|
#
|
376
396
|
def get(topic=nil)
|
377
|
-
# Subscribe to a topic, if an argument is given
|
378
|
-
subscribe(topic) unless topic.nil?
|
379
|
-
|
380
397
|
if block_given?
|
381
|
-
|
382
|
-
loop do
|
383
|
-
packet = @read_queue.pop
|
398
|
+
get_packet(topic) do |packet|
|
384
399
|
yield(packet.topic, packet.payload)
|
385
400
|
end
|
386
401
|
else
|
387
402
|
# Wait for one packet to be available
|
388
|
-
packet =
|
403
|
+
packet = get_packet(topic)
|
389
404
|
return packet.topic, packet.payload
|
390
405
|
end
|
391
406
|
end
|
@@ -410,11 +425,15 @@ class MQTT::Client
|
|
410
425
|
if block_given?
|
411
426
|
# Loop forever!
|
412
427
|
loop do
|
413
|
-
|
428
|
+
packet = @read_queue.pop
|
429
|
+
yield(packet)
|
430
|
+
puback_packet(packet) if packet.qos > 0
|
414
431
|
end
|
415
432
|
else
|
416
433
|
# Wait for one packet to be available
|
417
|
-
|
434
|
+
packet = @read_queue.pop
|
435
|
+
puback_packet(packet) if packet.qos > 0
|
436
|
+
return packet
|
418
437
|
end
|
419
438
|
end
|
420
439
|
|
@@ -436,7 +455,7 @@ class MQTT::Client
|
|
436
455
|
|
437
456
|
packet = MQTT::Packet::Unsubscribe.new(
|
438
457
|
:topics => topics,
|
439
|
-
:id =>
|
458
|
+
:id => next_packet_id
|
440
459
|
)
|
441
460
|
send_packet(packet)
|
442
461
|
end
|
@@ -452,25 +471,9 @@ private
|
|
452
471
|
unless result.nil?
|
453
472
|
# Yes - read in the packet
|
454
473
|
packet = MQTT::Packet.read(@socket)
|
455
|
-
|
456
|
-
# Add to queue
|
457
|
-
@read_queue.push(packet)
|
458
|
-
elsif packet.class == MQTT::Packet::Pingresp
|
459
|
-
@last_ping_response = Time.now
|
460
|
-
else
|
461
|
-
# Ignore all other packets
|
462
|
-
nil
|
463
|
-
# FIXME: implement responses for QOS 1 and 2
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
# Time to send a keep-alive ping request?
|
468
|
-
if @keep_alive > 0 and Time.now > @last_pingreq + @keep_alive
|
469
|
-
ping
|
474
|
+
handle_packet packet
|
470
475
|
end
|
471
|
-
|
472
|
-
# FIXME: check we received a ping response recently?
|
473
|
-
|
476
|
+
keep_alive!
|
474
477
|
# Pass exceptions up to parent thread
|
475
478
|
rescue Exception => exp
|
476
479
|
unless @socket.nil?
|
@@ -481,6 +484,40 @@ private
|
|
481
484
|
end
|
482
485
|
end
|
483
486
|
|
487
|
+
def handle_packet(packet)
|
488
|
+
if packet.class == MQTT::Packet::Publish
|
489
|
+
# Add to queue
|
490
|
+
@read_queue.push(packet)
|
491
|
+
elsif packet.class == MQTT::Packet::Pingresp
|
492
|
+
@last_ping_response = Time.now
|
493
|
+
elsif packet.class == MQTT::Packet::Puback
|
494
|
+
@pubacks_semaphore.synchronize do
|
495
|
+
@pubacks[packet.id] = packet
|
496
|
+
end
|
497
|
+
end
|
498
|
+
# Ignore all other packets
|
499
|
+
# FIXME: implement responses for QoS 2
|
500
|
+
end
|
501
|
+
|
502
|
+
def keep_alive!
|
503
|
+
if @keep_alive > 0
|
504
|
+
response_timeout = (@keep_alive * 1.5).ceil
|
505
|
+
if Time.now >= @last_ping_request + @keep_alive
|
506
|
+
packet = MQTT::Packet::Pingreq.new
|
507
|
+
send_packet(packet)
|
508
|
+
@last_ping_request = Time.now
|
509
|
+
elsif Time.now > @last_ping_response + response_timeout
|
510
|
+
raise MQTT::ProtocolException.new(
|
511
|
+
"No Ping Response received for #{response_timeout} seconds"
|
512
|
+
)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
def puback_packet(packet)
|
518
|
+
send_packet(MQTT::Packet::Puback.new :id => packet.id)
|
519
|
+
end
|
520
|
+
|
484
521
|
# Read and check a connection acknowledgement packet
|
485
522
|
def receive_connack
|
486
523
|
Timeout.timeout(@ack_timeout) do
|
@@ -500,7 +537,7 @@ private
|
|
500
537
|
|
501
538
|
# Send a packet to server
|
502
539
|
def send_packet(data)
|
503
|
-
#
|
540
|
+
# Raise exception if we aren't connected
|
504
541
|
raise MQTT::NotConnectedException if not connected?
|
505
542
|
|
506
543
|
# Only allow one thread to write to socket at a time
|
@@ -529,6 +566,9 @@ private
|
|
529
566
|
}
|
530
567
|
end
|
531
568
|
|
569
|
+
def next_packet_id
|
570
|
+
@last_packet_id = ( @last_packet_id || 0 ).next
|
571
|
+
end
|
532
572
|
|
533
573
|
# ---- Deprecated attributes and methods ---- #
|
534
574
|
public
|