TCP/IP通信と言うと、サーバ・クライアントシステムを思い浮かべますが、行き成り外部とデータの遣り取りをするのは危なっかしい気がしますので、テスト環境にも利用される(?)同一パソコン内のプロセス間通信(ソケット通信)を試してみる事に。
試しに、WinSockAPIを使って、内部プロセス間通信(データの送受信)を行うだけのサンプルを作ってみました。
sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
上の2つの代入文は、どちらも同じ値を代入します。分かり易い(ピンときた)方を選ぶと良いかも。
因みに、非常に難解なTCP/IP通信をぎゅっと短く平たく(間違いを恐れず)大雑把に言うと、通信する相手<パソコン>をIPアドレスを使って特定する役割を担うのがIP、通信する相手の種類<アプリケーション>をポート番号を使って指定する役割を担うのがTCP、2つ合わせて正確無比な通信をインターネット上で実現しています。
<注意点>
・WinSockAPIの accept, send, recv は、通常ブロッキング動作をします。Windows ではノンブロッキング動作にすることも出来るようですが、select を使った監視処理の方がお勧め(?)のようです。
・ブロッキング動作は利用者にすれば、アプリケーションが固まってしまったように見えます。
・プログラムはWinSockの利用を終了する時、WSACleanup APIを呼び出して、WinSockがプログラムのために確保した全てのリソースを解放しなければなりません。
・プログラムは WSAStartup APIの呼び出し成功数に応じて、WSACleanup APIを呼び出さなければなりません。最後の WSACleanup API呼び出しのみが実際のリソース解放を行います。それ以外は単に、WS2_32.DLL が持つ内部参照カウント数を減算するだけです。
・重要:sa.sin_addr.s_addr = INADDR_ANY; の設定は外部に無造作に通信窓を開けるため、セキュリティーホールに成り得ます。実際、IPアドレス 204.205.206.207 という得体の知れない(多分偽装?)相手から、接続要求を受け取る羽目に。安易に INADDR_ANY を利用すると、きっと痛い目に合う事に・・・。
【SOCKADDR構造体】
メンバー名 | 型 | 説明 |
---|---|---|
sa_family | unsigned short | アドレスファミリを表す定数 *1 |
sa_data | char [14] | sa_family の固有情報を表すための空き領域 |
*1:通常はAF_INET(IPv4インターネットプロトコル)
【SOCKADDR_IN構造体】
メンバー名 | 型 | 説明 |
---|---|---|
sin_family | short | アドレスファミリを表す定数 *1 |
sin_port | unsigned short | ポート番号 |
sin_addr | IN_ADDR | IPアドレス |
sin_zero | char [8] | 値0で初期化される空き領域 |
*1:通常はAF_INET(IPv4インターネットプロトコル)
お試し環境
WindowsXP 32bit Edition
Visual C++ 2008
/*---- サーバの作成 --------------------- コマンドライン -------------------*/
D:\vc2008\dllchk>cl server.c ws2_32.lib
/*----------------------------------------------------------------------------*/
/*---- クライアントの作成 ------------------ コマンドライン ----------------*/
D:\vc2008\dllchk>cl client.c ws2_32.lib
/*----------------------------------------------------------------------------*/
/*---- client の引数がホスト名----------------- お試し結果 -----------------*/
D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2401
From Client : len = 23, data = Let's try Communication
D:\vc2008\dllchk>client localhost
ホスト名でのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication
/*----------------------------------------------------------------------------*/
/*---- client の引数がアドレス----------------- お試し結果 -----------------*/
D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2417
From Client : len = 23, data = Let's try Communication
D:\vc2008\dllchk>client 127.0.0.1
アドレスでのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication
/*----------------------------------------------------------------------------*/
/*---- server.c ----------------------- お試しソース -----------------------*/
#include <stdio.h>
#include <winsock2.h>
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET s, c; // s - server socket, c - client socket
SOCKADDR_IN sa, ca; // sa - server address, ca - client address
int len;
char buf[64];
// WinSock の初期化
if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
printf("WSAStartup error\n");
return(1);
}
// ソケットの作成
sa.sin_family = AF_INET;
if((s = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
printf("socket error : %d\n", WSAGetLastError());
return(1);
}
// ソケットに名前を付ける
sa.sin_port = htons(54321);
// sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(s, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
printf("bind error : %d\n", WSAGetLastError());
return(1);
}
// 接続要求待ち
if(listen(s, 1) == SOCKET_ERROR) {
printf("listen error : %d\n", WSAGetLastError());
return(1);
}
// 接続要求受付
len = sizeof(ca);
if((c = accept(s, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
printf("accept error : %d\n", WSAGetLastError());
return(1);
} else {
printf("接続要求受付:クライアントアドレス = %s、ポート = %d\n", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
}
if(send(c, "Wellcome to WinSock Communication", 33, 0) < 1) {
printf("send error : %d\n", WSAGetLastError());
return(1);
}
memset(buf, 0, sizeof(buf));
if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
printf("recv error : %d\n", WSAGetLastError());
return(1);
}
printf("From Client : len = %d, data = %s\n", len, buf);
// WinSock 切断
shutdown(s, SD_BOTH);
// ソケットの破棄
closesocket(s);
closesocket(c);
// WinSock のリソース解放
WSACleanup();
return(0);
}
/*----------------------------------------------------------------------------*/
/*---- client.c ----------------------- お試しソース -----------------------*/
#include <stdio.h>
#include <winsock2.h>
int main(int argc, char *argv[])
{
WSADATA wsaData;
LPHOSTENT host;
SOCKET c; // c - client socket
SOCKADDR_IN sa; // sa - server address
int i, len;
char buf[64];
if(argc != 2) {
printf("Usage : %s dest\n", argv[0]);
return(1);
}
// WinSock の初期化
if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
printf("WSAStartup Error\n");
return(1);
}
if((c = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
printf("socket error : %d\n", WSAGetLastError());
return(1);
}
sa.sin_family = AF_INET;
sa.sin_port = htons(54321);
sa.sin_addr.s_addr = inet_addr(argv[1]);
if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
if((host = gethostbyname(argv[1])) == NULL) {
printf("ホスト名の取得に失敗しました : %s\n", argv[1]);
return(1);
}
// サーバへの接続要求
for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
// sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
break;
}
}
if((IN_ADDR *)host->h_addr_list[i] == NULL) {
printf("connect1 error : %d\n", WSAGetLastError());
return(1);
}
printf("ホスト名でのサーバ接続完了\n");
} else { // 入力がドット表記のアドレスなら
// サーバへの接続要求
if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
printf("connect2 error : %d\n", WSAGetLastError());
return(1);
}
printf("アドレスでのサーバ接続完了\n");
}
memset(buf, 0, sizeof(buf));
if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
printf("recv error : %d\n", WSAGetLastError());
return(1);
}
printf("From Server : len = %d, data = %s\n", len, buf);
if(send(c, "Let's try Communication", 23, 0) < 1) {
printf("send error : %d\n", WSAGetLastError());
return(1);
}
// ソケットの破棄
closesocket(c);
// WinSock のリソース解放
WSACleanup();
return(0);
}
/*----------------------------------------------------------------------------*/
/*============================================================================*/