ffi-rzmq 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS.txt +1 -0
- data/History.txt +35 -0
- data/README.rdoc +48 -15
- data/Rakefile +7 -2
- data/examples/README.rdoc +21 -76
- data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
- data/examples/v2api/local_lat_poll.rb +66 -0
- data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
- data/examples/v2api/publish_subscribe.rb +82 -0
- data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
- data/examples/v2api/remote_throughput.rb +39 -0
- data/examples/v2api/reqrep_poll.rb +62 -0
- data/examples/v2api/request_response.rb +40 -0
- data/examples/v2api/throughput_measurement.rb +138 -0
- data/examples/v3api/local_lat.rb +59 -0
- data/examples/v3api/local_lat_poll.rb +66 -0
- data/examples/v3api/local_throughput.rb +65 -0
- data/examples/v3api/publish_subscribe.rb +82 -0
- data/examples/v3api/remote_lat.rb +71 -0
- data/examples/v3api/remote_throughput.rb +47 -0
- data/examples/v3api/reqrep_poll.rb +62 -0
- data/examples/v3api/request_response.rb +40 -0
- data/examples/v3api/throughput_measurement.rb +166 -0
- data/ext/README +5 -0
- data/ffi-rzmq.gemspec +4 -4
- data/lib/ffi-rzmq.rb +4 -1
- data/lib/ffi-rzmq/constants.rb +178 -0
- data/lib/ffi-rzmq/context.rb +61 -45
- data/lib/ffi-rzmq/device.rb +22 -9
- data/lib/ffi-rzmq/exceptions.rb +0 -98
- data/lib/ffi-rzmq/libc.rb +19 -0
- data/lib/ffi-rzmq/libzmq.rb +188 -0
- data/lib/ffi-rzmq/message.rb +33 -40
- data/lib/ffi-rzmq/poll.rb +49 -52
- data/lib/ffi-rzmq/socket.rb +902 -392
- data/lib/ffi-rzmq/util.rb +101 -0
- data/spec/context_spec.rb +47 -21
- data/spec/device_spec.rb +78 -58
- data/spec/message_spec.rb +90 -12
- data/spec/multipart_spec.rb +162 -0
- data/spec/nonblocking_recv_spec.rb +325 -0
- data/spec/pushpull_spec.rb +95 -34
- data/spec/reqrep_spec.rb +55 -20
- data/spec/socket_spec.rb +353 -204
- data/spec/spec_helper.rb +46 -3
- data/version.txt +1 -1
- metadata +91 -66
- data/examples/local_lat_poll.rb +0 -54
- data/examples/local_lat_zerocopy.rb +0 -24
- data/examples/publish_subscribe.rb +0 -52
- data/examples/remote_lat_zerocopy.rb +0 -35
- data/examples/remote_throughput.rb +0 -27
- data/examples/reqrep_poll.rb +0 -49
- data/examples/request_response.rb +0 -23
- data/lib/ffi-rzmq/wrapper.rb +0 -121
- data/lib/ffi-rzmq/zmq.rb +0 -198
data/lib/ffi-rzmq/poll.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
|
2
2
|
module ZMQ
|
3
3
|
|
4
|
-
ZMQ_POLL_STR = 'zmq_poll'.freeze
|
5
|
-
|
6
4
|
class Poller
|
7
5
|
include ZMQ::Util
|
8
6
|
|
@@ -16,7 +14,7 @@ module ZMQ
|
|
16
14
|
@writables = []
|
17
15
|
end
|
18
16
|
|
19
|
-
# Checks each
|
17
|
+
# Checks each registered socket for selectability based on the poll items'
|
20
18
|
# registered +events+. Will block for up to +timeout+ milliseconds
|
21
19
|
# A millisecond is 1/1000 of a second, so to block for 1 second
|
22
20
|
# pass the value "1000" to #poll.
|
@@ -24,34 +22,42 @@ module ZMQ
|
|
24
22
|
# Pass "-1" or +:blocking+ for +timeout+ for this call to block
|
25
23
|
# indefinitely.
|
26
24
|
#
|
27
|
-
# May raise a ZMQ::PollError exception. This occurs when one of the
|
28
|
-
# registered sockets belongs to an application thread in another
|
29
|
-
# Context.
|
30
|
-
#
|
31
25
|
# This method will return *immediately* when there are no registered
|
32
26
|
# sockets. In that case, the +timeout+ parameter is not honored. To
|
33
27
|
# prevent a CPU busy-loop, the caller of this method should detect
|
34
28
|
# this possible condition (via #size) and throttle the call
|
35
29
|
# frequency.
|
36
30
|
#
|
31
|
+
# Returns 0 when there are no registered sockets that are readable
|
32
|
+
# or writable.
|
33
|
+
#
|
34
|
+
# Return 1 (or greater) to indicate the number of readable or writable
|
35
|
+
# sockets. These sockets should be processed using the #readables and
|
36
|
+
# #writables accessors.
|
37
|
+
#
|
38
|
+
# Returns -1 when there is an error. Use ZMQ::Util.errno to get the related
|
39
|
+
# error number.
|
40
|
+
#
|
37
41
|
def poll timeout = :blocking
|
38
42
|
unless @items.empty?
|
39
43
|
timeout = adjust timeout
|
40
44
|
items_triggered = LibZMQ.zmq_poll @items.address, @items.size, timeout
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
|
46
|
+
if Util.resultcode_ok?(items_triggered)
|
47
|
+
update_selectables
|
48
|
+
end
|
49
|
+
|
50
|
+
items_triggered
|
44
51
|
else
|
45
|
-
|
52
|
+
0
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
49
56
|
# The non-blocking version of #poll. See the #poll description for
|
50
57
|
# potential exceptions.
|
51
58
|
#
|
52
|
-
# May
|
53
|
-
#
|
54
|
-
# Context.
|
59
|
+
# May return -1 when an error is encounted. Check ZMQ::Util.errno
|
60
|
+
# to determine the underlying cause.
|
55
61
|
#
|
56
62
|
def poll_nonblock
|
57
63
|
poll 0
|
@@ -62,8 +68,6 @@ module ZMQ
|
|
62
68
|
# will only get registered at most once. Calling multiple times with
|
63
69
|
# different values for +events+ will OR the event information together.
|
64
70
|
#
|
65
|
-
# Does not raise any exceptions.
|
66
|
-
#
|
67
71
|
def register sock, events = ZMQ::POLLIN | ZMQ::POLLOUT, fd = 0
|
68
72
|
return false if (sock.nil? && fd.zero?) || events.zero?
|
69
73
|
|
@@ -84,13 +88,12 @@ module ZMQ
|
|
84
88
|
@raw_to_socket[item[:socket].address] = sock
|
85
89
|
@items << item
|
86
90
|
end
|
87
|
-
|
91
|
+
|
88
92
|
item[:events] |= events
|
89
93
|
end
|
90
94
|
|
91
|
-
# Deregister the +sock+ for +events+.
|
92
|
-
#
|
93
|
-
# Does not raise any exceptions.
|
95
|
+
# Deregister the +sock+ for +events+. When there are no events left,
|
96
|
+
# this also deletes the socket from the poll items.
|
94
97
|
#
|
95
98
|
def deregister sock, events, fd = 0
|
96
99
|
return unless sock || !fd.zero?
|
@@ -102,6 +105,9 @@ module ZMQ
|
|
102
105
|
item[:events] ^= events
|
103
106
|
|
104
107
|
delete sock if item[:events].zero?
|
108
|
+
true
|
109
|
+
else
|
110
|
+
false
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
@@ -129,33 +135,28 @@ module ZMQ
|
|
129
135
|
deregister sock, ZMQ::POLLOUT, 0
|
130
136
|
end
|
131
137
|
|
132
|
-
# Deletes the +sock+ for all subscribed events.
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
# if index = @sockets.index(sock)
|
138
|
-
# removed = @items.delete_at(index) and @sockets.delete(sock) and @raw_to_socket.delete(sock.socket.address)
|
139
|
-
# end
|
140
|
-
#
|
141
|
-
# removed
|
142
|
-
# end
|
138
|
+
# Deletes the +sock+ for all subscribed events. Called internally
|
139
|
+
# when a socket has been deregistered and has no more events
|
140
|
+
# registered anywhere.
|
141
|
+
#
|
143
142
|
def delete sock
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
143
|
+
removed_readable = deregister_readable sock
|
144
|
+
removed_writable = deregister_writable sock
|
145
|
+
|
146
|
+
if (size = @sockets.size) > 0
|
147
|
+
@sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
|
148
|
+
socket_deleted = size != @sockets.size
|
149
|
+
|
150
|
+
item_deleted = @items.delete sock
|
151
|
+
|
152
|
+
raw_deleted = @raw_to_socket.delete(sock.socket.address)
|
153
|
+
|
154
|
+
socket_deleted && item_deleted && raw_deleted
|
155
|
+
|
156
|
+
else
|
157
|
+
# return result of deregistration
|
158
|
+
removed_readable || removed_writable
|
159
|
+
end
|
159
160
|
end
|
160
161
|
|
161
162
|
def size(); @items.size; end
|
@@ -169,14 +170,10 @@ module ZMQ
|
|
169
170
|
|
170
171
|
private
|
171
172
|
|
172
|
-
def items_hash
|
173
|
-
hsh = {}
|
174
|
-
|
173
|
+
def items_hash hash
|
175
174
|
@items.each do |poll_item|
|
176
|
-
|
175
|
+
hash[@raw_to_socket[poll_item[:socket].address]] = poll_item
|
177
176
|
end
|
178
|
-
|
179
|
-
hsh
|
180
177
|
end
|
181
178
|
|
182
179
|
def update_selectables
|
data/lib/ffi-rzmq/socket.rb
CHANGED
@@ -1,16 +1,7 @@
|
|
1
1
|
|
2
2
|
module ZMQ
|
3
3
|
|
4
|
-
|
5
|
-
ZMQ_SETSOCKOPT_STR = 'zmq_setsockopt'.freeze
|
6
|
-
ZMQ_GETSOCKOPT_STR = 'zmq_getsockopt'.freeze
|
7
|
-
ZMQ_BIND_STR = 'zmq_bind'.freeze
|
8
|
-
ZMQ_CONNECT_STR = 'zmq_connect'.freeze
|
9
|
-
ZMQ_CLOSE_STR = 'zmq_close'.freeze
|
10
|
-
ZMQ_SEND_STR = 'zmq_send'.freeze
|
11
|
-
ZMQ_RECV_STR = 'zmq_recv'.freeze
|
12
|
-
|
13
|
-
class Socket
|
4
|
+
module CommonSocketBehavior
|
14
5
|
include ZMQ::Util
|
15
6
|
|
16
7
|
attr_reader :socket, :name
|
@@ -18,7 +9,39 @@ module ZMQ
|
|
18
9
|
# Allocates a socket of type +type+ for sending and receiving data.
|
19
10
|
#
|
20
11
|
# +type+ can be one of ZMQ::REQ, ZMQ::REP, ZMQ::PUB,
|
21
|
-
# ZMQ::SUB, ZMQ::PAIR, ZMQ::PULL, ZMQ::PUSH,
|
12
|
+
# ZMQ::SUB, ZMQ::PAIR, ZMQ::PULL, ZMQ::PUSH, ZMQ::XREQ, ZMQ::REP,
|
13
|
+
# ZMQ::DEALER or ZMQ::ROUTER.
|
14
|
+
#
|
15
|
+
# By default, this class uses ZMQ::Message for manual
|
16
|
+
# memory management. For automatic garbage collection of received messages,
|
17
|
+
# it is possible to override the :receiver_class to use ZMQ::ManagedMessage.
|
18
|
+
#
|
19
|
+
# sock = Socket.create(Context.create, ZMQ::REQ, :receiver_class => ZMQ::ManagedMessage)
|
20
|
+
#
|
21
|
+
# Advanced users may want to replace the receiver class with their
|
22
|
+
# own custom class. The custom class must conform to the same public API
|
23
|
+
# as ZMQ::Message.
|
24
|
+
#
|
25
|
+
# Creation of a new Socket object can return nil when socket creation
|
26
|
+
# fails.
|
27
|
+
#
|
28
|
+
# if (socket = Socket.new(context.pointer, ZMQ::REQ))
|
29
|
+
# ...
|
30
|
+
# else
|
31
|
+
# STDERR.puts "Socket creation failed"
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
def self.create context_ptr, type, opts = {:receiver_class => ZMQ::Message}
|
35
|
+
new(context_ptr, type, opts) rescue nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# To avoid rescuing exceptions, use the factory method #create for
|
39
|
+
# all socket creation.
|
40
|
+
#
|
41
|
+
# Allocates a socket of type +type+ for sending and receiving data.
|
42
|
+
#
|
43
|
+
# +type+ can be one of ZMQ::REQ, ZMQ::REP, ZMQ::PUB,
|
44
|
+
# ZMQ::SUB, ZMQ::PAIR, ZMQ::PULL, ZMQ::PUSH, ZMQ::XREQ, ZMQ::REP,
|
22
45
|
# ZMQ::DEALER or ZMQ::ROUTER.
|
23
46
|
#
|
24
47
|
# By default, this class uses ZMQ::Message for manual
|
@@ -31,26 +54,32 @@ module ZMQ
|
|
31
54
|
# own custom class. The custom class must conform to the same public API
|
32
55
|
# as ZMQ::Message.
|
33
56
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
57
|
+
# Creation of a new Socket object can raise an exception. This occurs when the
|
58
|
+
# +context_ptr+ is null or when the allocation of the 0mq socket within the
|
59
|
+
# context fails.
|
60
|
+
#
|
61
|
+
# begin
|
62
|
+
# socket = Socket.new(context.pointer, ZMQ::REQ)
|
63
|
+
# rescue ContextError => e
|
64
|
+
# # error handling
|
65
|
+
# end
|
38
66
|
#
|
39
67
|
def initialize context_ptr, type, opts = {:receiver_class => ZMQ::Message}
|
40
68
|
# users may override the classes used for receiving; class must conform to the
|
41
69
|
# same public API as ZMQ::Message
|
42
70
|
@receiver_klass = opts[:receiver_class]
|
71
|
+
|
72
|
+
context_ptr = context_ptr.pointer if context_ptr.kind_of?(ZMQ::Context)
|
43
73
|
|
44
74
|
unless context_ptr.null?
|
45
75
|
@socket = LibZMQ.zmq_socket context_ptr, type
|
46
|
-
if @socket
|
47
|
-
error_check ZMQ_SOCKET_STR, @socket.null? ? 1 : 0
|
76
|
+
if @socket && !@socket.null?
|
48
77
|
@name = SocketTypeNameMap[type]
|
49
78
|
else
|
50
|
-
raise ContextError.new
|
79
|
+
raise ContextError.new 'zmq_socket', 0, ETERM, "Socket pointer was null"
|
51
80
|
end
|
52
81
|
else
|
53
|
-
raise ContextError.new
|
82
|
+
raise ContextError.new 'zmq_socket', 0, ETERM, "Context pointer was null"
|
54
83
|
end
|
55
84
|
|
56
85
|
@sockopt_cache = {}
|
@@ -60,416 +89,897 @@ module ZMQ
|
|
60
89
|
|
61
90
|
# Set the queue options on this socket.
|
62
91
|
#
|
63
|
-
# Valid +
|
92
|
+
# Valid +name+ values that take a numeric +value+ are:
|
64
93
|
# ZMQ::HWM
|
65
|
-
# ZMQ::SWAP
|
94
|
+
# ZMQ::SWAP (version 2 only)
|
66
95
|
# ZMQ::AFFINITY
|
67
96
|
# ZMQ::RATE
|
68
97
|
# ZMQ::RECOVERY_IVL
|
69
|
-
# ZMQ::MCAST_LOOP
|
98
|
+
# ZMQ::MCAST_LOOP (version 2 only)
|
70
99
|
# ZMQ::LINGER
|
71
100
|
# ZMQ::RECONNECT_IVL
|
72
101
|
# ZMQ::BACKLOG
|
73
|
-
# ZMQ::RECOVER_IVL_MSEC
|
102
|
+
# ZMQ::RECOVER_IVL_MSEC (version 2 only)
|
103
|
+
# ZMQ::RECONNECT_IVL_MAX (version 3/4 only)
|
104
|
+
# ZMQ::MAXMSGSIZE (version 3/4 only)
|
105
|
+
# ZMQ::SNDHWM (version 3/4 only)
|
106
|
+
# ZMQ::RCVHWM (version 3/4 only)
|
107
|
+
# ZMQ::MULTICAST_HOPS (version 3/4 only)
|
108
|
+
# ZMQ::RCVTIMEO (version 3/4 only)
|
109
|
+
# ZMQ::SNDTIMEO (version 3/4 only)
|
110
|
+
# ZMQ::RCVLABEL (version 3/4 only)
|
74
111
|
#
|
75
|
-
# Valid +
|
76
|
-
# ZMQ::IDENTITY
|
112
|
+
# Valid +name+ values that take a string +value+ are:
|
113
|
+
# ZMQ::IDENTITY (version 2/3 only)
|
77
114
|
# ZMQ::SUBSCRIBE
|
78
115
|
# ZMQ::UNSUBSCRIBE
|
79
116
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
# #Context. See #ContextError.
|
83
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
117
|
+
# Returns 0 when the operation completed successfully.
|
118
|
+
# Returns -1 when this operation failed.
|
84
119
|
#
|
85
|
-
|
86
|
-
|
120
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
121
|
+
# cause.
|
122
|
+
#
|
123
|
+
# rc = socket.setsockopt(ZMQ::LINGER, 1_000)
|
124
|
+
# ZMQ::Util.resultcode_ok?(rc) ? puts("succeeded") : puts("failed")
|
125
|
+
#
|
126
|
+
def setsockopt name, value, length = nil
|
127
|
+
if long_long_option?(name)
|
128
|
+
length = 8
|
129
|
+
pointer = LibC.malloc length
|
130
|
+
pointer.write_long_long value
|
131
|
+
|
132
|
+
elsif int_option?(name)
|
133
|
+
length = 4
|
134
|
+
pointer = LibC.malloc length
|
135
|
+
pointer.write_int value
|
136
|
+
|
137
|
+
elsif string_option?(name)
|
138
|
+
length ||= value.size
|
139
|
+
|
140
|
+
# note: not checking errno for failed memory allocations :(
|
141
|
+
pointer = LibC.malloc length
|
142
|
+
pointer.write_string value
|
143
|
+
end
|
87
144
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
option_value_ptr = LibC.malloc option_len
|
93
|
-
option_value_ptr.write_long_long option_value
|
145
|
+
rc = LibZMQ.zmq_setsockopt @socket, name, pointer, length
|
146
|
+
LibC.free(pointer) unless pointer.nil? || pointer.null?
|
147
|
+
rc
|
148
|
+
end
|
94
149
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
150
|
+
# Convenience method for checking on additional message parts.
|
151
|
+
#
|
152
|
+
# Equivalent to calling Socket#getsockopt with ZMQ::RCVMORE.
|
153
|
+
#
|
154
|
+
# Warning: if the call to #getsockopt fails, this method will return
|
155
|
+
# false and swallow the error.
|
156
|
+
#
|
157
|
+
# message_parts = []
|
158
|
+
# message = Message.new
|
159
|
+
# rc = socket.recv(message)
|
160
|
+
# if ZMQ::Util.resultcode_ok?(rc)
|
161
|
+
# message_parts << message
|
162
|
+
# while more_parts?
|
163
|
+
# message = Message.new
|
164
|
+
# rc = socket.recv(message)
|
165
|
+
# message_parts.push(message) if resulcode_ok?(rc)
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
def more_parts?
|
170
|
+
array = []
|
171
|
+
rc = getsockopt ZMQ::RCVMORE, array
|
172
|
+
|
173
|
+
Util.resultcode_ok?(rc) ? array.at(0) : false
|
174
|
+
end
|
99
175
|
|
100
|
-
|
101
|
-
|
176
|
+
# Binds the socket to an +address+.
|
177
|
+
#
|
178
|
+
# socket.bind("tcp://127.0.0.1:5555")
|
179
|
+
#
|
180
|
+
def bind address
|
181
|
+
LibZMQ.zmq_bind @socket, address
|
182
|
+
end
|
102
183
|
|
103
|
-
|
104
|
-
|
105
|
-
|
184
|
+
# Connects the socket to an +address+.
|
185
|
+
#
|
186
|
+
# socket.connect("tcp://127.0.0.1:5555")
|
187
|
+
#
|
188
|
+
def connect address
|
189
|
+
rc = LibZMQ.zmq_connect @socket, address
|
190
|
+
end
|
106
191
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
192
|
+
# Closes the socket. Any unprocessed messages in queue are sent or dropped
|
193
|
+
# depending upon the value of the socket option ZMQ::LINGER.
|
194
|
+
#
|
195
|
+
# Returns 0 upon success *or* when the socket has already been closed.
|
196
|
+
# Returns -1 when the operation fails. Check ZMQ.errno for the error code.
|
197
|
+
#
|
198
|
+
# rc = socket.close
|
199
|
+
# puts("Given socket was invalid!") unless 0 == rc
|
200
|
+
#
|
201
|
+
def close
|
202
|
+
if @socket
|
203
|
+
remove_finalizer
|
204
|
+
rc = LibZMQ.zmq_close @socket
|
205
|
+
@socket = nil
|
206
|
+
release_cache
|
207
|
+
rc
|
208
|
+
else
|
209
|
+
0
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def __getsockopt__ name, array
|
217
|
+
value, length = sockopt_buffers name
|
218
|
+
|
219
|
+
rc = LibZMQ.zmq_getsockopt @socket, name, value, length
|
220
|
+
|
221
|
+
if Util.resultcode_ok?(rc)
|
222
|
+
result = if int_option?(name)
|
223
|
+
value.read_int
|
224
|
+
elsif long_long_option?(name)
|
225
|
+
value.read_long_long
|
226
|
+
elsif string_option?(name)
|
227
|
+
value.read_string(length.read_int)
|
111
228
|
end
|
112
229
|
|
113
|
-
|
114
|
-
error_check ZMQ_SETSOCKOPT_STR, result_code
|
115
|
-
ensure
|
116
|
-
LibC.free option_value_ptr unless option_value_ptr.nil? || option_value_ptr.null?
|
230
|
+
array << result
|
117
231
|
end
|
118
|
-
end
|
119
232
|
|
120
|
-
|
121
|
-
# the +option_name+ requested.
|
122
|
-
#
|
123
|
-
# Valid +option_name+ values and their return types:
|
124
|
-
# ZMQ::RCVMORE - boolean
|
125
|
-
# ZMQ::HWM - integer
|
126
|
-
# ZMQ::SWAP - integer
|
127
|
-
# ZMQ::AFFINITY - bitmap in an integer
|
128
|
-
# ZMQ::IDENTITY - string
|
129
|
-
# ZMQ::RATE - integer
|
130
|
-
# ZMQ::RECOVERY_IVL - integer
|
131
|
-
# ZMQ::MCAST_LOOP - boolean
|
132
|
-
# ZMQ::SNDBUF - integer
|
133
|
-
# ZMQ::RCVBUF - integer
|
134
|
-
# ZMQ::FD - fd in an integer
|
135
|
-
# ZMQ::EVENTS - bitmap integer
|
136
|
-
# ZMQ::LINGER - integer measured in milliseconds
|
137
|
-
# ZMQ::RECONNECT_IVL - integer measured in milliseconds
|
138
|
-
# ZMQ::BACKLOG - integer
|
139
|
-
# ZMQ::RECOVER_IVL_MSEC - integer measured in milliseconds
|
140
|
-
#
|
141
|
-
# Can raise two kinds of exceptions depending on the error.
|
142
|
-
# ContextError:: Raised when a socket operation is attempted on a terminated
|
143
|
-
# #Context. See #ContextError.
|
144
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
145
|
-
#
|
146
|
-
def getsockopt option_name
|
147
|
-
begin
|
148
|
-
option_value = FFI::MemoryPointer.new :pointer
|
149
|
-
option_length = FFI::MemoryPointer.new(:size_t) rescue FFI::MemoryPointer.new(:ulong)
|
150
|
-
|
151
|
-
unless [
|
152
|
-
TYPE, RCVMORE, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, MCAST_LOOP, IDENTITY,
|
153
|
-
SNDBUF, RCVBUF, FD, EVENTS, LINGER, RECONNECT_IVL, BACKLOG, RECOVERY_IVL_MSEC
|
154
|
-
].include? option_name
|
155
|
-
# we didn't understand the passed option argument
|
156
|
-
# will force a raise
|
157
|
-
error_check ZMQ_SETSOCKOPT_STR, -1
|
158
|
-
end
|
159
|
-
|
160
|
-
option_value, option_length = alloc_temp_sockopt_buffers option_name
|
161
|
-
|
162
|
-
result_code = LibZMQ.zmq_getsockopt @socket, option_name, option_value, option_length
|
163
|
-
error_check ZMQ_GETSOCKOPT_STR, result_code
|
164
|
-
ret = 0
|
165
|
-
|
166
|
-
case option_name
|
167
|
-
when RCVMORE, MCAST_LOOP
|
168
|
-
# boolean return
|
169
|
-
ret = option_value.read_long_long != 0
|
170
|
-
when HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF, RECOVERY_IVL_MSEC
|
171
|
-
ret = option_value.read_long_long
|
172
|
-
when TYPE, LINGER, RECONNECT_IVL, BACKLOG, FD, EVENTS
|
173
|
-
ret = option_value.read_int
|
174
|
-
when IDENTITY
|
175
|
-
ret = option_value.read_string(option_length.read_long_long)
|
176
|
-
end
|
177
|
-
|
178
|
-
ret
|
233
|
+
rc
|
179
234
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
# Closes the socket. Any unprocessed messages in queue are sent or dropped
|
223
|
-
# depending upon the value of the socket option ZMQ::LINGER.
|
224
|
-
#
|
225
|
-
def close
|
226
|
-
if @socket
|
227
|
-
remove_finalizer
|
228
|
-
result_code = LibZMQ.zmq_close @socket
|
229
|
-
error_check ZMQ_CLOSE_STR, result_code
|
230
|
-
@socket = nil
|
231
|
-
release_cache
|
235
|
+
|
236
|
+
# Calls to ZMQ.getsockopt require us to pass in some pointers. We can cache and save those buffers
|
237
|
+
# for subsequent calls. This is a big perf win for calling RCVMORE which happens quite often.
|
238
|
+
# Cannot save the buffer for the IDENTITY.
|
239
|
+
def sockopt_buffers name
|
240
|
+
if long_long_option?(name)
|
241
|
+
# int64_t or uint64_t
|
242
|
+
unless @sockopt_cache[:int64]
|
243
|
+
length = FFI::MemoryPointer.new :size_t
|
244
|
+
length.write_int 8
|
245
|
+
@sockopt_cache[:int64] = [FFI::MemoryPointer.new(:int64), length]
|
246
|
+
end
|
247
|
+
|
248
|
+
@sockopt_cache[:int64]
|
249
|
+
|
250
|
+
elsif int_option?(name)
|
251
|
+
# int, 0mq assumes int is 4-bytes
|
252
|
+
unless @sockopt_cache[:int32]
|
253
|
+
length = FFI::MemoryPointer.new :size_t
|
254
|
+
length.write_int 4
|
255
|
+
@sockopt_cache[:int32] = [FFI::MemoryPointer.new(:int32), length]
|
256
|
+
end
|
257
|
+
|
258
|
+
@sockopt_cache[:int32]
|
259
|
+
|
260
|
+
elsif string_option?(name)
|
261
|
+
length = FFI::MemoryPointer.new :size_t
|
262
|
+
# could be a string of up to 255 bytes
|
263
|
+
length.write_int 255
|
264
|
+
[FFI::MemoryPointer.new(255), length]
|
265
|
+
|
266
|
+
else
|
267
|
+
# uh oh, someone passed in an unknown option; use a slop buffer
|
268
|
+
unless @sockopt_cache[:unknown]
|
269
|
+
length = FFI::MemoryPointer.new :size_t
|
270
|
+
length.write_int 4
|
271
|
+
@sockopt_cache[:unknown] = [FFI::MemoryPointer.new(:int32), length]
|
272
|
+
end
|
273
|
+
|
274
|
+
@sockopt_cache[:unknown]
|
275
|
+
end
|
232
276
|
end
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
# same public API as #Message.
|
237
|
-
#
|
238
|
-
# +flags+ may take two values:
|
239
|
-
# * 0 (default) - blocking operation
|
240
|
-
# * ZMQ::NOBLOCK - non-blocking operation
|
241
|
-
# * ZMQ::SNDMORE - this message is part of a multi-part message
|
242
|
-
#
|
243
|
-
# Returns true when the message was successfully enqueued.
|
244
|
-
# Returns false under two conditions.
|
245
|
-
# 1. The message could not be enqueued
|
246
|
-
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
247
|
-
#
|
248
|
-
# The application code is responsible for handling the +message+ object
|
249
|
-
# lifecycle when #send returns.
|
250
|
-
#
|
251
|
-
# Can raise two kinds of exceptions depending on the error.
|
252
|
-
# ContextError:: Raised when a socket operation is attempted on a terminated
|
253
|
-
# #Context. See #ContextError.
|
254
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
255
|
-
#
|
256
|
-
def send message, flags = 0
|
257
|
-
begin
|
258
|
-
result_code = LibZMQ.zmq_send @socket, message.address, flags
|
259
|
-
|
260
|
-
# when the flag isn't set, do a normal error check
|
261
|
-
# when set, check to see if the message was successfully queued
|
262
|
-
queued = noblock?(flags) ? error_check_nonblock(result_code) : error_check(ZMQ_SEND_STR, result_code)
|
277
|
+
|
278
|
+
def supported_option? name
|
279
|
+
int_option?(name) || long_long_option?(name) || string_option?(name)
|
263
280
|
end
|
264
281
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
# +flags+ may be ZMQ::NOBLOCK.
|
273
|
-
#
|
274
|
-
# Can raise two kinds of exceptions depending on the error.
|
275
|
-
# ContextError:: Raised when a socket operation is attempted on a terminated
|
276
|
-
# #Context. See #ContextError.
|
277
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
278
|
-
#
|
279
|
-
def send_string message_string, flags = 0
|
280
|
-
message = Message.new message_string
|
281
|
-
result_code = send_and_close message, flags
|
282
|
-
|
283
|
-
result_code
|
284
|
-
end
|
285
|
-
|
286
|
-
# Send a sequence of strings as a multipart message out of the +parts+
|
287
|
-
# passed in for transmission. Every element of +parts+ should be
|
288
|
-
# a String.
|
289
|
-
#
|
290
|
-
# +flags+ may be ZMQ::NOBLOCK.
|
291
|
-
#
|
292
|
-
# Raises the same exceptions as Socket#send.
|
293
|
-
#
|
294
|
-
def send_strings parts, flags = 0
|
295
|
-
return false if !parts || parts.empty?
|
296
|
-
|
297
|
-
parts[0...-1].each do |part|
|
298
|
-
return false unless send_string part, flags | ZMQ::SNDMORE
|
282
|
+
def int_option? name
|
283
|
+
EVENTS == name ||
|
284
|
+
LINGER == name ||
|
285
|
+
RECONNECT_IVL == name ||
|
286
|
+
FD == name ||
|
287
|
+
TYPE == name ||
|
288
|
+
BACKLOG == name
|
299
289
|
end
|
300
290
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
# Sends a message. This will automatically close the +message+ for both successful
|
305
|
-
# and failed sends.
|
306
|
-
#
|
307
|
-
# Raises the same exceptions as Socket#send
|
308
|
-
#
|
309
|
-
def send_and_close message, flags = 0
|
310
|
-
begin
|
311
|
-
result_code = send message, flags
|
312
|
-
ensure
|
313
|
-
message.close
|
291
|
+
def string_option? name
|
292
|
+
SUBSCRIBE == name ||
|
293
|
+
UNSUBSCRIBE == name
|
314
294
|
end
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
#
|
320
|
-
# +flags+ may take two values:
|
321
|
-
# 0 (default) - blocking operation
|
322
|
-
# ZMQ::NOBLOCK - non-blocking operation
|
323
|
-
#
|
324
|
-
# Returns a true when it successfully dequeues one from the queue. Also, the +message+
|
325
|
-
# object is populated by the library with a data buffer containing the received
|
326
|
-
# data.
|
327
|
-
#
|
328
|
-
# Returns nil when a message could not be dequeued *and* +flags+ is set
|
329
|
-
# with ZMQ::NOBLOCK. The +message+ object is not modified in this situation.
|
330
|
-
#
|
331
|
-
# The application code is *not* responsible for handling the +message+ object lifecycle
|
332
|
-
# when #recv raises an exception. The #recv method takes ownership of the
|
333
|
-
# +message+ and its associated buffers. A failed call will
|
334
|
-
# release the data buffers assigned to the +message+.
|
335
|
-
#
|
336
|
-
# Can raise two kinds of exceptions depending on the error.
|
337
|
-
# ContextError:: Raised when a socket operation is attempted on a terminated
|
338
|
-
# #Context. See #ContextError.
|
339
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
340
|
-
#
|
341
|
-
def recv message, flags = 0
|
342
|
-
begin
|
343
|
-
dequeued = _recv message, flags
|
344
|
-
rescue ZeroMQError
|
345
|
-
message.close
|
346
|
-
raise
|
295
|
+
|
296
|
+
def long_long_option? name
|
297
|
+
RCVMORE == name ||
|
298
|
+
AFFINITY == name
|
347
299
|
end
|
348
300
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
# Helper method to make a new #Message instance and convert its payload
|
353
|
-
# to a string.
|
354
|
-
#
|
355
|
-
# +flags+ may be ZMQ::NOBLOCK.
|
356
|
-
#
|
357
|
-
# Can raise two kinds of exceptions depending on the error.
|
358
|
-
# ContextError:: Raised when a socket operation is attempted on a terminated
|
359
|
-
# #Context. See #ContextError.
|
360
|
-
# SocketError:: See all of the possibilities in the docs for #SocketError.
|
361
|
-
#
|
362
|
-
def recv_string flags = 0
|
363
|
-
message = @receiver_klass.new
|
364
|
-
|
365
|
-
begin
|
366
|
-
dequeued = _recv message, flags
|
367
|
-
|
368
|
-
if dequeued
|
369
|
-
message.copy_out_string
|
370
|
-
else
|
371
|
-
nil
|
372
|
-
end
|
373
|
-
ensure
|
374
|
-
message.close
|
301
|
+
def unsupported_setsock_option? name
|
302
|
+
RCVMORE == name
|
375
303
|
end
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
# +flags+ may be ZMQ::NOBLOCK.
|
381
|
-
#
|
382
|
-
# Raises the same exceptions as Socket#recv.
|
383
|
-
#
|
384
|
-
def recv_strings flags = 0
|
385
|
-
parts = []
|
386
|
-
parts << recv_string(flags)
|
387
|
-
parts << recv_string(flags) while more_parts?
|
388
|
-
parts
|
389
|
-
end
|
390
|
-
|
391
|
-
private
|
392
|
-
|
393
|
-
def noblock? flag
|
394
|
-
(NOBLOCK & flag) == NOBLOCK
|
395
|
-
end
|
396
|
-
|
397
|
-
def _recv message, flags = 0
|
398
|
-
result_code = LibZMQ.zmq_recv @socket, message.address, flags
|
399
|
-
|
400
|
-
if noblock?(flags)
|
401
|
-
error_check_nonblock(result_code)
|
402
|
-
else
|
403
|
-
error_check(ZMQ_RECV_STR, result_code)
|
304
|
+
|
305
|
+
def unsupported_getsock_option? name
|
306
|
+
UNSUBSCRIBE == name ||
|
307
|
+
SUBSCRIBE == name
|
404
308
|
end
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
# for subsequent calls. This is a big perf win for calling RCVMORE which happens quite often.
|
409
|
-
# Cannot save the buffer for the IDENTITY.
|
410
|
-
def alloc_temp_sockopt_buffers option_name
|
411
|
-
case option_name
|
412
|
-
when RCVMORE, MCAST_LOOP, HWM, SWAP, AFFINITY, RATE, RECOVERY_IVL, SNDBUF, RCVBUF, RECOVERY_IVL_MSEC
|
413
|
-
# int64_t
|
414
|
-
unless @sockopt_cache[:int64]
|
415
|
-
length = FFI::MemoryPointer.new :int64
|
416
|
-
length.write_long_long 8
|
417
|
-
@sockopt_cache[:int64] = [FFI::MemoryPointer.new(:int64), length]
|
418
|
-
end
|
419
|
-
@sockopt_cache[:int64]
|
420
|
-
|
421
|
-
when TYPE, LINGER, RECONNECT_IVL, BACKLOG, FD, EVENTS
|
422
|
-
# int, 0mq assumes int is 4-bytes
|
423
|
-
unless @sockopt_cache[:int32]
|
424
|
-
length = FFI::MemoryPointer.new :int32
|
425
|
-
length.write_int 4
|
426
|
-
@sockopt_cache[:int32] = [FFI::MemoryPointer.new(:int32), length]
|
427
|
-
end
|
428
|
-
@sockopt_cache[:int32]
|
429
|
-
|
430
|
-
when IDENTITY
|
431
|
-
length = FFI::MemoryPointer.new :int64
|
432
|
-
# could be a string of up to 255 bytes
|
433
|
-
length.write_long_long 255
|
434
|
-
[FFI::MemoryPointer.new(255), length]
|
309
|
+
|
310
|
+
def release_cache
|
311
|
+
@sockopt_cache.clear
|
435
312
|
end
|
436
|
-
end
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
313
|
+
end # module CommonSocketBehavior
|
314
|
+
|
315
|
+
|
316
|
+
module IdentitySupport
|
317
|
+
|
318
|
+
# Convenience method for getting the value of the socket IDENTITY.
|
319
|
+
#
|
320
|
+
def identity
|
321
|
+
array = []
|
322
|
+
getsockopt IDENTITY, array
|
323
|
+
array.at(0)
|
446
324
|
end
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
# require a minimum of 0mq 2.1.0 to support socket finalizers; it contains important
|
454
|
-
# fixes for sockets and threads so that a garbage collector thread can successfully
|
455
|
-
# reap this resource without crashing
|
456
|
-
if Util.minimum_api?([2, 1, 0])
|
457
|
-
def define_finalizer
|
458
|
-
ObjectSpace.define_finalizer(self, self.class.close(@socket))
|
325
|
+
|
326
|
+
# Convenience method for setting the value of the socket IDENTITY.
|
327
|
+
#
|
328
|
+
def identity=(value)
|
329
|
+
setsockopt IDENTITY, value.to_s
|
459
330
|
end
|
460
|
-
|
461
|
-
|
462
|
-
|
331
|
+
|
332
|
+
|
333
|
+
private
|
334
|
+
|
335
|
+
def string_option? name
|
336
|
+
super ||
|
337
|
+
IDENTITY == name
|
463
338
|
end
|
464
|
-
end
|
339
|
+
end # module IdentitySupport
|
340
|
+
|
341
|
+
|
342
|
+
if LibZMQ.version2?
|
343
|
+
|
344
|
+
class Socket
|
345
|
+
# Inclusion order is *important* since later modules may have a call
|
346
|
+
# to #super. We want those calls to go up the chain in a particular
|
347
|
+
# order
|
348
|
+
include CommonSocketBehavior
|
349
|
+
include IdentitySupport
|
350
|
+
|
351
|
+
# Get the options set on this socket.
|
352
|
+
#
|
353
|
+
# +name+ determines the socket option to request
|
354
|
+
# +array+ should be an empty array; a result of the proper type
|
355
|
+
# (numeric, string, boolean) will be inserted into
|
356
|
+
# the first position.
|
357
|
+
#
|
358
|
+
# Valid +option_name+ values:
|
359
|
+
# ZMQ::RCVMORE - true or false
|
360
|
+
# ZMQ::HWM - integer
|
361
|
+
# ZMQ::SWAP - integer
|
362
|
+
# ZMQ::AFFINITY - bitmap in an integer
|
363
|
+
# ZMQ::IDENTITY - string
|
364
|
+
# ZMQ::RATE - integer
|
365
|
+
# ZMQ::RECOVERY_IVL - integer
|
366
|
+
# ZMQ::MCAST_LOOP - true or false
|
367
|
+
# ZMQ::SNDBUF - integer
|
368
|
+
# ZMQ::RCVBUF - integer
|
369
|
+
# ZMQ::FD - fd in an integer
|
370
|
+
# ZMQ::EVENTS - bitmap integer
|
371
|
+
# ZMQ::LINGER - integer measured in milliseconds
|
372
|
+
# ZMQ::RECONNECT_IVL - integer measured in milliseconds
|
373
|
+
# ZMQ::BACKLOG - integer
|
374
|
+
# ZMQ::RECOVER_IVL_MSEC - integer measured in milliseconds
|
375
|
+
#
|
376
|
+
# Returns 0 when the operation completed successfully.
|
377
|
+
# Returns -1 when this operation failed.
|
378
|
+
#
|
379
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
380
|
+
# cause.
|
381
|
+
#
|
382
|
+
# # retrieve high water mark
|
383
|
+
# array = []
|
384
|
+
# rc = socket.getsockopt(ZMQ::HWM, array)
|
385
|
+
# hwm = array.first if ZMQ::Util.resultcode_ok?(rc)
|
386
|
+
#
|
387
|
+
def getsockopt name, array
|
388
|
+
rc = __getsockopt__ name, array
|
389
|
+
|
390
|
+
if Util.resultcode_ok?(rc) && (RCVMORE == name || MCAST_LOOP == name)
|
391
|
+
# convert to boolean
|
392
|
+
array[0] = 1 == array[0]
|
393
|
+
end
|
394
|
+
|
395
|
+
rc
|
396
|
+
end
|
397
|
+
|
398
|
+
# Queues the message for transmission. Message is assumed to conform to the
|
399
|
+
# same public API as #Message.
|
400
|
+
#
|
401
|
+
# +flags+ may take two values:
|
402
|
+
# * 0 (default) - blocking operation
|
403
|
+
# * ZMQ::NOBLOCK - non-blocking operation
|
404
|
+
# * ZMQ::SNDMORE - this message is part of a multi-part message
|
405
|
+
#
|
406
|
+
# Returns 0 when the message was successfully enqueued.
|
407
|
+
# Returns -1 under two conditions.
|
408
|
+
# 1. The message could not be enqueued
|
409
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
410
|
+
#
|
411
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
412
|
+
# cause.
|
413
|
+
#
|
414
|
+
# The application code is responsible for handling the +message+ object
|
415
|
+
# lifecycle when #send returns. Regardless of the return code, the user
|
416
|
+
# is responsible for calling message.close to free the memory in use.
|
417
|
+
#
|
418
|
+
def send message, flags = 0
|
419
|
+
LibZMQ.zmq_send @socket, message.address, flags
|
420
|
+
end
|
421
|
+
|
422
|
+
# Helper method to make a new #Message instance out of the +message_string+ passed
|
423
|
+
# in for transmission.
|
424
|
+
#
|
425
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
426
|
+
#
|
427
|
+
# Returns 0 when the message was successfully enqueued.
|
428
|
+
# Returns -1 under two conditions.
|
429
|
+
# 1. The message could not be enqueued
|
430
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
431
|
+
#
|
432
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
433
|
+
# cause.
|
434
|
+
#
|
435
|
+
def send_string message_string, flags = 0
|
436
|
+
message = Message.new message_string
|
437
|
+
send_and_close message, flags
|
438
|
+
end
|
439
|
+
|
440
|
+
# Send a sequence of strings as a multipart message out of the +parts+
|
441
|
+
# passed in for transmission. Every element of +parts+ should be
|
442
|
+
# a String.
|
443
|
+
#
|
444
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
445
|
+
#
|
446
|
+
# Returns 0 when the message was successfully enqueued.
|
447
|
+
# Returns -1 under two conditions.
|
448
|
+
# 1. The message could not be enqueued
|
449
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
450
|
+
#
|
451
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
452
|
+
# cause.
|
453
|
+
#
|
454
|
+
def send_strings parts, flags = 0
|
455
|
+
return -1 if !parts || parts.empty?
|
456
|
+
|
457
|
+
parts[0..-2].each do |part|
|
458
|
+
rc = send_string part, flags | ZMQ::SNDMORE
|
459
|
+
return rc unless Util.resultcode_ok?(rc)
|
460
|
+
end
|
461
|
+
|
462
|
+
send_string parts[-1], flags
|
463
|
+
end
|
464
|
+
|
465
|
+
# Send a sequence of messages as a multipart message out of the +parts+
|
466
|
+
# passed in for transmission. Every element of +parts+ should be
|
467
|
+
# a Message (or subclass).
|
468
|
+
#
|
469
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
470
|
+
#
|
471
|
+
# Returns 0 when the message was successfully enqueued.
|
472
|
+
# Returns -1 under two conditions.
|
473
|
+
# 1. The message could not be enqueued
|
474
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
475
|
+
#
|
476
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
477
|
+
# cause.
|
478
|
+
#
|
479
|
+
def sendmsgs parts, flags = 0
|
480
|
+
return -1 if !parts || parts.empty?
|
481
|
+
|
482
|
+
parts[0..-2].each do |part|
|
483
|
+
rc = send part, flags | ZMQ::SNDMORE
|
484
|
+
return rc unless Util.resultcode_ok?(rc)
|
485
|
+
end
|
486
|
+
|
487
|
+
send parts[-1], flags
|
488
|
+
end
|
489
|
+
|
490
|
+
# Sends a message. This will automatically close the +message+ for both successful
|
491
|
+
# and failed sends.
|
492
|
+
#
|
493
|
+
# Returns 0 when the message was successfully enqueued.
|
494
|
+
# Returns -1 under two conditions.
|
495
|
+
# 1. The message could not be enqueued
|
496
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
497
|
+
#
|
498
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
499
|
+
# cause.
|
500
|
+
#
|
501
|
+
def send_and_close message, flags = 0
|
502
|
+
rc = send message, flags
|
503
|
+
message.close
|
504
|
+
rc
|
505
|
+
end
|
506
|
+
|
507
|
+
# Dequeues a message from the underlying queue. By default, this is a blocking operation.
|
508
|
+
#
|
509
|
+
# +flags+ may take two values:
|
510
|
+
# 0 (default) - blocking operation
|
511
|
+
# ZMQ::NOBLOCK - non-blocking operation
|
512
|
+
#
|
513
|
+
# Returns 0 when the message was successfully dequeued.
|
514
|
+
# Returns -1 under two conditions.
|
515
|
+
# 1. The message could not be dequeued
|
516
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
517
|
+
#
|
518
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
519
|
+
# cause.
|
520
|
+
#
|
521
|
+
# The application code is responsible for handling the +message+ object lifecycle
|
522
|
+
# when #recv returns an error code.
|
523
|
+
#
|
524
|
+
def recv message, flags = 0
|
525
|
+
LibZMQ.zmq_recv @socket, message.address, flags
|
526
|
+
end
|
527
|
+
|
528
|
+
# Converts the received message to a string and replaces the +string+ arg
|
529
|
+
# contents.
|
530
|
+
#
|
531
|
+
# +string+ should be an empty string, .e.g. ''
|
532
|
+
# +flags+ may be ZMQ::NOBLOCK.
|
533
|
+
#
|
534
|
+
# Returns 0 when the message was successfully dequeued.
|
535
|
+
# Returns -1 under two conditions.
|
536
|
+
# 1. The message could not be dequeued
|
537
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
538
|
+
#
|
539
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
540
|
+
# cause.
|
541
|
+
#
|
542
|
+
def recv_string string, flags = 0
|
543
|
+
message = @receiver_klass.new
|
544
|
+
rc = recv message, flags
|
545
|
+
string.replace(message.copy_out_string) if Util.resultcode_ok?(rc)
|
546
|
+
message.close
|
547
|
+
rc
|
548
|
+
end
|
549
|
+
|
550
|
+
# Receive a multipart message as a list of strings.
|
551
|
+
#
|
552
|
+
# +list+ should be an object that responds to #append or #<< so received
|
553
|
+
# strings can be appended to it
|
554
|
+
# +flag+ may be ZMQ::NOBLOCK. Any other flag will be removed
|
555
|
+
#
|
556
|
+
# Returns 0 when all messages were successfully dequeued.
|
557
|
+
# Returns -1 under two conditions.
|
558
|
+
# 1. A message could not be dequeued
|
559
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
560
|
+
#
|
561
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
562
|
+
# cause. Also, the +list+ will not be modified when there was an error.
|
563
|
+
#
|
564
|
+
def recv_strings list, flag = 0
|
565
|
+
array = []
|
566
|
+
rc = recvmsgs array, flag
|
567
|
+
|
568
|
+
if Util.resultcode_ok?(rc)
|
569
|
+
array.each do |message|
|
570
|
+
list << message.copy_out_string
|
571
|
+
message.close
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
rc
|
576
|
+
end
|
577
|
+
|
578
|
+
# Receive a multipart message as an array of objects
|
579
|
+
# (by default these are instances of Message).
|
580
|
+
#
|
581
|
+
# +list+ should be an object that responds to #append or #<< so received
|
582
|
+
# messages can be appended to it
|
583
|
+
# +flag+ may be ZMQ::NOBLOCK. Any other flag will be
|
584
|
+
# removed.
|
585
|
+
#
|
586
|
+
# Returns 0 when all messages were successfully dequeued.
|
587
|
+
# Returns -1 under two conditions.
|
588
|
+
# 1. A message could not be dequeued
|
589
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
590
|
+
#
|
591
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
592
|
+
# cause. Also, the +list+ will not be modified when there was an error.
|
593
|
+
#
|
594
|
+
def recvmsgs list, flag = 0
|
595
|
+
flag = NOBLOCK if noblock?(flag)
|
596
|
+
|
597
|
+
parts = []
|
598
|
+
message = @receiver_klass.new
|
599
|
+
rc = recv message, flag
|
600
|
+
parts << message
|
601
|
+
|
602
|
+
# check rc *first*; necessary because the call to #more_parts? can reset
|
603
|
+
# the zmq_errno to a weird value, so the zmq_errno that was set on the
|
604
|
+
# call to #recv gets lost
|
605
|
+
while Util.resultcode_ok?(rc) && more_parts?
|
606
|
+
message = @receiver_klass.new
|
607
|
+
rc = recv message, flag
|
608
|
+
parts << message
|
609
|
+
end
|
610
|
+
|
611
|
+
# only append the received parts if there were no errors
|
612
|
+
# FIXME:
|
613
|
+
# need to detect EAGAIN if flag is set; EAGAIN means we have read all that we
|
614
|
+
# can and should return whatever was already read; need a spec!
|
615
|
+
if Util.resultcode_ok?(rc)
|
616
|
+
parts.each { |part| list << part }
|
617
|
+
end
|
618
|
+
|
619
|
+
rc
|
620
|
+
end
|
621
|
+
|
622
|
+
|
623
|
+
private
|
624
|
+
|
625
|
+
def noblock? flags
|
626
|
+
(NOBLOCK & flags) == NOBLOCK
|
627
|
+
end
|
628
|
+
|
629
|
+
def int_option? name
|
630
|
+
super ||
|
631
|
+
RECONNECT_IVL_MAX == name
|
632
|
+
end
|
633
|
+
|
634
|
+
def long_long_option? name
|
635
|
+
super ||
|
636
|
+
HWM == name ||
|
637
|
+
SWAP == name ||
|
638
|
+
RATE == name ||
|
639
|
+
RECOVERY_IVL == name ||
|
640
|
+
RECOVERY_IVL_MSEC == name ||
|
641
|
+
MCAST_LOOP == name ||
|
642
|
+
SNDBUF == name ||
|
643
|
+
RCVBUF == name
|
644
|
+
end
|
645
|
+
|
646
|
+
# these finalizer-related methods cannot live in the CommonSocketBehavior
|
647
|
+
# module; they *must* be in the class definition directly
|
648
|
+
|
649
|
+
def define_finalizer
|
650
|
+
ObjectSpace.define_finalizer(self, self.class.close(@socket))
|
651
|
+
end
|
652
|
+
|
653
|
+
def remove_finalizer
|
654
|
+
ObjectSpace.undefine_finalizer self
|
655
|
+
end
|
656
|
+
|
657
|
+
def self.close socket
|
658
|
+
Proc.new { LibZMQ.zmq_close socket }
|
659
|
+
end
|
660
|
+
end # class Socket for version2
|
661
|
+
|
662
|
+
end # LibZMQ.version2?
|
663
|
+
|
664
|
+
|
665
|
+
if LibZMQ.version3? || LibZMQ.version4?
|
666
|
+
class Socket
|
667
|
+
include CommonSocketBehavior
|
668
|
+
include IdentitySupport
|
669
|
+
|
670
|
+
# Get the options set on this socket.
|
671
|
+
#
|
672
|
+
# +name+ determines the socket option to request
|
673
|
+
# +array+ should be an empty array; a result of the proper type
|
674
|
+
# (numeric, string, boolean) will be inserted into
|
675
|
+
# the first position.
|
676
|
+
#
|
677
|
+
# Valid +option_name+ values:
|
678
|
+
# ZMQ::RCVMORE - true or false
|
679
|
+
# ZMQ::HWM - integer
|
680
|
+
# ZMQ::SWAP - integer
|
681
|
+
# ZMQ::AFFINITY - bitmap in an integer
|
682
|
+
# ZMQ::IDENTITY - string
|
683
|
+
# ZMQ::RATE - integer
|
684
|
+
# ZMQ::RECOVERY_IVL - integer
|
685
|
+
# ZMQ::SNDBUF - integer
|
686
|
+
# ZMQ::RCVBUF - integer
|
687
|
+
# ZMQ::FD - fd in an integer
|
688
|
+
# ZMQ::EVENTS - bitmap integer
|
689
|
+
# ZMQ::LINGER - integer measured in milliseconds
|
690
|
+
# ZMQ::RECONNECT_IVL - integer measured in milliseconds
|
691
|
+
# ZMQ::BACKLOG - integer
|
692
|
+
# ZMQ::RECOVER_IVL_MSEC - integer measured in milliseconds
|
693
|
+
#
|
694
|
+
# Returns 0 when the operation completed successfully.
|
695
|
+
# Returns -1 when this operation failed.
|
696
|
+
#
|
697
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
698
|
+
# cause.
|
699
|
+
#
|
700
|
+
# # retrieve high water mark
|
701
|
+
# array = []
|
702
|
+
# rc = socket.getsockopt(ZMQ::HWM, array)
|
703
|
+
# hwm = array.first if ZMQ::Util.resultcode_ok?(rc)
|
704
|
+
#
|
705
|
+
def getsockopt name, array
|
706
|
+
rc = __getsockopt__ name, array
|
707
|
+
|
708
|
+
if Util.resultcode_ok?(rc) && (RCVMORE == name)
|
709
|
+
# convert to boolean
|
710
|
+
array[0] = 1 == array[0]
|
711
|
+
end
|
712
|
+
|
713
|
+
rc
|
714
|
+
end
|
715
|
+
|
716
|
+
# The last message part received is tested to see if it is a label.
|
717
|
+
#
|
718
|
+
# Equivalent to calling Socket#getsockopt with ZMQ::RCVLABEL.
|
719
|
+
#
|
720
|
+
# Warning: if the call to #getsockopt fails, this method will return
|
721
|
+
# false and swallow the error.
|
722
|
+
#
|
723
|
+
# labels = []
|
724
|
+
# message_parts = []
|
725
|
+
# message = Message.new
|
726
|
+
# rc = socket.recv(message)
|
727
|
+
# if ZMQ::Util.resultcode_ok?(rc)
|
728
|
+
# label? ? labels.push(message) : message_parts.push(message)
|
729
|
+
# while more_parts?
|
730
|
+
# message = Message.new
|
731
|
+
# if ZMQ::Util.resulcode_ok?(socket.recv(message))
|
732
|
+
# label? ? labels.push(message) : message_parts.push(message)
|
733
|
+
# end
|
734
|
+
# end
|
735
|
+
# end
|
736
|
+
#
|
737
|
+
def label?
|
738
|
+
array = []
|
739
|
+
rc = getsockopt ZMQ::RCVLABEL, array
|
740
|
+
|
741
|
+
Util.resultcode_ok?(rc) ? array.at(0) : false
|
742
|
+
end
|
743
|
+
|
744
|
+
# Queues the message for transmission. Message is assumed to conform to the
|
745
|
+
# same public API as #Message.
|
746
|
+
#
|
747
|
+
# +flags+ may take two values:
|
748
|
+
# * 0 (default) - blocking operation
|
749
|
+
# * ZMQ::DONTWAIT - non-blocking operation
|
750
|
+
# * ZMQ::SNDMORE - this message is part of a multi-part message
|
751
|
+
#
|
752
|
+
# Returns 0 when the message was successfully enqueued.
|
753
|
+
# Returns -1 under two conditions.
|
754
|
+
# 1. The message could not be enqueued
|
755
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
756
|
+
#
|
757
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
758
|
+
# cause.
|
759
|
+
#
|
760
|
+
def sendmsg message, flags = 0
|
761
|
+
LibZMQ.zmq_sendmsg @socket, message.address, flags
|
762
|
+
end
|
763
|
+
|
764
|
+
# Helper method to make a new #Message instance out of the +string+ passed
|
765
|
+
# in for transmission.
|
766
|
+
#
|
767
|
+
# +flags+ may be ZMQ::DONTWAIT and ZMQ::SNDMORE.
|
768
|
+
#
|
769
|
+
# Returns 0 when the message was successfully enqueued.
|
770
|
+
# Returns -1 under two conditions.
|
771
|
+
# 1. The message could not be enqueued
|
772
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
773
|
+
#
|
774
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
775
|
+
# cause.
|
776
|
+
#
|
777
|
+
def send_string string, flags = 0
|
778
|
+
message = Message.new string
|
779
|
+
send_and_close message, flags
|
780
|
+
end
|
781
|
+
|
782
|
+
# Send a sequence of strings as a multipart message out of the +parts+
|
783
|
+
# passed in for transmission. Every element of +parts+ should be
|
784
|
+
# a String.
|
785
|
+
#
|
786
|
+
# +flags+ may be ZMQ::DONTWAIT.
|
787
|
+
#
|
788
|
+
# Returns 0 when the messages were successfully enqueued.
|
789
|
+
# Returns -1 under two conditions.
|
790
|
+
# 1. A message could not be enqueued
|
791
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
792
|
+
#
|
793
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
794
|
+
# cause.
|
795
|
+
#
|
796
|
+
def send_strings parts, flags = 0
|
797
|
+
return -1 if !parts || parts.empty?
|
798
|
+
flags = DONTWAIT if dontwait?(flags)
|
799
|
+
|
800
|
+
parts[0..-2].each do |part|
|
801
|
+
rc = send_string part, (flags | ZMQ::SNDMORE)
|
802
|
+
return rc unless Util.resultcode_ok?(rc)
|
803
|
+
end
|
804
|
+
|
805
|
+
send_string parts[-1], flags
|
806
|
+
end
|
807
|
+
|
808
|
+
# Send a sequence of messages as a multipart message out of the +parts+
|
809
|
+
# passed in for transmission. Every element of +parts+ should be
|
810
|
+
# a Message (or subclass).
|
811
|
+
#
|
812
|
+
# +flags+ may be ZMQ::DONTWAIT.
|
813
|
+
#
|
814
|
+
# Returns 0 when the messages were successfully enqueued.
|
815
|
+
# Returns -1 under two conditions.
|
816
|
+
# 1. A message could not be enqueued
|
817
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
818
|
+
#
|
819
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
820
|
+
# cause.
|
821
|
+
#
|
822
|
+
def sendmsgs parts, flags = 0
|
823
|
+
return -1 if !parts || parts.empty?
|
824
|
+
flags = DONTWAIT if dontwait?(flags)
|
825
|
+
|
826
|
+
parts[0..-2].each do |part|
|
827
|
+
rc = sendmsg part, (flags | ZMQ::SNDMORE)
|
828
|
+
return rc unless Util.resultcode_ok?(rc)
|
829
|
+
end
|
830
|
+
|
831
|
+
sendmsg parts[-1], flags
|
832
|
+
end
|
833
|
+
|
834
|
+
# Sends a message. This will automatically close the +message+ for both successful
|
835
|
+
# and failed sends.
|
836
|
+
#
|
837
|
+
# Returns 0 when the message was successfully enqueued.
|
838
|
+
# Returns -1 under two conditions.
|
839
|
+
# 1. The message could not be enqueued
|
840
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
841
|
+
#
|
842
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
843
|
+
# cause.
|
844
|
+
#
|
845
|
+
def send_and_close message, flags = 0
|
846
|
+
rc = sendmsg message, flags
|
847
|
+
message.close
|
848
|
+
rc
|
849
|
+
end
|
850
|
+
|
851
|
+
# Dequeues a message from the underlying queue. By default, this is a blocking operation.
|
852
|
+
#
|
853
|
+
# +flags+ may take two values:
|
854
|
+
# 0 (default) - blocking operation
|
855
|
+
# ZMQ::DONTWAIT - non-blocking operation
|
856
|
+
#
|
857
|
+
# Returns 0 when the message was successfully dequeued.
|
858
|
+
# Returns -1 under two conditions.
|
859
|
+
# 1. The message could not be dequeued
|
860
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
861
|
+
#
|
862
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
863
|
+
# cause.
|
864
|
+
#
|
865
|
+
# The application code is responsible for handling the +message+ object lifecycle
|
866
|
+
# when #recv returns an error code.
|
867
|
+
#
|
868
|
+
def recvmsg message, flags = 0
|
869
|
+
LibZMQ.zmq_recvmsg @socket, message.address, flags
|
870
|
+
end
|
871
|
+
|
872
|
+
# Helper method to make a new #Message instance and convert its payload
|
873
|
+
# to a string.
|
874
|
+
#
|
875
|
+
# +flags+ may be ZMQ::DONTWAIT.
|
876
|
+
#
|
877
|
+
# Returns 0 when the message was successfully dequeued.
|
878
|
+
# Returns -1 under two conditions.
|
879
|
+
# 1. The message could not be dequeued
|
880
|
+
# 2. When +flags+ is set with ZMQ::DONTWAIT and the socket returned EAGAIN.
|
881
|
+
#
|
882
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
883
|
+
# cause.
|
884
|
+
#
|
885
|
+
# The application code is responsible for handling the +message+ object lifecycle
|
886
|
+
# when #recv returns an error code.
|
887
|
+
#
|
888
|
+
def recv_string string, flags = 0
|
889
|
+
message = @receiver_klass.new
|
890
|
+
rc = recvmsg message, flags
|
891
|
+
string.replace(message.copy_out_string) if Util.resultcode_ok?(rc)
|
892
|
+
message.close
|
893
|
+
rc
|
894
|
+
end
|
465
895
|
|
466
|
-
|
467
|
-
|
468
|
-
|
896
|
+
# Receive a multipart message as a list of strings.
|
897
|
+
#
|
898
|
+
# +flag+ may be ZMQ::DONTWAIT. Any other flag will be
|
899
|
+
# removed.
|
900
|
+
#
|
901
|
+
def recv_strings list, flag = 0
|
902
|
+
array = []
|
903
|
+
rc = recvmsgs array, flag
|
904
|
+
|
905
|
+
if Util.resultcode_ok?(rc)
|
906
|
+
array.each do |message|
|
907
|
+
list << message.copy_out_string
|
908
|
+
message.close
|
909
|
+
end
|
910
|
+
end
|
911
|
+
|
912
|
+
rc
|
913
|
+
end
|
914
|
+
|
915
|
+
# Receive a multipart message as an array of objects
|
916
|
+
# (by default these are instances of Message).
|
917
|
+
#
|
918
|
+
# +flag+ may be ZMQ::DONTWAIT. Any other flag will be
|
919
|
+
# removed.
|
920
|
+
#
|
921
|
+
# Raises the same exceptions as Socket#recv.
|
922
|
+
#
|
923
|
+
def recvmsgs list, flag = 0
|
924
|
+
flag = DONTWAIT if dontwait?(flag)
|
925
|
+
|
926
|
+
parts = []
|
927
|
+
message = @receiver_klass.new
|
928
|
+
rc = recvmsg message, flag
|
929
|
+
parts << message
|
930
|
+
|
931
|
+
# check rc *first*; necessary because the call to #more_parts? can reset
|
932
|
+
# the zmq_errno to a weird value, so the zmq_errno that was set on the
|
933
|
+
# call to #recv gets lost
|
934
|
+
while Util.resultcode_ok?(rc) && more_parts?
|
935
|
+
message = @receiver_klass.new
|
936
|
+
rc = recvmsg message, flag
|
937
|
+
parts << message
|
938
|
+
end
|
939
|
+
|
940
|
+
# only append the received parts if there were no errors
|
941
|
+
if Util.resultcode_ok?(rc)
|
942
|
+
parts.each { |part| list << part }
|
943
|
+
end
|
944
|
+
|
945
|
+
rc
|
946
|
+
end
|
947
|
+
|
948
|
+
|
949
|
+
private
|
469
950
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
951
|
+
def dontwait? flags
|
952
|
+
(DONTWAIT & flags) == DONTWAIT
|
953
|
+
end
|
954
|
+
alias :noblock? :dontwait?
|
955
|
+
|
956
|
+
def int_option? name
|
957
|
+
super ||
|
958
|
+
RCVLABEL == name ||
|
959
|
+
RECONNECT_IVL_MAX == name ||
|
960
|
+
RCVHWM == name ||
|
961
|
+
SNDHWM == name ||
|
962
|
+
RATE == name ||
|
963
|
+
RECOVERY_IVL == name ||
|
964
|
+
SNDBUF == name ||
|
965
|
+
RCVBUF == name
|
966
|
+
end
|
967
|
+
|
968
|
+
# these finalizer-related methods cannot live in the CommonSocketBehavior
|
969
|
+
# module; they *must* be in the class definition directly
|
970
|
+
|
971
|
+
def define_finalizer
|
972
|
+
ObjectSpace.define_finalizer(self, self.class.close(@socket))
|
973
|
+
end
|
974
|
+
|
975
|
+
def remove_finalizer
|
976
|
+
ObjectSpace.undefine_finalizer self
|
977
|
+
end
|
978
|
+
|
979
|
+
def self.close socket
|
980
|
+
Proc.new { LibZMQ.zmq_close socket }
|
981
|
+
end
|
982
|
+
end # Socket for version3
|
983
|
+
end # LibZMQ.version3?
|
474
984
|
|
475
985
|
end # module ZMQ
|