信頼性の高い通信を行なうためには両方のホストの間でコネクションを確立 したストリーム型通信が使われます。メッセージの順番に意味がある場合や、 相互にハンドシェークする場合などメッセージの到着の前後が違ったり欠落し たりしては困るものです。どのようなサービスがtcpを用い、どのようなサー ビスがudpを用いているのか/etc/servicesやy pcat servicesで調べてみてく ださい。
実装の上ではストリーム型通信もソケット通信を用いて実現されています。 ですから前節で見たソケット通信がそのまま使えます。違うところはドメイン がPF_INETになり、名前の与え方がすこし変わることです。ソケットを開きま しょう。まずはクライアントから。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
int fd;
struct sockaddr_in sa;
struct hostent *hp;
/* ストリームソケット */
fd = socket( PF_INET, SOCK_STREAM, 0 );
if( fd == -1 ) { /* 戻り値が-1はエラー */ }
/* アドレスファミリーはAF_INET */
sa.sin_family = AF_INET;
/* ポート番号はアプリケーションごとに */
sa.sin_port = htons( 100001 );
if( hp = gethostbyname( "yaoya" ) ) /* 相手のアドレス */
sa.sin_addr.s_addr =
*( unsigned long * ) ( * hp -> h_addr_list );
if( connect( fd, ( struct sockaddr * ) &sa,
sizeof( sa ) ) == -1 )
{ /* -1は接続失敗 */ }
これより後はsendやrecvを使って送受信した後shutdownしてcloseします。
if( send( fd, sbuf, strlen( sbuf ), 0 ) == -1 )
{ /* 戻り値が-1はエラー */ }
bzero( rbuf, sizeof( rbuf ) );
len = recv( fd, rbuf, sizeof( rbuf ), 0 );
if( len == -1 ) { /* error */ }
shutdown( fd, 2 );
close( fd );
bzeroはバッファを0で初期化します。recvは行末のヌル文字を書いてくれない のでこの処理が必要です。shutdownの二つ目の引数2は送受信ともにシャット ダウンすることを示しています。
次にサーバ側を見ましょう。サーバ側は誰から繋がるかはわからないのでア ドレスの設定が異なります。
/* インクルードファイルや宣言はクライアントと同じ */
int fd, ac, len;
struct sockaddr_in sa, sc;
fd = socket( PF_INET, SOCK_STREAM, 0 );
sa.sin_family = AF_INET;
sa.sin_port = htons( 100001 );
sa.sin_addr.s_addr = INADDR_ANY;
if( bind( fd, ( struct sockaddr * ) &sa,
sizeof( sa ) ) == -1 )
{ /* bindに失敗 */ }
if( listen( fd, 5 ) == -1 )
{ /* listenに失敗 */ }
len = sizeof( sc );
ac = accept( fd, ( struct sockaddr * ) &sc, &len );
if( ac == -1 )
{ /* acceptに失敗 */ }
ソケットをまず開くところは同じです。つぎに自分のソケットにポート番号を 割り付けるためbindを呼びます。クライアント側のconnectで使ったポート番 号と同じにしなければなりません。アドレスとしては誰からでもよいので INADDR_ANYを使います。次にクライアントからの接続を受け付ける手続き listenを呼びます。この例で示した引数の5は接続のための待ち行列のエント リーの数です。これでサーバはクライアントからの接続を受け付けられる状態 になりました。以後はアクセプトした相手と繋がっているソケットacを使って 送受信します。acceptの引数scには接続された相手のアドレスが書かれます。 lenをscのサイズで初期化しているのを忘れないでください。
len = recv( ac, rbuf, sizeof( rbuf ), 0 );
if( len == -1 )
{ /* recvに失敗 */ }
if( send( ac, sbuf, strlen( sbuf ), 0 ) == -1 )
{ /* sendに失敗 */ }
shutdown( ac, 2 );
close( ac );
要求のあったクライアントとの接続acはこれで閉じられましたが、もとのソケッ トfdはまだ開いています。これによりサーバは次の接続要求をfdを使って受け 取ることになります。サーバが終了するときはfdも閉鎖する必要があります。
はたしてうまく接続できたでしょうか。ソケットの接続状況を調べるコマン ドはnetsta tです。アドレスファミリーAF_INETで開かれているソケットを次 のようにして見ることができます。
% netstat -af inet
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
udp 0 0 *.2739 *.*
udp 0 0 *.2736 *.*
...
tcp 0 0 *.10001 *.* LISTEN
...
これはサーバ側(ホスト名yaoya)です。INADDR_ANYで(*)接続を待っている (LISTEN)ポート番号10001のtcpソケットがあることがわかります。同じように クライアント側(ホスト名s akanaya)では例えば
tcp 0 0 sakanaya.4658 yaoya.10001 ESTABLISHED
などのように見えるはずです。ESTABLISHEDは接続が確立していることを表わ しています。詳しくはman netstatを見てください。