simple_ipc 1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +29 -0
  2. data/lib/simple_ipc.rb +234 -0
  3. data/test/test.rb +8 -0
  4. metadata +66 -0
@@ -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")
@@ -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
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # test
3
+ #
4
+ # Created by Paolo Bosetti on 2011-01-18.
5
+ # Copyright (c) 2011 University of Trento. All rights reserved.
6
+ #
7
+
8
+ require "./lib/simple_ipc"
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
+