TCPを使う

ここでは、winsockを使ってTCPによる通信をする方法を説明したいと思います。

TCPとは

インターネットでの通信の90%以上はTCPによるものだと言われています。 通信を行うプログラムを書く場合、ほとんどがTCPによるものになると思います。

インターネットは信頼性のない通信路です。 信頼性がないインターネットでは、通信中にパケットロス、ビットエラー、順番の入れ替えなどが発生する可能性があります。 TCPは、そのような通信路上の障害を隠蔽してくれます。 具体的には、パケットロスが発生したら再送を行ったり、順番が入れ替わると正しい順番に直したりしています。

TCPによる通信はサーバとクライアントの2者間で行われます。 サーバは通信要求が来るまで待ち続けます。 TCPによる通信は、クライアントがサーバに対して接続要求を出すことから始まります。 サーバが接続要求を受け付けるとクライアントとサーバの間に仮想的な接続(バーチャルサーキット)が出来上がります。

プログラムを書く場合、そのソケットがそのバーチャルサーキットを表します。 ユーザがソケットに対して書き込みを行うと、反対側のソケットにそのデータがそのまま転送されます。 そのため、ソケットの両側のユーザは通信路上でのパケットロスなどの障害を気にすることなく、ソケットに対する書き込みと読み出しを行えば通信が行えてしまいます。

TCPプログラミング

TCPによる通信を行うプログラムの書き方はサーバとクライアントで異なります。 以下に、サーバとクライアントのプログラム作成手順概要を示します。

サーバ
サーバは、クライアントからの接続要求を待ちます。 接続要求を待つには、どのような待ち方をするかを設定しないといけません。 設定する情報としては、例えば、接続待ちをするTCPポート番号があります。 一度接続が出来上がってしまえば、サーバとクライアントで通信方法に違いはありません。
  • ソケットを作る
  • 接続待ちをするIPアドレスとポートを設定する
  • ソケットに名前をつける(bindする)
  • 接続待ちする
  • クライアントからの接続を受け付ける
  • 通信を行う
クライアント
クライアントは、特定のIPアドレス+TCPポート番号で接続待ちをしているサーバに対して接続要求を出します。 接続が成功すると、通信を開始できます。 一度接続が出来上がってしまえば、サーバとクライアントで通信方法に違いはありません。
  • ソケットを作る
  • 接続相手を設定する
  • 接続する
  • 通信を行う

単純なTCPサーバ

winsockを使った簡単なサーバのサンプルを以下に示します。 コードを簡単にするため、エラー処理は省いてあります。 実際にコードを書く場合にはエラー処理も行ったコードにして下さい。


#include <winsock2.h>

int
main()
{
 WSADATA wsaData;
 SOCKET sock0;
 struct sockaddr_in addr;
 struct sockaddr_in client;
 int len;
 SOCKET sock;

 // winsock2の初期化
 WSAStartup(MAKEWORD(2,0), &wsaData);

 // ソケットの作成
 sock0 = socket(AF_INET, SOCK_STREAM, 0);

 // ソケットの設定
 addr.sin_family = AF_INET;
 addr.sin_port = htons(12345);
 addr.sin_addr.S_un.S_addr = INADDR_ANY;
 bind(sock0, (struct sockaddr *)&addr, sizeof(addr));

 // TCPクライアントからの接続要求を待てる状態にする
 listen(sock0, 5);

 // TCPクライアントからの接続要求を受け付ける
 len = sizeof(client);
 sock = accept(sock0, (struct sockaddr *)&client, &len);

 // 5文字送信
 send(sock, "HELLO", 5, 0);

 // TCPセッションの終了
 closesocket(sock);

 // winsock2の終了処理
 WSACleanup();

 return 0;
}

上記TCPサーバは、接続してきたクライアントに対して「HELLO」という文字列を送信して終了します。

単純なTCPクライアント

winsockを使った簡単なクライアントのサンプルを以下に示します。 コードを簡単にするため、エラー処理は省いてあります。 実際にコードを書く場合にはエラー処理も行ったコードにして下さい。


#include <stdio.h>
#include <winsock2.h>

int
main()
{
 WSADATA wsaData;
 struct sockaddr_in server;
 SOCKET sock;
 char buf[32];

 // winsock2の初期化
 WSAStartup(MAKEWORD(2,0), &wsaData);

 // ソケットの作成
 sock = socket(AF_INET, SOCK_STREAM, 0);

 // 接続先指定用構造体の準備
 server.sin_family = AF_INET;
 server.sin_port = htons(12345);
 server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

 // サーバに接続
 connect(sock, (struct sockaddr *)&server, sizeof(server));

 // サーバからデータを受信
 memset(buf, 0, sizeof(buf));
 int n = recv(sock, buf, sizeof(buf), 0);

 printf("%d, %s\n", n, buf);

 // winsock2の終了処理
 WSACleanup();

 return 0;
}

上記TCPクライアントは、サーバに接続すると文字列を受信して表示します。

プロフェッショナルIPv6解説動画シリーズ再生リスト

動画で学ぶ「プロフェッショナルIPv6」を作っています。 もしよろしければご覧ください。お楽しみいただければ幸いです!