Rubyネットワークプログラミング入門!ソケットの基本とTCPサーバーの作り方
Rubyでオリジナルのネットワーククライアントやサーバーを作りたいと思ったことはありませんか?あるいは、その仕組みを理解したいだけかもしれません。
その場合、必ず「ソケット」と向き合うことになります。
この記事では、Rubyネットワークプログラミングの基礎を学び、Rubyを使って他のサーバーやクライアントと通信を始めるための方法をご紹介します。
ソケットとは何か?
ソケットとは、通信チャネルのエンドポイント(終端)のことです。クライアントとサーバーの両方が、このソケットを使って通信を行います。
その仕組みは非常にシンプルです。
接続が確立されると、ソケットにデータを書き込むことでデータが相手側へと送られます。受信側はソケットからデータを読み取り、受け取ったデータを処理します。
ソケットの種類
利用できるソケットにはいくつかの種類があります。最も一般的なのはTCPソケットで、HTTPやFTPなどTCPベースのサービスへの接続が可能です。
UDPベースのプロトコルを使う必要がある場合は、UDPソケットを使用します。
その他のソケットはやや特殊なものです。Unixドメインソケットは、Unixシステム上でTCP接続のオーバーヘッドなしにプロセス間通信(IPC)を実現できる仕組みです。
Rubyでのソケットの使い方
ソケットで何ができるのかがわかったところで、実際に使ってみましょう。
まず、ソケットライブラリをプログラムに読み込みます。
require 'socket'
TCPソケットを作成するには、TCPSocketクラスを使用します。引数には接続先のIPアドレスとポート番号を指定します。
これにより接続の確立が試みられ、接続できなかった場合はErrno::ECONNREFUSEDエラーが発生します。
socket = TCPSocket.new('google.com', 80)
これでソケットを通じてメッセージを送信できるようになります。相手側に内容を正しく理解してもらうには、通信先のプロトコルに従う必要があります。
socket.write "GET / HTTP/1.1" socket.write "\r\n\r\n"
使用するメソッドの多くは、TCPSocketの親クラスから継承されたものです。
サーバーからのレスポンスを読み取るには、recvメソッドを使います。
引数には、ソケットから読み取りたいバイト数を指定します。
puts socket.recv(100)
ここで少し問題があります。データが返ってこず、アプリが何もしていないように見えることがあるのです。
その理由は、読み取れるデータが十分にない場合、プログラムが「ブロック」されるからです。
つまり、データが利用可能になるか、サーバーが接続を閉じるまで待機し続けるということです。
扱うプロトコルに応じて、読み取るデータ量を増減させるとよいでしょう。
ブロッキングが問題になる場合は、IOクラスのreadpartialメソッドやread_nonblockメソッドを確認してみてください。
TCPサーバーの書き方
次にサーバーを作ってみましょう!手順はクライアントの作成と似ていますが、ソケットにインターフェースをバインド(bind)させ、その上でリッスン(listen)し、最後に着信接続を受け入れる(accept)必要があります。
TCPServerクラスは、最初の2つの処理を自動的に行ってくれます。
以下はその例です。
require 'socket'
socket = TCPServer.new('0.0.0.0', 8080)
client = socket.accept
puts "New client! #{client}"
client.write("Hello from server")
client.close
このサンプルサーバーはポート8080で待ち受け、接続してきたクライアントにメッセージを送信します。
注目すべきは、受け入れられるクライアントは1つだけで、その後プログラムが終了してしまうという点です。
複数のクライアントを受け入れる
複数のクライアントを受け入れて応答するには、ループとスレッドを組み合わせる必要があります。
例:
require 'socket'
PORT = 8081
socket = TCPServer.new('0.0.0.0', PORT)
def handle_connection(client)
puts "New client! #{client}"
client.write("Hello from server")
client.close
end
puts "Listening on #{PORT}. Press CTRL+C to cancel."
loop do
client = socket.accept
Thread.new { handle_connection(client) }
end
これで、停止するまで待ち受けを続けるサーバーが起動します。
さらにレベルアップして、Rubyで本格的なWebサーバーを書く方法を学びたい方は、関連のブログ記事をぜひ参照してください。
まとめ
この記事では、TCPソケットとは何か、その仕組み、そしてTCPServerやTCPSocketといったRubyのクラスを使って、インターネット上の他のマシンと通信できるアプリケーションを作る方法を学びました。
Rubyのネットワークプログラミングで遊ぶのはとても楽しいものです!
さあ、クールなものを作って、コメント欄でみんなにシェアしてください 🙂
また、Rubyのスキルを向上させ続けたい方は、ニュースレターへの登録もお忘れなく!
-
Rubyで新しいプログラミング言語を作る:インタプリタ編
GitHubで完全なソースコードを公開中 Stoffleプログラミング言語の完全な実装はGitHubで公開しています。バグを見つけたり、疑問点がある場合は、ぜひIssueを立ててください。 この記事では、Rubyだけで作られたおもちゃのプログラミング言語「Stoffle」のインタプリタ実装を始めます。このプロジェクトについては、シリーズ第1回目の記事で詳しく紹介しているので、まだ読んでいない方はそちらもチェックしてみてください。 今回構築するのは、いわゆる「ツリーを辿るインタプリタ(tree-walk interpreter)」と呼ばれるものです。前回の記事では、フラットなトークンの
-
Rubyで実践する関数型プログラミング完全ガイド ― 純粋関数・イミュータブルデータ・カリー化の基本
Rubyを書いていると、「関数型プログラミング」という言葉を目にする機会が増えてきます。しかし、実際にどんなものなのか、自分のコードに取り入れるべきなのか、疑問に感じている方も多いのではないでしょうか。 関数型プログラミングとは、具体的に何を指すのか? オブジェクト指向プログラミング(OOP)とは何が違うのか? Rubyでも関数型的な書き方を採用すべきなのか? この記事では、これらの疑問にわかりやすく答えながら、Rubyで今日から使える関数型プログラミングの考え方とテクニックを解説します。 関数型プログラミングとは? 関数型プログラミングは一時的な流行や難しい専門用語ではなく、長い歴史を持