====== AlWorker TCP/IPサーバー ====== require "al_worker_tcp" ---- TCPサーバーを作成する機能です。SMTPサーバのような、chat式のサーバ機能を実装しています。 {{:alworker:tcp_explain1.png?nolink|}} ====== サンプル ====== サーバー require "al_worker_tcp" class TcpServer < AlWorker # イニシャライザでTCPを用意する。 def initialize2() @tcp = Tcp.new( "", 10025 ) # リスンアドレス、ポート番号を指定。 @tcp.run( self ) end # helloコマンド # "helo localhost" def tcp_helo( sock, param ) sock.puts "250 hello welcome." return true end # mailコマンド # "mail from: " def tcp_mail( sock, param ) if /^from: ?(.+)$/ =~ param[""] sock.puts "250 Ok" else sock.puts "501 Syntax error." end return true end end server = TcpServer.new("tcp_server") server.parse_option() server.daemon() クライアント(以下の例は、Ruby基本機能のみ使用した例で、Aloneライブラリは使わず実装しています) require "socket" sock = TCPSocket.open("localhost", 10025) puts 'SEND "helo"' sock.puts "helo" puts 'RECEIVE ' + sock.gets puts 'SEND "quit"' sock.puts "quit" puts 'RECEIVE ' + sock.gets sock.close ===== 実行結果 ===== % ruby tcp_client.rb SEND "helo" RECEIVE 250 hello welcome. SEND "quit" RECEIVE 200 OK quit. ===== 解説 ===== AlWorkerを継承したクラスに “tcp_” をプレフィックスとして付与したメソッドを定義すると、フレームワークにTCPコマンドとして認識されます。\\ メソッドには、接続ソケット sock と、パラメータ param が渡されます。\\ パラメータはJSONパースされ、paramには結果のHashが渡されます。JSONパースできなかった場合は、param[""]にパース前の文字列が入ります。\\ 返り値に trueを指定すると継続してコマンドを受けつけ、falseを指定すると接続を切ります。\\ デフォルトで、接続を切るためのコマンド、"quit"が定義されています。\\ 定義されていないコマンドが送られた場合、"501 Error Command not implemented." を返します。\\ ====== 同期/非同期 ====== イベントハンドラはデフォルトで同期的に呼び出され、たとえば2つのクライアントから同時にコマンドを受信しても、一つずつ実行します。\\ 非同期にしたい場合は、”tcp_a_” をプレフィックスとして付けます。 def tcp_a_xxxx( sock, param ) # any code return true end ====== バイナリの送受信 ====== Chatプロトコルでは、テキストの送受信のみ想定しており、エンコーディングはデフォルトで UTF-8 です。\\ バイナリの送受信が必要な場合は、以下のようにエンコーディングを変更します。 @tcp.set_encoding(Encoding::ASCII_8BIT) ====== サービスモデル(スレッド/プロセス) ====== デフォルトで、スレッドモデルで動作します。\\ 接続ごとに別プロセスで動作させる(プロセスモデル)には、以下のように指定します。 @tcp.mode_service = :process ====== chat以外のプロトコルを使用 ====== chat方式ではないプロトコルを使いたい場合は、AlWorker::Tcpクラスを継承して、start_service()メソッドをオーバライドします。 すべてのGETリクエストに "It works!" と返すウェブサーバ require "al_worker_tcp" class WebServer < AlWorker::Tcp def start_service( sock ) req = [] while txt = sock.gets txt.chomp! break if txt.empty? req << txt end if !req.empty? && req[0].upcase.start_with?("GET ") sock.puts "HTTP/1.0 200 OK\r\n" sock.puts "Content-Type: text/html\r\n" sock.puts "\r\n" sock.puts "\r\n" sock.puts "TESTPAGE\r\n" sock.puts "

It works!

\r\n" end end end class TcpServer < AlWorker def initialize2() log.level = Logger::DEBUG @tcp = WebServer.new("", 3000) # リスンアドレス、ポート番号を指定。 @tcp.run( self ) end end server = TcpServer.new("web_server") server.daemon()