dango 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ Manifest.txt
2
+ README.txt
3
+ lib/dango/client_framework.rb
4
+ lib/dango/framework_base.rb
5
+ lib/dango/server_framework.rb
6
+ lib/dango/monitor/dango_monitor_client.rb
7
+ lib/dango/script/dango_server.rb
8
+ lib/dango/shared/memory_store.rb
9
+ lib/dango/tasks/dango_rake.rb
10
+ lib/dango/version.rb
11
+ setup.rb
12
+ test/test_helper.rb
13
+ test/test_dango.rb
data/README.txt ADDED
@@ -0,0 +1,47 @@
1
+ = dango -- Ruby - Flash conneciton network flamework
2
+
3
+
4
+ == Download
5
+
6
+ The latest version of dango can be found at
7
+
8
+ * http://rubyforge.org/frs/?group_id=****
9
+
10
+ == Installation
11
+
12
+ === Normal Installation
13
+
14
+ You can install dango with the following command.
15
+
16
+ % ruby setup.rb
17
+
18
+ from its distribution directory.
19
+
20
+ === GEM Installation
21
+
22
+ Download and install dango with the following.
23
+
24
+ gem install dango
25
+
26
+ == dango References
27
+
28
+ * dango Project Page: http://rubyforge.org/projects/dango
29
+ * dango API Documents: http://dango.rubyforge.org
30
+
31
+ == Simple Example
32
+
33
+ ruby "rubygems" # you use rubygems
34
+ ruby "image_size"
35
+ ruby "open-uri"
36
+ open("http://www.rubycgi.org/image/ruby_gtk_book_title.jpg", "rb") do |fh|
37
+ p ImageSize.new(fh.read).get_size
38
+ end
39
+
40
+ == Licence
41
+
42
+ This code is free to use under the terms of the Ruby's licence.
43
+
44
+ == Contact
45
+
46
+ Comments are welcome. Send an email to "Keisuke Minami":mailto:keisuke@rccn.com
47
+
@@ -0,0 +1,260 @@
1
+ #!ruby -Ku
2
+
3
+ =begin
4
+ = コネクション型クライアントフレームワーク
5
+ =end
6
+
7
+ require "dango/framework_base"
8
+
9
+ # フレームワーククラス
10
+ class DangoClientFramework
11
+ include DangoFrameworkModule
12
+
13
+ SendReceiveSleepIntervalSec = 0.1 # データ送信後の順の際のタイムアウトチェック間隔秒
14
+
15
+ # cs_client_send_receive_data用の共有メモリ
16
+ def send_receive_shared_init
17
+ @send_receive_shared = SharedMemoryStore.new
18
+ end
19
+ def send_receive_shared
20
+ @send_receive_shared
21
+ end
22
+
23
+ # 起動処理
24
+ def cs_client_init
25
+ end
26
+
27
+ # セッションが切れると呼ばれる処理
28
+ def cs_session_closed
29
+ end
30
+
31
+ # メイン処理
32
+ def initialize(env, config = nil)
33
+ if !config
34
+ config = YAML.load(open("dango/config/#{env}.yml", "rb"){|fh| fh.read})
35
+ end
36
+
37
+ @config = config
38
+
39
+ # 変数の初期設定
40
+ @connection_client_host = @config['client']['host'] || 'localhost'
41
+ @connection_client_port = @config['network']['port'] || 15000
42
+
43
+ @connection_client_host = 'localhost' # 未定義時にこのホスト名でサーバー開始
44
+ @connection_client_port = 15000 # 未定義時にこのポートでサーバー開始
45
+ @connection_client_log_file = "" # 未定義時にこのログファイル名を使う
46
+ @connection_client_log_level = Logger::DEBUG # 未定義時にこのログレベルになる
47
+
48
+ @recv_count = 0 # 受信回数
49
+ @send_count = 0 # 送信回数
50
+
51
+ Signal.trap('INT') do # SIGINT を捕捉する。
52
+ puts "shutdown"
53
+ # exit
54
+ exit!
55
+ end
56
+
57
+ cs_client_init() # 初期設定読み込み
58
+
59
+ @log_file = @connection_client_log_file
60
+ @log_level = @connection_client_log_level
61
+
62
+ # loggerの準備
63
+ cs_logger.debug("client initialize")
64
+
65
+ # データ送受信用の共有メモリ初期化
66
+ send_receive_shared_init()
67
+ @send_receive_mutex = Mutex.new # 送受信用の排他処理
68
+
69
+ # 通知共有メモリの初期化
70
+ cs_client_notice_shared_init()
71
+
72
+ # ソケットの準備
73
+ @sock = nil
74
+
75
+ # サーバーへ接続
76
+ begin
77
+ @sock = TCPSocket.new(@connection_client_host, @connection_client_port)
78
+ rescue Errno::EBADF
79
+ raise DangoFrameworkConnectionError
80
+ end
81
+
82
+ @sock.binmode
83
+ @sock.sync = true
84
+ @tmutex = Mutex.new # sockの排他処理用
85
+ cs_logger.debug("addr:#{@sock.addr.inspect}")
86
+
87
+ # データ受信用のスレッドの開始
88
+ @thread_receive = Thread.start{ thread_main() }
89
+ end
90
+
91
+ # データ受信用のスレッド処理
92
+ def thread_main()
93
+ catch(:session_closed) do
94
+ loop do
95
+ begin
96
+ if @sock
97
+ cs_logger.debug "start receive_data"
98
+ ret_obj = nil
99
+ begin
100
+ ret_obj = cs_receive_data(@sock) # データ受信処理
101
+ rescue DangoFrameworkConnectionError
102
+ cs_logger.debug "sock error:#{error_message($!, 'u')}"
103
+ throw(:session_closed)
104
+ rescue DangoFrameworkError
105
+ cs_logger.debug "sock error:#{error_message($!, 'u')}"
106
+ throw(:session_closed)
107
+ end
108
+ cs_logger.debug "finished cs_receive_data:#{ret_obj.inspect}"
109
+
110
+ if !(ret_obj && ret_obj["notice"])
111
+ cs_logger.info "no notice:#{ret_obj["notice"].inspect}"
112
+ end
113
+
114
+ # メソッド呼び出し
115
+ cs_logger.info "calling method=#{ret_obj["notice"].inspect}"
116
+ begin
117
+ __send__("cs_receive_#{ret_obj["notice"]}", ret_obj)
118
+ rescue
119
+ cs_logger.error "error in cs_receive_#{ret_obj["notice"]}\n#{error_message($!, 'u')}"
120
+ end
121
+ cs_logger.debug "called method=#{ret_obj["notice"].inspect}"
122
+
123
+ else
124
+ sleep 0.1
125
+ end
126
+
127
+ rescue Exception
128
+ cs_logger.debug "#{error_message($!, 'u')}"
129
+ end
130
+ end
131
+ end
132
+
133
+ client_quit() # クライアントの終了処理
134
+ end
135
+ private :thread_main
136
+
137
+ # ユーザーが終了させたい時に呼び出すメソッド(セッションを切る)
138
+ def cs_client_close
139
+ client_quit() # クライアントの終了処理
140
+ end
141
+
142
+ # クライアントの終了処理
143
+ def client_quit()
144
+ begin
145
+ @sock.close if @sock
146
+ rescue
147
+ cs_logger.debug "sock close failed. #{$!}"
148
+ end
149
+ @sock = nil
150
+
151
+ @is_session_closed = true
152
+ cs_logger.info "calling cs_session_closed()"
153
+ cs_session_closed() # セッションが切れると呼ばれる処理
154
+ end
155
+ private :client_quit
156
+
157
+ # クライアントの暗号化処理
158
+ def cs_client_send_encrypt(str) # 継承用
159
+ str
160
+ end
161
+ def cs_send_encrypt(str) # フレームワークから呼ばれる部分
162
+ cs_client_send_encrypt(str)
163
+ end
164
+
165
+ # クライアントの復号化処理
166
+ def cs_client_receive_decrypt(str) # 継承用
167
+ str
168
+ end
169
+ def cs_receive_decrypt(str) # フレームワークから呼ばれる部分
170
+ cs_client_receive_decrypt(str)
171
+ end
172
+
173
+ # クライアントからサーバーへのデータ送信
174
+ def cs_client_send_data(action_name, send_obj)
175
+ send_obj_dup = send_obj.dup
176
+ send_obj_dup["action"] = action_name.to_s
177
+ cs_send_data(@sock, send_obj_dup)
178
+ end
179
+
180
+ # サーバーからクライアントへのデータ送信
181
+ # 自動でreturn_nameに対応したcs_receive_メソッドが出来上がるので注意
182
+ # options = {:return_name=> :timeout => }
183
+ # return_nameは省略時は、action_nameの頭にreturnを付けたもののシンボルになる
184
+ # timeoutは省略時は6秒
185
+ def cs_client_send_receive_data(action_name, send_obj, options = {})
186
+ cs_logger.debug "cs_client_send_receive_data(#{action_name.inspect}, #{send_obj.inspect}, #{options.inspect})"
187
+
188
+ return_name = options[:return_name] || ("return_" + action_name.to_s).to_sym
189
+ timeout = options[:timeout] || 6
190
+
191
+ @send_receive_mutex.synchronize do
192
+ send_receive_shared[action_name] = nil
193
+
194
+ receive_thread = nil
195
+ end_reserved_time = Time.now + timeout
196
+
197
+ # 戻ってきたデータのチェック
198
+ action_name_sym = (action_name.class == Symbol) ? (":"+action_name.to_s) : ('"'+action_name+'"')
199
+ instance_method_name = "cs_receive_#{return_name}"
200
+ expr = <<-EOF
201
+ def self.#{instance_method_name}(ret_obj)
202
+ cs_logger.debug "ret_obj:" + ret_obj.inspect
203
+ send_receive_shared[#{action_name_sym}] = ret_obj
204
+ end
205
+ EOF
206
+ instance_eval expr
207
+
208
+ # タイムアウトチェック
209
+ (timeout.to_f / SendReceiveSleepIntervalSec).to_i.times do
210
+ raise(DangoFrameworkTimeoutError, "timeout:#{action_name}") if Time.now > end_reserved_time
211
+
212
+ # 送信スレッドが開始していなければ開始
213
+ if !receive_thread
214
+ receive_thread = Thread.start do
215
+ cs_client_send_data(action_name, send_obj)
216
+ end
217
+ end
218
+
219
+ # 戻ってきたデータがあれば
220
+ if send_receive_shared[action_name]
221
+ cs_logger.debug "action_name:#{send_receive_shared[action_name].inspect}"
222
+ break
223
+ end
224
+
225
+ sleep SendReceiveSleepIntervalSec # スリープ
226
+ end
227
+
228
+ if !send_receive_shared[action_name] # 戻ってきたデータがあるかどうかチェック
229
+ raise(DangoFrameworkError, "received data is none")
230
+ end
231
+
232
+ #remove_method(instance_method_name) # 定義したインスタンスメソッドを削除しておく
233
+
234
+ send_receive_shared[action_name]
235
+ end
236
+ end
237
+
238
+ # 通知共有メモリ情報の書き換えクラス
239
+ class ClientNoticeShared
240
+ def initialize(parent)
241
+ @parent = parent
242
+ end
243
+
244
+ def []=(key, value) # キーに対する書き込み(読み込みはないぞ)
245
+ action_name = "action_notice_shared_#{key}"
246
+ send_obj = {:data => value}
247
+ @parent.cs_client_send_data(action_name, send_obj)
248
+ end
249
+ end
250
+
251
+ def cs_client_notice_shared_init # 通知共有メモリの初期化
252
+ @client_notice_shared = ClientNoticeShared.new(self)
253
+ end
254
+
255
+ def cs_client_notice_shared # 通知共有メモリ
256
+ @client_notice_shared
257
+ end
258
+
259
+ end
260
+
@@ -0,0 +1,247 @@
1
+ #!ruby -Ku
2
+
3
+ =begin
4
+ = コネクションのサーバー、クライアントで使うモジュール
5
+ =end
6
+
7
+ require "rubygems"
8
+
9
+ require 'logger'
10
+ require 'socket'
11
+ require 'thread'
12
+ require 'timeout'
13
+ require 'yaml'
14
+ require "stringio"
15
+ require "pp"
16
+ require "kconv"
17
+ require "fcntl"
18
+ require "json"
19
+
20
+ require "dango/shared/memory_store.rb"
21
+
22
+
23
+ class Object
24
+ # 1.8.5では実装済みの機能を1.8.4でも使えるようにしたもの
25
+ if RUBY_VERSION < '1.8.5'
26
+ def pretty_inspect
27
+ str = StringIO.open("", "w+") do |sio|
28
+ PP.pp(self, sio)
29
+ sio.rewind
30
+ sio.read
31
+ end
32
+
33
+ str
34
+ end
35
+ end
36
+
37
+ def deep_dup
38
+ Marshal.load(Marshal.dump(self))
39
+ end
40
+ end
41
+
42
+ # Timeクラスの表示形式修正
43
+ #class Time
44
+ # def to_s
45
+ # self.strftime("%Y-%m-%d %H:%M:%S")
46
+ # end
47
+ #end
48
+
49
+ # 例外定義
50
+ class DangoFrameworkError < StandardError; end
51
+ class DangoFrameworkTimeoutError < DangoFrameworkError; end
52
+ class DangoFrameworkConnectionError < DangoFrameworkError; end
53
+
54
+ # エラー出力用モジュール
55
+ module ErrorMessage
56
+ # デバッグ出力用のメソッド
57
+ def error_message(exception_class, code = nil)
58
+ if exception_class.kind_of?(Exception)
59
+ "#{exception_class.class} is not Exception class"
60
+ end
61
+
62
+ str = "#{exception_class.class}:#{exception_class.message}\n#{exception_class.backtrace.pretty_inspect}"
63
+ if code.downcase == "u" || code.downcase == "utf8"
64
+ str = str.toutf8
65
+ elsif code.downcase == "s" || code.downcase == "sjis"
66
+ str = str.tosjis
67
+ end
68
+ str
69
+ end
70
+ end
71
+
72
+ # ユーティティ
73
+ module DangoUtilModule
74
+ # 配列をランダムに並べ替え
75
+ # arrは順番を入れ替えたい配列
76
+ # numは入れ替え回数(省略時2、大きな数を入れると確実にランダムになるが時間が掛かる)
77
+ def array_random(arr, num = 2)
78
+ arr.sort_by{rand}
79
+
80
+ # ret_arr = arr.deep_dup
81
+ # (ret_arr.size * num).times do # メンバー数のnum倍の回数、入れ替え
82
+ # idx = rand(ret_arr.size)
83
+ # id = ret_arr.delete_at(idx)
84
+ # ret_arr.push(id)
85
+ # end
86
+ # ret_arr
87
+ end
88
+ end
89
+
90
+ # クライアントとサーバーで共有するモジュール
91
+ module DangoFrameworkModule
92
+ include ErrorMessage
93
+
94
+ CommMaxDigit = 5 # 通信の最大桁数
95
+ MaxLen = 1024 # 通信の一度の送信バイト数
96
+
97
+ DefaultDataType = 0 # デフォルトのデータのタイプ
98
+
99
+ # 0 = JSON
100
+ # 1 = YAML
101
+ # 2 = Marshal
102
+
103
+ # デバッグ出力用のメソッド
104
+ def cs_debug_print(str)
105
+ cs_logger.debug str
106
+ puts str if $VERBOSE
107
+ end
108
+
109
+ # ロガーの定義
110
+ def cs_logger
111
+ # if !@cs_logger # これだと警告が出てうざいので
112
+ if !self.instance_variables.find{|iv| iv == '@cs_logger'} # @cs_loggerが未定義なら
113
+ if @log_file != ""
114
+ log_dir = File.dirname(@log_file)
115
+ Dir.mkdir(log_dir) if !File.exist?(log_dir)
116
+ @cs_logger = Logger.new(@log_file)
117
+ @cs_logger.level = @log_level
118
+ # @cs_logger.datetime_format = "%Y-%m-%dT%H:%M:%S"
119
+ @cs_logger.datetime_format = "aaaaa"
120
+ @cs_logger.progname = "#{Thread.current.object_id}"
121
+ end
122
+ end
123
+ @cs_logger
124
+ end
125
+
126
+ # データ受信処理
127
+ def cs_receive_data(sock)
128
+ ret_data = ""
129
+
130
+ cs_logger.debug "receive_data(#{sock})"
131
+
132
+ begin
133
+ # size_str = sock.readpartial(CommMaxDigit + 1)
134
+ # size_str = sock.sysread(CommMaxDigit + 1)
135
+ size_str = sock.recv(CommMaxDigit + 1)
136
+ rescue EOFError
137
+ # EOFErrorはその接続終了時の切断と思われるのでDangoFrameworkConnectionErrorにしない
138
+ #raise(DangoFrameworkError, "failed to read sock for EOF reached.\n#{error_message($!, 'u')}")
139
+ raise(DangoFrameworkError, "failed to read sock for EOF reached.")
140
+ rescue
141
+ raise(DangoFrameworkConnectionError, "failed to read sock.\n#{error_message($!, 'u')}")
142
+ end
143
+
144
+ dtype, size = size_str[0, 5].unpack("cN")
145
+
146
+ raise(DangoFrameworkError, "size=0:size_str=#{size_str.inspect}") if size == 0
147
+
148
+ cs_logger.debug "size=#{size}:size_str=#{size_str.inspect}"
149
+
150
+ ret_data_orig = ""
151
+
152
+ while size > 0
153
+ read_len = MaxLen > size ? size : MaxLen
154
+ begin
155
+ # this_ret_data_orig = sock.readpartial(read_len)
156
+ # this_ret_data_orig = sock.sysread(read_len)
157
+ this_ret_data_orig = sock.recv(read_len)
158
+ rescue
159
+ raise(DangoFrameworkConnectionError, "failed to read sock(data).\n#{error_message($!, 'u')}")
160
+ end
161
+
162
+ ret_data_orig += this_ret_data_orig
163
+ # cs_logger.debug "size:#{size.inspect}:this_ret_data_orig:#{this_ret_data_orig.inspect}"
164
+
165
+ size -= this_ret_data_orig.size
166
+ # cs_logger.debug "size:#{size.inspect}"
167
+ end
168
+
169
+ # cs_logger.debug "ret_data_orig:#{ret_data_orig.inspect}"
170
+
171
+ ret_data = cs_receive_decrypt(ret_data_orig[0..-2])
172
+
173
+ @recv_count += 1 if @recv_count # 受信回数カウント
174
+ # cs_logger.debug "ret_data:#{ret_data.inspect}"
175
+
176
+ begin
177
+ if ret_data == "\n" # データが空ならparseしない
178
+ data = {}
179
+ elsif dtype == 0
180
+ data = JSON.parse(ret_data)
181
+ elsif dtype == 1
182
+ data = YAML::load(ret_data)
183
+ elsif dtype == 2
184
+ data = Marshal.load(ret_data)
185
+ end
186
+ rescue
187
+ raise(DangoFrameworkError, "data parse error.\nret_data=#{ret_data}")
188
+ end
189
+ end
190
+
191
+ # データ送信処理
192
+ def cs_send_data(sock, send_obj, options = {})
193
+ cs_logger.debug "cs_send_data"
194
+
195
+ dtype = options[:type] || DefaultDataType
196
+
197
+ if send_obj == {}
198
+ send_data_orig = ""
199
+ elsif dtype == 0
200
+ send_data_orig = JSON.generate(send_obj)
201
+ elsif dtype == 1
202
+ send_data_orig = send_obj.to_yaml
203
+ elsif dtype == 2
204
+ send_data_orig = Marshal.dump(send_obj)
205
+ end
206
+
207
+ send_data = cs_send_encrypt(send_data_orig) + "\n"
208
+ # send_data = cs_send_encrypt(send_data_orig) + "\r\n"
209
+ # send_data = cs_send_encrypt(send_data_orig) + "\015\012"
210
+
211
+ # cs_logger.debug "start send_data:#{send_data.inspect}"
212
+
213
+ size = send_data.size
214
+
215
+ if size >= 4294967296
216
+ raise(DangoFrameworkError, "max size over. size:#{size} >= 4294967296")
217
+ end
218
+
219
+ # size_str = sprintf("%0#{CommMaxDigit}d", size)
220
+
221
+ size_str = [dtype, size].pack("cN")
222
+ # cs_logger.debug "size=#{size}:size_str=#{size_str.inspect}"
223
+
224
+ send_buf = size_str + "\n" + send_data
225
+ # cs_logger.debug "send_buf:#{send_buf.inspect}"
226
+
227
+ begin
228
+ # sock.write send_buf
229
+ # sock.flush
230
+
231
+ # sock.send(size_str + "\n", 0)
232
+ # sock.flush
233
+ # sock.send(send_data, 0)
234
+ # sock.flush
235
+
236
+ sock.send(send_buf, 0)
237
+ sock.flush
238
+ rescue
239
+ raise(DangoFrameworkConnectionError, "sock write failed.\n#{error_message($!, 'u')}")
240
+ end
241
+
242
+ @send_count += 1 if @send_count # 受信回数カウント
243
+ # cs_logger.debug "finish send_data:#{send_data.inspect}"
244
+ sock
245
+ end
246
+ end
247
+