connect, getprotobyname, send, recv, socket, close■この章で解説するモジュール
Socket (sockaddr_in, inet_aton)■この章でできること
・低水準クライアントソケット
Perlでソケット通信をする場合、C言語やUNIXのシステムコールとよく似たインターフェースの低水準なソケットを使う方法と、よりインターフェースを単純化したIO::Socketモジュールを使う方法があります。ここでは前者の低水準なソケットを使って、クライアントソケット作る方法を解説します。
では、早速ソースを見てみましょう。
#!/usr/bin/perl ############################################# # 低水準クライアントソケットのサンプル # Author: "Perl Programming Tips" ############################################# use Socket; # TCPのプロトコル番号を取得 $tcpProtoNo = getprotobyname('tcp'); # SOCKET作成 socket(HSOCK, PF_INET, SOCK_STREAM, $tcpProtoNo) || die("socket() fail:$!\n"); # パックされたネットワークアドレスを作成 $address = sockaddr_in(3000, inet_aton('localhost')); # SOCKET接続 connect(HSOCK, $address) || die("connect() fail:$!\n"); # メッセージ送信 send(HSOCK, 'Message from client to server.', 0); # メッセージ受信 recv(HSOCK, $message, 30, MSG_WAITALL); print "$message\n"; # SOCKET切断 close(HSOCK);
まずはuse SocketでSocketモジュールをインポートしておきます。Perlの標準関数に組み込まれているソケット関連の関数は、connectやrecv、sendなどのプリミティブなものだけです。Socketモジュールはその他の補助的な関数や定義などが含まれています。なので、低水準なソケットを実装する場合もSocketモジュールの使用はほぼ必須になります。
最初にsocket関数に渡すためのTCPのプロトコル番号を取得しておきます。ソケットにはストリームソケット、ダイアグラムソケットがあり、それぞれTCP、UDPプロトコルを使います。ここではストリームソケットを使うので、getprotobynameでTCPのプロトコル番号を取得しています。
次はsocket関数でソケットをオープンし、ファイルハンドルに結びつけます。以降のソケット操作はこのファイルハンドルを指定することになります。socket関数の第2引数で指定しているのはソケットのネットワークドメインです。これはインターネットドメイン、UNIXドメインがあり、それぞれPF_INET、PF_UNIXを指定します。ここではPF_INETを指定しています。第3引数はストリームソケットの場合SOCK_STREAM、ダイアグラムソケットの場合SOCK_DGRAMを指定します。
この後はconect関数でソケットを接続しますが、その前にSocketモジュールの機能を使って接続先のアドレス、ポート番号の情報を作成しておきます。これはこれはインターネットドメインの場合sockaddr_in関数、UNIXドメインの場合sockaddr_ux関数を使用します。ポート番号はあらかじめサーバ、クライアント間でどの番号を使うか決めておきましょう。
これで準備ができたので、connect関数でサーバに接続します。当然、この時点でサーバ側がクライアントの接続待ち状態になっていないと接続できません。
コネクションが確立された後は、クライアント、サーバ間で自由にデータの送受信ができます。これにはいくつか方法がありますが、send、recv関数を使うのが簡単です。
ここで、ソケットのブロッキングについて触れておきましょう。ソケットはストリームソケットの場合、クライアント/サーバ間でsend、recvを使ってメッセージの送受信をしますが、これは同時に実行されるとは限りません。送信の方が早かった場合は、受信側のマシンの受信バッファに一時的にメッセージが置かれるので、たいていの場合(バッファがあふれたりしなければ)問題はありません。しかし逆に、まだsendで送信していないうちにrecvで受信しようとすると、失敗します。
これを避けるために、受信待ちをする仕組みが必要になってきます。つまり相手が送信してくるまで受信の口を開けて待っているということです。このように待ち状態にして同期されることをブロッキングといいます。
ソケットのメッセージ送受信でこれを実現する簡単な方法は、recv関数でMSG_WAITALLを指定することです。これを指定した場合、recv関数は指定したバイト数を受信し終わるまでは制御を戻しません。
ただし、この方法は何らかの原因で指定したバイト数を受信できなかった場合は、永久にブロックすることになるのでとても危険です。あくまで簡易的な方法だと考えたほうがいいでしょう。では安全に作るにはどうすればいいかというと、ここでは解説しませんが、select関数などを使ってブロッキングにタイムアウト処理を入れるのが一般的です。
では、最後にclose関数でソケットのクローズ処理を行います。