dango 0.0.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/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
+