rbczmq 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/.travis.yml +19 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +19 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +247 -0
- data/Rakefile +67 -0
- data/examples/loop.rb +109 -0
- data/examples/poller.rb +37 -0
- data/examples/pub_sub.rb +101 -0
- data/examples/push_pull.rb +104 -0
- data/examples/req_rep.rb +100 -0
- data/ext/czmq.tar.gz +0 -0
- data/ext/rbczmq/context.c +280 -0
- data/ext/rbczmq/context.h +26 -0
- data/ext/rbczmq/extconf.rb +138 -0
- data/ext/rbczmq/frame.c +401 -0
- data/ext/rbczmq/frame.h +24 -0
- data/ext/rbczmq/jruby.h +22 -0
- data/ext/rbczmq/loop.c +413 -0
- data/ext/rbczmq/loop.h +24 -0
- data/ext/rbczmq/message.c +620 -0
- data/ext/rbczmq/message.h +24 -0
- data/ext/rbczmq/poller.c +308 -0
- data/ext/rbczmq/poller.h +29 -0
- data/ext/rbczmq/pollitem.c +251 -0
- data/ext/rbczmq/pollitem.h +25 -0
- data/ext/rbczmq/rbczmq_ext.c +198 -0
- data/ext/rbczmq/rbczmq_ext.h +94 -0
- data/ext/rbczmq/rbczmq_prelude.h +22 -0
- data/ext/rbczmq/rubinius.h +24 -0
- data/ext/rbczmq/ruby18.h +43 -0
- data/ext/rbczmq/ruby19.h +15 -0
- data/ext/rbczmq/socket.c +1570 -0
- data/ext/rbczmq/socket.h +136 -0
- data/ext/rbczmq/timer.c +110 -0
- data/ext/rbczmq/timer.h +23 -0
- data/ext/zeromq.tar.gz +0 -0
- data/lib/rbczmq.rb +3 -0
- data/lib/zmq.rb +77 -0
- data/lib/zmq/context.rb +50 -0
- data/lib/zmq/default_handler.rb +16 -0
- data/lib/zmq/frame.rb +11 -0
- data/lib/zmq/handler.rb +76 -0
- data/lib/zmq/loop.rb +131 -0
- data/lib/zmq/message.rb +9 -0
- data/lib/zmq/poller.rb +22 -0
- data/lib/zmq/pollitem.rb +31 -0
- data/lib/zmq/socket.rb +125 -0
- data/lib/zmq/socket/dealer.rb +33 -0
- data/lib/zmq/socket/pair.rb +39 -0
- data/lib/zmq/socket/pub.rb +30 -0
- data/lib/zmq/socket/pull.rb +29 -0
- data/lib/zmq/socket/push.rb +32 -0
- data/lib/zmq/socket/rep.rb +37 -0
- data/lib/zmq/socket/req.rb +37 -0
- data/lib/zmq/socket/router.rb +38 -0
- data/lib/zmq/socket/sub.rb +27 -0
- data/lib/zmq/timer.rb +12 -0
- data/lib/zmq/version.rb +5 -0
- data/perf/pair.rb +7 -0
- data/perf/pair/local.rb +22 -0
- data/perf/pair/remote.rb +25 -0
- data/perf/pub_sub.rb +7 -0
- data/perf/pub_sub/local.rb +22 -0
- data/perf/pub_sub/remote.rb +25 -0
- data/perf/push_pull.rb +7 -0
- data/perf/push_pull/local.rb +21 -0
- data/perf/push_pull/remote.rb +25 -0
- data/perf/req_rep.rb +7 -0
- data/perf/req_rep/local.rb +35 -0
- data/perf/req_rep/remote.rb +28 -0
- data/perf/runner.rb +142 -0
- data/rbczmq.gemspec +22 -0
- data/test/helper.rb +21 -0
- data/test/socket/test_dealer_socket.rb +14 -0
- data/test/socket/test_pair_socket.rb +24 -0
- data/test/socket/test_pair_sockets.rb +74 -0
- data/test/socket/test_pub_socket.rb +17 -0
- data/test/socket/test_pub_sub_sockets.rb +87 -0
- data/test/socket/test_pull_socket.rb +17 -0
- data/test/socket/test_push_pull_sockets.rb +81 -0
- data/test/socket/test_push_socket.rb +17 -0
- data/test/socket/test_rep_socket.rb +25 -0
- data/test/socket/test_req_rep_sockets.rb +42 -0
- data/test/socket/test_req_socket.rb +27 -0
- data/test/socket/test_router_socket.rb +14 -0
- data/test/socket/test_routing.rb +66 -0
- data/test/socket/test_sub_socket.rb +17 -0
- data/test/test_context.rb +86 -0
- data/test/test_frame.rb +78 -0
- data/test/test_handler.rb +28 -0
- data/test/test_loop.rb +252 -0
- data/test/test_message.rb +201 -0
- data/test/test_poller.rb +154 -0
- data/test/test_pollitem.rb +78 -0
- data/test/test_socket.rb +403 -0
- data/test/test_threading.rb +34 -0
- data/test/test_timer.rb +37 -0
- data/test/test_zmq.rb +62 -0
- metadata +208 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Socket::Push
|
4
|
+
|
5
|
+
# == ZMQ::Socket::Push
|
6
|
+
#
|
7
|
+
# A socket of type ZMQ::Socket::Push is used by a pipeline node to send messages to downstream pipeline nodes. Messages are
|
8
|
+
# load-balanced to all connected downstream nodes. The ZMQ::Socket#recv function is not implemented for this socket type.
|
9
|
+
#
|
10
|
+
# When a ZMQ::Socket::Push socket enters an exceptional state due to having reached the high water mark for all downstream
|
11
|
+
# nodes, or if there are no downstream nodes at all, then any ZMQ::Socket#send operations on the socket shall block until
|
12
|
+
# the exceptional state ends or at least one downstream node becomes available for sending; messages are not discarded.
|
13
|
+
#
|
14
|
+
# Deprecated alias: ZMQ_DOWNSTREAM.
|
15
|
+
#
|
16
|
+
# === Summary of ZMQ::Socket::Push characteristics
|
17
|
+
#
|
18
|
+
# [Compatible peer sockets] ZMQ::Socket::Pull
|
19
|
+
# [Direction] Unidirectional
|
20
|
+
# [Send/receive pattern] Send only
|
21
|
+
# [Incoming routing strategy] N/A
|
22
|
+
# [Outgoing routing strategy] Load-balanced
|
23
|
+
# [ZMQ::Socket#hwm option action] Block
|
24
|
+
|
25
|
+
TYPE_STR = "PUSH"
|
26
|
+
|
27
|
+
def type
|
28
|
+
ZMQ::PUSH
|
29
|
+
end
|
30
|
+
|
31
|
+
include ZMQ::UpstreamSocket
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Socket::Rep
|
4
|
+
|
5
|
+
# == ZMQ::Socket::Rep
|
6
|
+
#
|
7
|
+
# A socket of type ZMQ::Socket::Rep is used by a service to receive requests from and send replies to a client. This socket type
|
8
|
+
# allows only an alternating sequence of ZMQ::Socket#recv and subsequent ZMQ::Socket#send calls. Each request received is
|
9
|
+
# fair-queued from among all clients, and each reply sent is routed to the client that issued the last request. If the
|
10
|
+
# original requester doesn't exist any more the reply is silently discarded.
|
11
|
+
#
|
12
|
+
# When a ZMQ::Socket::Rep socket enters an exceptional state due to having reached the high water mark for a client, then any replies
|
13
|
+
# sent to the client in question shall be dropped until the exceptional state ends.
|
14
|
+
#
|
15
|
+
# === Summary of ZMQ::Socket#rep characteristics
|
16
|
+
#
|
17
|
+
# [Compatible peer sockets] ZMQ::Socket::Rep
|
18
|
+
# [Direction] Bidirectional
|
19
|
+
# [Send/receive pattern] Receive, Send, Receive, Send, …
|
20
|
+
# [Incoming routing strategy] Fair-queued
|
21
|
+
# [Outgoing routing strategy] Last peer
|
22
|
+
# [ZMQ::Socket#hwm option action] Drop
|
23
|
+
|
24
|
+
TYPE_STR = "REP"
|
25
|
+
|
26
|
+
def type
|
27
|
+
ZMQ::REP
|
28
|
+
end
|
29
|
+
|
30
|
+
unsupported_api :sendm
|
31
|
+
handle_fsm_errors "REP sockets allows only an alternating sequence of receive and subsequent send calls.", :send, :send_frame, :send_message, :recv, :recv_nonblock, :recv_frame, :recv_frame_nonblock, :recv_message
|
32
|
+
|
33
|
+
def send_frame(frame, flags = 0)
|
34
|
+
raise ZMQ::Error, "cannot send multiple frames on REP sockets" if (flags & ZMQ::Frame::MORE) == ZMQ::Frame::MORE
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Socket::Req
|
4
|
+
|
5
|
+
# == ZMQ::Socket::Req
|
6
|
+
#
|
7
|
+
# A socket of type ZMQ::Socket::Req is used by a client to send requests to and receive replies from a service. This socket type allows
|
8
|
+
# only an alternating sequence of ZMQ::Socket#send and subsequent ZMQ::Socket#recv calls. Each request sent is load-balanced
|
9
|
+
# among all services, and each reply received is matched with the last issued request.
|
10
|
+
#
|
11
|
+
# When a ZMQ::Socket::Req socket enters an exceptional state due to having reached the high water mark for all services, or if there
|
12
|
+
# are no services at all, then any ZMQ::Socket#send operations on the socket shall block until the exceptional state ends or at
|
13
|
+
# least one service becomes available for sending; messages are not discarded.
|
14
|
+
#
|
15
|
+
# === Summary of ZMQ::Socket::Req characteristics
|
16
|
+
#
|
17
|
+
# [Compatible peer sockets] ZMQ::Socket::Rep
|
18
|
+
# [Direction] Bidirectional
|
19
|
+
# [Send/receive pattern] Send, Receive, Send, Receive, …
|
20
|
+
# [Outgoing routing strategy] Load-balanced
|
21
|
+
# [Incoming routing strategy] Last peer
|
22
|
+
# [ZMQ::Socket#hwm option action] Block
|
23
|
+
|
24
|
+
TYPE_STR = "REQ"
|
25
|
+
|
26
|
+
def type
|
27
|
+
ZMQ::REQ
|
28
|
+
end
|
29
|
+
|
30
|
+
unsupported_api :sendm
|
31
|
+
handle_fsm_errors "REQ sockets allows only an alternating sequence of send and receive calls.", :send, :send_frame, :send_message, :recv, :recv_nonblock, :recv_frame, :recv_frame_nonblock, :recv_message
|
32
|
+
|
33
|
+
def send_frame(frame, flags = 0)
|
34
|
+
raise ZMQ::Error, "cannot send multiple frames on REQ sockets" if (flags & ZMQ::Frame::MORE) == ZMQ::Frame::MORE
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Socket::Router
|
4
|
+
|
5
|
+
# == ZMQ::Socket::Router
|
6
|
+
#
|
7
|
+
# A socket of type ZMQ::Socket::Router is an advanced pattern used for extending request/reply sockets. When receiving messages
|
8
|
+
# a ZMQ::Socket::Router socket shall prepend a message part containing the identity of the originating peer to the message before
|
9
|
+
# passing it to the application. Messages received are fair-queued from among all connected peers. When sending messages a
|
10
|
+
# ZMQ::Socket::Router socket shall remove the first part of the message and use it to determine the identity of the peer the message
|
11
|
+
# shall be routed to. If the peer does not exist anymore the message shall be silently discarded.
|
12
|
+
#
|
13
|
+
# Previously this socket was called ZMQ_XREP and that name remains available for backwards compatibility.
|
14
|
+
#
|
15
|
+
# When a ZMQ::Socket::Router socket enters an exceptional state due to having reached the high water mark for all peers, or if
|
16
|
+
# there are no peers at all, then any messages sent to the socket shall be dropped until the exceptional state ends. Likewise, any
|
17
|
+
# messages routed to a non-existent peer or a peer for which the individual high water mark has been reached shall also be dropped.
|
18
|
+
#
|
19
|
+
# When a ZMQ::Socket::Request socket is connected to a ZMQ::Socket::Router socket, in addition to the identity of the originating
|
20
|
+
# peer each message received shall contain an empty delimiter message part. Hence, the entire structure of each received message as
|
21
|
+
# seen by the application becomes: one or more identity parts, delimiter part, one or more body parts. When sending replies to a
|
22
|
+
# ZMQ::Socket::Request socket the application must include the delimiter part.
|
23
|
+
#
|
24
|
+
# === Summary of ZMQ_ROUTER characteristics
|
25
|
+
#
|
26
|
+
# [Compatible peer socket] ZMQ::Socket::Dealer, ZMQ::Socket::Req, ZMQ::Socket::Rep
|
27
|
+
# [Direction] Bidirectional
|
28
|
+
# [Send/receive pattern] Unrestricted
|
29
|
+
# [Outgoing routing strategy] See text
|
30
|
+
# [Incoming routing strategy] Fair-queued
|
31
|
+
# [ZMQ::Socket#hwm option action] Drop
|
32
|
+
|
33
|
+
TYPE_STR = "ROUTER"
|
34
|
+
|
35
|
+
def type
|
36
|
+
ZMQ::ROUTER
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Socket::Sub
|
4
|
+
|
5
|
+
# == ZMQ::Socket::Sub
|
6
|
+
#
|
7
|
+
# A socket of type ZMQ::Socket::Sub is used by a subscriber to subscribe to data distributed by a publisher. Initially a
|
8
|
+
# ZMQ::Socket::Sub socket is not subscribed to any messages, use ZMQ::Socket#subscribe to specify which messages to
|
9
|
+
# subscribe to. The ZMQ::Socket#send function is not implemented for this socket type.
|
10
|
+
#
|
11
|
+
# === Summary of ZMQ::Socket::Sub characteristics
|
12
|
+
#
|
13
|
+
# [Compatible peer sockets] ZMQ::Socket::Pub
|
14
|
+
# [Direction] Unidirectional
|
15
|
+
# [Send/receive pattern] Receive only
|
16
|
+
# [Incoming routing strategy] Fair-queued
|
17
|
+
# [Outgoing routing strategy] N/A
|
18
|
+
# [ZMQ::Socket#hwm option action] Drop
|
19
|
+
|
20
|
+
TYPE_STR = "SUB"
|
21
|
+
|
22
|
+
def type
|
23
|
+
ZMQ::SUB
|
24
|
+
end
|
25
|
+
|
26
|
+
include ZMQ::DownstreamSocket
|
27
|
+
end
|
data/lib/zmq/timer.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ZMQ::Timer
|
4
|
+
|
5
|
+
# Callback for error conditions such as exceptions raised in timer callbacks. Receives an exception instance as argument and raises by default.
|
6
|
+
#
|
7
|
+
# handler.on_error(err) => raise
|
8
|
+
#
|
9
|
+
def on_error(exception)
|
10
|
+
raise exception
|
11
|
+
end
|
12
|
+
end
|
data/lib/zmq/version.rb
ADDED
data/perf/pair.rb
ADDED
data/perf/pair/local.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
pair = ctx.socket(:PAIR)
|
5
|
+
sleep 2
|
6
|
+
pair.connect($runner.endpoint)
|
7
|
+
|
8
|
+
messages, start_time = 0, nil
|
9
|
+
while (case $runner.encoding
|
10
|
+
when :string
|
11
|
+
pair.recv
|
12
|
+
when :frame
|
13
|
+
pair.recv_frame
|
14
|
+
when :message
|
15
|
+
pair.recv_message
|
16
|
+
end) do
|
17
|
+
start_time ||= Time.now
|
18
|
+
messages += 1
|
19
|
+
break if messages == $runner.msg_count
|
20
|
+
end
|
21
|
+
|
22
|
+
$runner.stats(start_time)
|
data/perf/pair/remote.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ.context
|
4
|
+
pair = ctx.socket(:PAIR);
|
5
|
+
pair.bind($runner.endpoint);
|
6
|
+
|
7
|
+
msg = $runner.payload
|
8
|
+
|
9
|
+
start_time = Time.now
|
10
|
+
$runner.msg_count.times do
|
11
|
+
case $runner.encoding
|
12
|
+
when :string
|
13
|
+
pair.send(msg)
|
14
|
+
when :frame
|
15
|
+
pair.send_frame(ZMQ::Frame(msg))
|
16
|
+
when :message
|
17
|
+
m = ZMQ::Message.new
|
18
|
+
m.pushstr "header"
|
19
|
+
m.pushstr msg
|
20
|
+
m.pushstr "body"
|
21
|
+
pair.send_message(m)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Sent #{$runner.msg_count} messages in %ss ..." % (Time.now - start_time)
|
data/perf/pub_sub.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
sub = ctx.socket(:SUB)
|
5
|
+
sub.subscribe("")
|
6
|
+
sub.connect($runner.endpoint)
|
7
|
+
|
8
|
+
messages, start_time = 0, nil
|
9
|
+
while (case $runner.encoding
|
10
|
+
when :string
|
11
|
+
sub.recv
|
12
|
+
when :frame
|
13
|
+
sub.recv_frame
|
14
|
+
when :message
|
15
|
+
sub.recv_message
|
16
|
+
end) do
|
17
|
+
start_time ||= Time.now
|
18
|
+
messages += 1
|
19
|
+
break if messages == $runner.msg_count
|
20
|
+
end
|
21
|
+
|
22
|
+
$runner.stats(start_time)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
pub = ctx.socket(:PUB);
|
5
|
+
pub.bind($runner.endpoint);
|
6
|
+
|
7
|
+
msg = $runner.payload
|
8
|
+
|
9
|
+
start_time = Time.now
|
10
|
+
$runner.msg_count.times do
|
11
|
+
case $runner.encoding
|
12
|
+
when :string
|
13
|
+
pub.send(msg)
|
14
|
+
when :frame
|
15
|
+
pub.send_frame(ZMQ::Frame(msg))
|
16
|
+
when :message
|
17
|
+
m = ZMQ::Message.new
|
18
|
+
m.pushstr "header"
|
19
|
+
m.pushstr msg
|
20
|
+
m.pushstr "body"
|
21
|
+
pub.send_message(m)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Sent #{$runner.msg_count} messages in %ss ..." % (Time.now - start_time)
|
data/perf/push_pull.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
pull = ctx.socket(:PULL)
|
5
|
+
pull.connect($runner.endpoint)
|
6
|
+
|
7
|
+
messages, start_time = 0, nil
|
8
|
+
while (case $runner.encoding
|
9
|
+
when :string
|
10
|
+
pull.recv
|
11
|
+
when :frame
|
12
|
+
pull.recv_frame
|
13
|
+
when :message
|
14
|
+
pull.recv_message
|
15
|
+
end) do
|
16
|
+
start_time ||= Time.now
|
17
|
+
messages += 1
|
18
|
+
break if messages == $runner.process_msg_count
|
19
|
+
end
|
20
|
+
|
21
|
+
$runner.stats(start_time)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
push = ctx.socket(:PUSH);
|
5
|
+
push.bind($runner.endpoint);
|
6
|
+
|
7
|
+
msg = $runner.payload
|
8
|
+
|
9
|
+
start_time = Time.now
|
10
|
+
$runner.msg_count.times do
|
11
|
+
case $runner.encoding
|
12
|
+
when :string
|
13
|
+
push.send(msg)
|
14
|
+
when :frame
|
15
|
+
push.send_frame(ZMQ::Frame(msg))
|
16
|
+
when :message
|
17
|
+
m = ZMQ::Message.new
|
18
|
+
m.pushstr "header"
|
19
|
+
m.pushstr msg
|
20
|
+
m.pushstr "body"
|
21
|
+
push.send_message(m)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Sent #{$runner.msg_count} messages in %ss ..." % (Time.now - start_time)
|
data/perf/req_rep.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
req = ctx.socket(:REQ)
|
5
|
+
req.connect($runner.endpoint)
|
6
|
+
|
7
|
+
msg = $runner.payload
|
8
|
+
|
9
|
+
messages, start_time = 0, nil
|
10
|
+
while (case $runner.encoding
|
11
|
+
when :string
|
12
|
+
req.send(msg)
|
13
|
+
when :frame
|
14
|
+
req.send_frame(ZMQ::Frame(msg))
|
15
|
+
when :message
|
16
|
+
m = ZMQ::Message.new
|
17
|
+
m.pushstr "header"
|
18
|
+
m.pushstr msg
|
19
|
+
m.pushstr "body"
|
20
|
+
req.send_message(m)
|
21
|
+
end) do
|
22
|
+
start_time ||= Time.now
|
23
|
+
messages += 1
|
24
|
+
case $runner.encoding
|
25
|
+
when :string
|
26
|
+
req.recv
|
27
|
+
when :frame
|
28
|
+
req.recv_frame
|
29
|
+
when :message
|
30
|
+
req.recv_message
|
31
|
+
end
|
32
|
+
break if messages == $runner.msg_count
|
33
|
+
end
|
34
|
+
|
35
|
+
$runner.stats(start_time)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
ctx = ZMQ::Context.new
|
4
|
+
rep = ctx.socket(:REP);
|
5
|
+
rep.bind($runner.endpoint);
|
6
|
+
|
7
|
+
start_time = Time.now
|
8
|
+
$runner.msg_count.times do
|
9
|
+
msg = case $runner.encoding
|
10
|
+
when :string
|
11
|
+
rep.recv
|
12
|
+
when :frame
|
13
|
+
rep.recv_frame
|
14
|
+
when :message
|
15
|
+
rep.recv_message
|
16
|
+
end
|
17
|
+
|
18
|
+
case $runner.encoding
|
19
|
+
when :string
|
20
|
+
rep.send(msg)
|
21
|
+
when :frame
|
22
|
+
rep.send_frame(msg)
|
23
|
+
when :message
|
24
|
+
rep.send_message(msg)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "Sent #{$runner.msg_count} messages in %ss ..." % (Time.now - start_time)
|