====== 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()