simple_ipc 1.1
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/README.md +29 -0
- data/lib/simple_ipc.rb +234 -0
- data/test/test.rb +8 -0
- metadata +66 -0
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
SimpleIPC
|
2
|
+
=========
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
SimpleIPC is a library for doing simple inter-process communication in Ruby.
|
8
|
+
|
9
|
+
Example
|
10
|
+
-------
|
11
|
+
|
12
|
+
Server example:
|
13
|
+
|
14
|
+
from_client = SimpleIPC::IPC.new :port => 5000, :nonblock => true, :kind => :unix
|
15
|
+
from_client.listen
|
16
|
+
running = true
|
17
|
+
while running != "stop" do
|
18
|
+
running = from_client.get
|
19
|
+
p running if running
|
20
|
+
sleep 0.01
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
Client example:
|
25
|
+
|
26
|
+
to_server = SimpleIPC::IPC.new :kind => :unix
|
27
|
+
to_server.send([1,2,3, "test"])
|
28
|
+
to_server.send({:a => "test", :b => "prova"})
|
29
|
+
to_server.send("stop")
|
data/lib/simple_ipc.rb
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# simple_ipc
|
3
|
+
#
|
4
|
+
# Created by Paolo Bosetti on 2011-01-18.
|
5
|
+
# Copyright (c) 2011 University of Trento. All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
require "yaml"
|
9
|
+
require "socket"
|
10
|
+
require "timeout"
|
11
|
+
require "fileutils"
|
12
|
+
|
13
|
+
|
14
|
+
# SimpleIPC implements a simple inter process communication
|
15
|
+
# @author Paolo Bosetti
|
16
|
+
module SimpleIPC
|
17
|
+
VERSION = "1.1"
|
18
|
+
LOCALHOST = "127.0.0.1"
|
19
|
+
BROADCAST = "" # Accept connections from INADDR_ANY
|
20
|
+
LENGTH_CODE = 'N'
|
21
|
+
LENGTH_SIZE = [0].pack(LENGTH_CODE).size
|
22
|
+
|
23
|
+
def SimpleIPC.version; VERSION; end
|
24
|
+
|
25
|
+
# Wrapper class exposing the same API for +UNIXSocket+ and +UDPSocket+ classes.
|
26
|
+
class Socket
|
27
|
+
|
28
|
+
# Default initialization hash is:
|
29
|
+
# {
|
30
|
+
# :port => 5000, # port, only used for UDPSockets
|
31
|
+
# :host => LOCALHOST, # Host to talk with, only used for UDPSockets
|
32
|
+
# :kind => :unix, # kind of socket, either :unix or :udp
|
33
|
+
# :force => true # if true, force removing of stale socket files
|
34
|
+
# }
|
35
|
+
# @param [Hash] args a hash of config values
|
36
|
+
def initialize(args = {})
|
37
|
+
@cfg = {
|
38
|
+
:port => 5000,
|
39
|
+
:host => LOCALHOST,
|
40
|
+
:kind => :unix,
|
41
|
+
:force => true
|
42
|
+
}
|
43
|
+
@cfg.merge! args
|
44
|
+
case @cfg[:kind]
|
45
|
+
when :unix
|
46
|
+
@socket_file = "/tmp/#{$0}.sok"
|
47
|
+
@socket = nil
|
48
|
+
when :udp
|
49
|
+
@socket = UDPSocket.new
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Either :unix or :udp allowed"
|
52
|
+
end
|
53
|
+
@open = false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Opens the connection. Only has to be called once before sending messages.
|
57
|
+
# Only used for client sockets.
|
58
|
+
def connect
|
59
|
+
return false if @open
|
60
|
+
case @cfg[:kind]
|
61
|
+
when :unix
|
62
|
+
@socket = UNIXSocket.open(@socket_file)
|
63
|
+
when :udp
|
64
|
+
@socket.connect(@cfg[:host], @cfg[:port])
|
65
|
+
end
|
66
|
+
@open = true
|
67
|
+
end
|
68
|
+
|
69
|
+
# Sends a +String+ through the socket.
|
70
|
+
# @param [String] string the message to be sent
|
71
|
+
def print(string)
|
72
|
+
@socket.print(string)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Listens for incoming messages, i.e. becomes a server. If +@cfg[:force]+
|
76
|
+
# is true, it also silently removes any existing stale socket file, otherwise
|
77
|
+
# stops.
|
78
|
+
# @raise [Errno::EADDRINUSE] when +@cfg[:force]+ is false and a socket file
|
79
|
+
# already exists
|
80
|
+
def listen
|
81
|
+
case @cfg[:kind]
|
82
|
+
when :unix
|
83
|
+
@socket = UNIXServer.open(@socket_file).accept
|
84
|
+
when :udp
|
85
|
+
@socket.bind(BROADCAST, @cfg[:port])
|
86
|
+
end
|
87
|
+
rescue Errno::EADDRINUSE
|
88
|
+
if @cfg[:force] then
|
89
|
+
FileUtils::rm(@socket_file)
|
90
|
+
retry
|
91
|
+
else
|
92
|
+
raise Errno::EADDRINUSE, $!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Receives a message of length +bytes+.
|
97
|
+
# @param [Integer] bytes the number of characters to be read
|
98
|
+
# @return [String]
|
99
|
+
def recvfrom(bytes)
|
100
|
+
@socket.recvfrom(bytes)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Receives a message of length +bytes+ in non-blocking way.
|
104
|
+
# @param [Integer] bytes the number of characters to be read
|
105
|
+
# @return [String]
|
106
|
+
def recv_nonblock(bytes)
|
107
|
+
@socket.recv_nonblock(bytes)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Closes the socket and removes the socket file if it exists.
|
111
|
+
def close
|
112
|
+
@socket.close
|
113
|
+
@open = false
|
114
|
+
FileUtils::rm(@socket_file) if @socket_file
|
115
|
+
end
|
116
|
+
|
117
|
+
end #Socket Class
|
118
|
+
|
119
|
+
class IPC
|
120
|
+
attr_accessor :cfg
|
121
|
+
|
122
|
+
# Default initialization hash is:
|
123
|
+
# {:port => 5000, # Port to listen at
|
124
|
+
# :host => LOCALHOST, # Host to talk to
|
125
|
+
# :timeout => 0, # Timeout for blocking connections
|
126
|
+
# :blocking => false} # use blocking read
|
127
|
+
# @param [Hash] args a hash of config values
|
128
|
+
def initialize(args = {})
|
129
|
+
raise ArgumentError, "expecting an Hash" unless args.kind_of? Hash
|
130
|
+
@cfg = {:port => 5000, :host => LOCALHOST, :timeout => 0}
|
131
|
+
@cfg.merge! args
|
132
|
+
@socket = Socket.new @cfg
|
133
|
+
end
|
134
|
+
|
135
|
+
# Sends a general object to the server. If an optional block is given, then it
|
136
|
+
# is used to perform the object serialization. Otherwise, YAML#dump is used
|
137
|
+
# for serialization.
|
138
|
+
# @param [Object] something an object
|
139
|
+
# @yield [Object] a block that serializes the received +Object+
|
140
|
+
def send(something)
|
141
|
+
if block_given? then
|
142
|
+
payload = yield(something)
|
143
|
+
else
|
144
|
+
payload = YAML.dump(something)
|
145
|
+
end
|
146
|
+
length = [payload.size].pack(LENGTH_CODE)
|
147
|
+
@socket.connect
|
148
|
+
@socket.print length
|
149
|
+
@socket.print payload
|
150
|
+
return payload
|
151
|
+
end
|
152
|
+
|
153
|
+
# Puts the object in listening state (becomes a server).
|
154
|
+
def listen
|
155
|
+
@socket.listen
|
156
|
+
end
|
157
|
+
|
158
|
+
# Gets an object (only valid if it is a server). An optional block can be
|
159
|
+
# given for parsing the received +String+. If no block is given, then the
|
160
|
+
# YAML#load deserialization is automatically used.
|
161
|
+
# @return [Object] a parsed object
|
162
|
+
# @yield [String] a block that deserializes the received +String+
|
163
|
+
def get
|
164
|
+
result = nil
|
165
|
+
begin
|
166
|
+
if @cfg[:timeout] > 0 and !@cfg[:nonblock] then
|
167
|
+
Timeout::timeout(@cfg[:timeout]) do |to|
|
168
|
+
result = get_
|
169
|
+
end
|
170
|
+
else
|
171
|
+
result = get_
|
172
|
+
end
|
173
|
+
rescue Timeout::Error
|
174
|
+
result = nil
|
175
|
+
rescue Errno::EAGAIN
|
176
|
+
return nil
|
177
|
+
end
|
178
|
+
|
179
|
+
if block_given? then
|
180
|
+
return yield(result)
|
181
|
+
else
|
182
|
+
return YAML.load(result)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Closes the socket.
|
187
|
+
def close
|
188
|
+
@socket.close
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
def get_
|
193
|
+
if @cfg[:nonblock] then
|
194
|
+
msg, sender = @socket.recv_nonblock(LENGTH_SIZE)
|
195
|
+
else
|
196
|
+
msg = @socket.recvfrom(LENGTH_SIZE)[0]
|
197
|
+
end
|
198
|
+
length = msg.unpack(LENGTH_CODE)[0]
|
199
|
+
msg, sender = @socket.recvfrom(length)
|
200
|
+
return msg
|
201
|
+
end
|
202
|
+
|
203
|
+
end #IPC Class
|
204
|
+
end #SimpleIPC module
|
205
|
+
|
206
|
+
if $0 == __FILE__ then
|
207
|
+
puts "Using SimpleIPC version #{SimpleIPC::version}"
|
208
|
+
ary = [1,2,3,4]
|
209
|
+
if ARGV[0] == "server" then
|
210
|
+
from_client = SimpleIPC::IPC.new :port => 5000, :nonblock => true, :kind => :udp
|
211
|
+
from_client.listen
|
212
|
+
running = true
|
213
|
+
while running != "stop" do
|
214
|
+
running = from_client.get
|
215
|
+
p running if running
|
216
|
+
sleep 0.01
|
217
|
+
end
|
218
|
+
# p from_client.get
|
219
|
+
# p from_client.get {|s| s.split(",").map {|v| v.to_f}}
|
220
|
+
# p from_client.get {|s| s.unpack("N4")}
|
221
|
+
|
222
|
+
else # client
|
223
|
+
to_server = SimpleIPC::IPC.new :port => 5000, :kind => :udp
|
224
|
+
to_server.send([1,2,3, "test"])
|
225
|
+
to_server.send({:a => "test", :b => "prova"})
|
226
|
+
to_server.send("stop")
|
227
|
+
|
228
|
+
# to_server.send([1,2,3,4]) {|o| o * ","}
|
229
|
+
# to_server.send(ary) {|o| o.pack("N#{ary.size}")}
|
230
|
+
to_server.close
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
|
data/test/test.rb
ADDED
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_ipc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 1
|
8
|
+
version: "1.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Paolo Bosetti
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2011-01-19 00:00:00 +01:00
|
17
|
+
default_executable:
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description: Simple Inter-Process Communication library
|
21
|
+
email: paolo.bosetti@me.com
|
22
|
+
executables: []
|
23
|
+
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files: []
|
27
|
+
|
28
|
+
files:
|
29
|
+
- README.md
|
30
|
+
- lib/simple_ipc.rb
|
31
|
+
- test/test.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/pbosetti/simpleipc
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --inline-source
|
39
|
+
- --charset=UTF-8
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project: simple_ipc
|
61
|
+
rubygems_version: 1.3.7
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Simple Inter-Process Communication library
|
65
|
+
test_files: []
|
66
|
+
|