gihyo.jp この書籍を参考に、RubyでTCPサーバ/クライアントを作ってみました。
目次
TCPとソケット
TCP(Transmission Control Protocol)は、コンピュータネットワーク上で信頼性のあるデータ転送を提供する通信プロトコルです。TCP通信には、ソケット(socket)というAPIを使用します。サーバ側ソケットがクライアントからの接続を待ち受け、クライアント側ソケットでサーバのホストとポートを指定して接続します。これにより、双方向のデータ送信が可能な通信経路が確立されます。
今回は、Rubyのsocketライブラリを利用して、簡単なTCPサーバ/クライアントを作ってみました。
動作環境
サーバ側
初期化
class TcpServer def initialize @server = TCPServer.open(8001) @client = nil end end
TCPServer クラスのopen
メソッドを使用して、ポート番号 8001 での接続を待ち受けるサーバーソケットを作成します。
TCPServer.open
メソッドは、指定したポート番号にバインドされた新しい TCPServer オブジェクトを返します。
これでクライアントからの接続を受け付ける準備が整います。
クライアントとの接続を表すソケットを初期化するために、@clientインスタンス変数を nilで初期化しています。 この変数は、クライアントとの接続が確立された後にソケットオブジェクトを保持するために使用します。
待機
def accept puts 'waiting for connection...' @client = @server.accept end
accept
メソッドは、クライアントからの接続があるまで処理をブロックします。
接続があるとクライアントとの通信用のソケットを返すので、それを@client変数に代入します。
データ受信
def recv puts 'connected' # 受け取った内容をsever_recv.txtに書き込む File.open('server_recv.txt', 'w') do |f| while line = @client.gets break if line.chomp == '0' f.write(line) end end end
TCPサーバー内でクライアントからのデータを受信し、それを server_recv.txt
ファイルに書き込むための処理を行っています。
ファイルを書き込みモードで開きます。
@client.gets
で、クライアントからのデータを1行ずつ取得し、受信したデータが '0'のときにループを終了します。
受信したデータを server_recv.txt
ファイルに書き込みます。
データ送信
def send(_data) File.open('server_send.txt', 'r') do |f| while line = f.gets @client.puts(line) end end end
server_send.txt
ファイルを読み込みモードで開き、ファイルから行ごとにデータを読み込みます。
puts
は、クライアントのソケットに対してデータを送信するメソッドで、ファイルから読み込んだ行ごとにクライアントにデータを送信しています。
切断
def close @client.close end
切断します。
ソースコード
require 'socket' # tcpサーバークラス class TcpServer # 初期化 def initialize @server = TCPServer.open(8001) @client = nil end def run accept recv send('server_send.txt') close end private # クライアントからの接続を待つ def accept puts 'waiting for connection...' @client = @server.accept end # クライアントからのデータを受信する def recv puts 'connected' # 受け取った内容をsever_recv.txtに書き込む File.open('server_recv.txt', 'w') do |f| while line = @client.gets break if line.chomp == '0' f.write(line) end end end # クライアントにデータを送信する def send(_data) File.open('server_send.txt', 'r') do |f| while line = f.gets @client.puts(line) end end end def close puts 'connection closed' @client.close end end # サーバーの起動 server = TcpServer.new server.run
クライアント側
初期化
class TcpClient # 初期化 def initialize @client = TCPSocket.open('localhost', 8001) end end
TCPSocket.open
メソッドを使用して、指定されたホスト名とポート番号でソケットを開きます。
送信
def send(_data) # client_send.txtの内容をサーバーに送信する File.open('client_send.txt', 'r') do |f| while line = f.gets @client.puts(line) end end # サーバーに0を送信する @client.puts('0') end
puts
は、サーバーのソケットに対してデータを送信するメソッドで、ファイルから読み込んだ行ごとにサーバーにデータを送信しています。
最後にサーバーに '0' を送信し、通信の終了を示します。
ソースコード
require 'socket' # tcpクライエントクラス class TcpClient # 初期化 def initialize @client = TCPSocket.open('localhost', 8001) end def run send('client_send.txt') recv close end private def send(_data) # client_send.txtの内容をサーバーに送信する File.open('client_send.txt', 'r') do |f| while line = f.gets @client.puts(line) end end # サーバーに0を送信する @client.puts('0') end def recv # サーバーからのデータを受信する File.open('client_recv.txt', 'w') do |f| while line = @client.gets f.write(line) end end end def close @client.close end end # クライアントの起動 client = TcpClient.new client.run
実行
client_send.txt
とserver_send.txt
ファイルを用意する
- client_send.txt
My name is Mike
- server_send.txt
Hello, Mike!
起動
- サーバ側
$ ruby TCP/tcp_server.rb waiting for connection...
- クライアント側
$ ruby TCP/tcp_client.rb
確認
server_send.txt
と同内容のclient_recv.txt
が出来ていれば成功です。
TCPサーバをWebブラウザで叩く
tcp_server.rb
を本物のWebブラウザで叩いてみます。
サーバ起動
$ ruby TCP/tcp_server.rb
URL入力
ブラウザにhttp://localhost:8001/index.html
と入力する
確認
server_recv.txt
でHTTPリクエストを確認できます。
TCPクライアントでWebサーバを叩く
次はWebサーバがブラウザに対して何を返すのか確認してみます。 書籍ではApacheを使っていますが、面倒なので、実際のWebサイトのアドレスを叩いてみます。
tcp_client.rbの書き換え
def initialize @client = TCPSocket.new(ENV.fetch('WEBSITE', nil), 80) end
TCPSocket.new メソッドを使用して、指定されたホストとポート番号での新しいソケットを作成します。 secureでない接続が出来てしまうサイトを晒すのもどうかと思うので、URLは環境変数にして読み込んでいます。
def send request = "GET / HTTP/1.1\r\n" request += ENV.fetch('HOST', nil) request += "Connection: close\r\n\r\n" @client.puts(request) end
send
メソッドも書き換えます。
# @client.puts('0')
'0'の送信は不要なのでコメントアウトします
起動
$ ruby TCP/tcp_client.rb
確認
client_recv.txt
にHPPTレスポンスが出力されたら成功です。
思ったより簡単にできました🎉
ソースコード
https://github.com/kei-kmj/HenacatRuby/commit/def12ca9a209686fd1b9910947a9f653f5b42c5f