2007年6月16日土曜日

プロセス間通信の方法の一つとしてsocketを使う

概要

プロセス間通信の方法の一つとしてsocketを使う。
通信を行う二つのプロセスのうち、一方をserver、もう一方をclientとする。
serverは通信を行うための口を用意する。
通信の準備

通信を行う口は以下の手順で用意する。これはserverが行う。

* socketを作る。(socket)
* socketに名前を付ける。(bind)
* 受け入れOKを示す。(listen)

これで通信を行う口は用意された。この後、clientは自分で作ったsocketをこの口に結合(connect)し、serverはそれを受け入れる(accept)。serverのsocketに名前を付けるのは、clientがその名前のsocketに結合するのに識別名が必要だからである。
connectとacceptはどちらが先に発行されてもかまわない。お互い結合されるのを待つ。

socketの名前

socketを作るとき、定義域としてAF_UNIXかAF_INETを指定する。それぞれ、UNIXドメイン、INETドメインと呼ばれる。
名前を付けるときはそのドメインの中で一意でなければならない。
UNIXドメインは、ひとつの計算機がその範囲である。この中で一意であると保証するのに簡単な方法としてファイルがある。そこで、名前を作るとその名前のファイルが作られる。こうすれば、そのシステムで一意であることが保証される。ただし、作られたファイルは残ってしまう。通信が終了しても自動的に消してはくれない。
INETドメインでは、ネットワークで結合されたすべてがその範囲である。この中で一意に名前を定めるのは容易ではない。そこで、まず計算機にネットワークで一意な名前を付け、それと組合せて名前を付ける。具体的には、計算機に付けられる名前はIPアドレスである、組み合わされるのはポート番号である。つまり、IPアドレス+ポート番号でINETdドメインで一意な名前を付ける。
通信

お互い結合してしまえば、後は通信するだけである。
送信はsend/writeで、受信はrecv/readで行う。socketを作るとファイルディスクリプタが与えられるので、低レベルのファイルアクセスと大体同じことができるので、read/writeも使える。通信と言ってもそんなに特殊なことをするわけではない。
送信は送りつけるだけだが、受信は相手から何か送られてくるのを待つので、間違えるとデッドロックしてしまう。ただし、結合そのものが切れてしまうと、recv/readで待っていた場合受信データ長0で抜けてきてくれる。
通信の終了

プロセスそのものを終わらせてしまってもいいのだが、一応終了の手順は以下の通りである。

* 結合を切る(shutdown)
* socketをクローズ(close)

UNIXドメインの場合、付けた名前のファイルが残るので、これを消しておく(unlink)。

プログラム例

以下のプログラム例は、serverがclientからのメッセージを受信して表示するものである。

* UNIXドメイン版 server
* UNIXドメイン版 client
* INETドメイン版 server
* INETドメイン版 client
* Makefile

複数のclientと通信するserver

ここまでは基本ということで1対1の通信の話をしてきたが、serverと呼ばれるものであれば複数のclientを通信する場合もある。こういう場合、selectを使うとよい。
これは、待ちたいフィルディスクリプタのリストを渡して、どれか来たら抜けて来てくれる。時間を指定すればその時間を過ぎれば何も来ていなくても抜けて来てくれる。
以下のプログラム例は、これを使って、複数のclientからの要求を処理するserverと、1秒毎にメッセージを送るclientである。UNIXドメイン版のみだが、もちろんINETドメインでも動作する。また、無限ループするプログラムなので、signalで終了を検知した場合終了処理をするようにしてある。

* server
* client
* Makefile

実際に使って気付いたこと

実際使ってみて気付いたことを以下に記すが、いろいろな環境で試したわけではないので、もしかしたら大丈夫かもしれない。

* selectを使って、複数のファイルディスクリプタを扱えるのはいいのだけど、最大で64までしか使えない。これ以上になると落ちる。
後でわかったことだが、一つのプロセスがオープンできるファイルの数は、limits.hのOPEN_MAXで既定されていて、これが64だから。(1998.11.19)
* UNIXドメインやINETドメインでもlocalhostの場合は、送受信はどんなデータでも大丈夫だが、INETドメインで別の計算機と通信する場合は文字列でないとちゃんとデータの受け渡しができない。

プロセス間通信の方法の一つとしてsocketを使う

概要

プロセス間通信の方法の一つとしてsocketを使う。
通信を行う二つのプロセスのうち、一方をserver、もう一方をclientとする。
serverは通信を行うための口を用意する。
通信の準備

通信を行う口は以下の手順で用意する。これはserverが行う。

* socketを作る。(socket)
* socketに名前を付ける。(bind)
* 受け入れOKを示す。(listen)

これで通信を行う口は用意された。この後、clientは自分で作ったsocketをこの口に結合(connect)し、serverはそれを受け入れる(accept)。serverのsocketに名前を付けるのは、clientがその名前のsocketに結合するのに識別名が必要だからである。
connectとacceptはどちらが先に発行されてもかまわない。お互い結合されるのを待つ。

socketの名前

socketを作るとき、定義域としてAF_UNIXかAF_INETを指定する。それぞれ、UNIXドメイン、INETドメインと呼ばれる。
名前を付けるときはそのドメインの中で一意でなければならない。
UNIXドメインは、ひとつの計算機がその範囲である。この中で一意であると保証するのに簡単な方法としてファイルがある。そこで、名前を作るとその名前のファイルが作られる。こうすれば、そのシステムで一意であることが保証される。ただし、作られたファイルは残ってしまう。通信が終了しても自動的に消してはくれない。
INETドメインでは、ネットワークで結合されたすべてがその範囲である。この中で一意に名前を定めるのは容易ではない。そこで、まず計算機にネットワークで一意な名前を付け、それと組合せて名前を付ける。具体的には、計算機に付けられる名前はIPアドレスである、組み合わされるのはポート番号である。つまり、IPアドレス+ポート番号でINETdドメインで一意な名前を付ける。
通信

お互い結合してしまえば、後は通信するだけである。
送信はsend/writeで、受信はrecv/readで行う。socketを作るとファイルディスクリプタが与えられるので、低レベルのファイルアクセスと大体同じことができるので、read/writeも使える。通信と言ってもそんなに特殊なことをするわけではない。
送信は送りつけるだけだが、受信は相手から何か送られてくるのを待つので、間違えるとデッドロックしてしまう。ただし、結合そのものが切れてしまうと、recv/readで待っていた場合受信データ長0で抜けてきてくれる。
通信の終了

プロセスそのものを終わらせてしまってもいいのだが、一応終了の手順は以下の通りである。

* 結合を切る(shutdown)
* socketをクローズ(close)

UNIXドメインの場合、付けた名前のファイルが残るので、これを消しておく(unlink)。

プログラム例

以下のプログラム例は、serverがclientからのメッセージを受信して表示するものである。

* UNIXドメイン版 server
* UNIXドメイン版 client
* INETドメイン版 server
* INETドメイン版 client
* Makefile

複数のclientと通信するserver

ここまでは基本ということで1対1の通信の話をしてきたが、serverと呼ばれるものであれば複数のclientを通信する場合もある。こういう場合、selectを使うとよい。
これは、待ちたいフィルディスクリプタのリストを渡して、どれか来たら抜けて来てくれる。時間を指定すればその時間を過ぎれば何も来ていなくても抜けて来てくれる。
以下のプログラム例は、これを使って、複数のclientからの要求を処理するserverと、1秒毎にメッセージを送るclientである。UNIXドメイン版のみだが、もちろんINETドメインでも動作する。また、無限ループするプログラムなので、signalで終了を検知した場合終了処理をするようにしてある。

* server
* client
* Makefile

実際に使って気付いたこと

実際使ってみて気付いたことを以下に記すが、いろいろな環境で試したわけではないので、もしかしたら大丈夫かもしれない。

* selectを使って、複数のファイルディスクリプタを扱えるのはいいのだけど、最大で64までしか使えない。これ以上になると落ちる。
後でわかったことだが、一つのプロセスがオープンできるファイルの数は、limits.hのOPEN_MAXで既定されていて、これが64だから。(1998.11.19)
* UNIXドメインやINETドメインでもlocalhostの場合は、送受信はどんなデータでも大丈夫だが、INETドメインで別の計算機と通信する場合は文字列でないとちゃんとデータの受け渡しができない。

プロセス間通信の方法の一つとしてsocketを使う

概要

プロセス間通信の方法の一つとしてsocketを使う。
通信を行う二つのプロセスのうち、一方をserver、もう一方をclientとする。
serverは通信を行うための口を用意する。
通信の準備

通信を行う口は以下の手順で用意する。これはserverが行う。

* socketを作る。(socket)
* socketに名前を付ける。(bind)
* 受け入れOKを示す。(listen)

これで通信を行う口は用意された。この後、clientは自分で作ったsocketをこの口に結合(connect)し、serverはそれを受け入れる(accept)。serverのsocketに名前を付けるのは、clientがその名前のsocketに結合するのに識別名が必要だからである。
connectとacceptはどちらが先に発行されてもかまわない。お互い結合されるのを待つ。

socketの名前

socketを作るとき、定義域としてAF_UNIXかAF_INETを指定する。それぞれ、UNIXドメイン、INETドメインと呼ばれる。
名前を付けるときはそのドメインの中で一意でなければならない。
UNIXドメインは、ひとつの計算機がその範囲である。この中で一意であると保証するのに簡単な方法としてファイルがある。そこで、名前を作るとその名前のファイルが作られる。こうすれば、そのシステムで一意であることが保証される。ただし、作られたファイルは残ってしまう。通信が終了しても自動的に消してはくれない。
INETドメインでは、ネットワークで結合されたすべてがその範囲である。この中で一意に名前を定めるのは容易ではない。そこで、まず計算機にネットワークで一意な名前を付け、それと組合せて名前を付ける。具体的には、計算機に付けられる名前はIPアドレスである、組み合わされるのはポート番号である。つまり、IPアドレス+ポート番号でINETdドメインで一意な名前を付ける。
通信

お互い結合してしまえば、後は通信するだけである。
送信はsend/writeで、受信はrecv/readで行う。socketを作るとファイルディスクリプタが与えられるので、低レベルのファイルアクセスと大体同じことができるので、read/writeも使える。通信と言ってもそんなに特殊なことをするわけではない。
送信は送りつけるだけだが、受信は相手から何か送られてくるのを待つので、間違えるとデッドロックしてしまう。ただし、結合そのものが切れてしまうと、recv/readで待っていた場合受信データ長0で抜けてきてくれる。
通信の終了

プロセスそのものを終わらせてしまってもいいのだが、一応終了の手順は以下の通りである。

* 結合を切る(shutdown)
* socketをクローズ(close)

UNIXドメインの場合、付けた名前のファイルが残るので、これを消しておく(unlink)。

プログラム例

以下のプログラム例は、serverがclientからのメッセージを受信して表示するものである。

* UNIXドメイン版 server
* UNIXドメイン版 client
* INETドメイン版 server
* INETドメイン版 client
* Makefile

複数のclientと通信するserver

ここまでは基本ということで1対1の通信の話をしてきたが、serverと呼ばれるものであれば複数のclientを通信する場合もある。こういう場合、selectを使うとよい。
これは、待ちたいフィルディスクリプタのリストを渡して、どれか来たら抜けて来てくれる。時間を指定すればその時間を過ぎれば何も来ていなくても抜けて来てくれる。
以下のプログラム例は、これを使って、複数のclientからの要求を処理するserverと、1秒毎にメッセージを送るclientである。UNIXドメイン版のみだが、もちろんINETドメインでも動作する。また、無限ループするプログラムなので、signalで終了を検知した場合終了処理をするようにしてある。

* server
* client
* Makefile

実際に使って気付いたこと

実際使ってみて気付いたことを以下に記すが、いろいろな環境で試したわけではないので、もしかしたら大丈夫かもしれない。

* selectを使って、複数のファイルディスクリプタを扱えるのはいいのだけど、最大で64までしか使えない。これ以上になると落ちる。
後でわかったことだが、一つのプロセスがオープンできるファイルの数は、limits.hのOPEN_MAXで既定されていて、これが64だから。(1998.11.19)
* UNIXドメインやINETドメインでもlocalhostの場合は、送受信はどんなデータでも大丈夫だが、INETドメインで別の計算機と通信する場合は文字列でないとちゃんとデータの受け渡しができない。

selectを使う

ここでは、selectを使って複数のソケットからデータを受け取る方法を説明したいと思います。
select

普通の状態では、recvやrecvfromはデータが受信できるまでブロッキングします。ソケットを一つしか利用していない場合にはブロッキングは非常に便利なのですが、ソケットが複数になると困ってしまいます。複数のソケットを扱うとき、片方のソケットでブロッキングしたままになってしまうと他のソケットにデータが到着しても受信が出来なくなってしまいます。そのため、複数のソケットを扱っていると、どのソケットからデータが受信可能か知りたくなります。

ブロッキングとは、関数が返ってこない事を表します。例えば、recvはデータを受信して関数が戻ってきます。言い方を変えると、データを受信するまでブロックしています。 recvやrecvfromをブロッキングしないノンブロッキング方式で使う事も可能ですが、ここではブロッキング方式のまま使う方法を説明します。

そのような機能を提供するのがselectです。 selectを使うと、データが受信可能なソケットでのみrecvやrecvfromを実行することができます。 selectは登録したソケットをブロッキング状態で監視し、どれかがデータ受信するとブロッキング状態を解除します。
selectを使うサンプルコード

selectは受信可能かどうかだけではなく、送信可能かどうか、エラーがあるかなどもチェックできますがここでは受信可能かどうかをチェックする方法のみをとりあげます。

下記サンプルコードでは、UDPソケットを2つ作成しています。作成した2つのUDPソケットは、selectに登録してselectはブロッキング状態に入ります。 UDPソケットにデータが到着するとrecvを行い、受信した内容を表示します。


#include
#include

int
main()
{
SOCKET sock1, sock2;
struct sockaddr_in addr1, addr2;
fd_set fds, readfds;
char buf[2048];
WSADATA wsaData;

WSAStartup(MAKEWORD(2,0), &wsaData);

// 受信ソケットを2つ作ります
sock1 = socket(AF_INET, SOCK_DGRAM, 0);
sock2 = socket(AF_INET, SOCK_DGRAM, 0);

addr1.sin_family = AF_INET;
addr2.sin_family = AF_INET;

addr1.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addr2.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

// 2つの別々のポートで待つために別のポート番号をそれぞれ設定します
addr1.sin_port = htons(11111);
addr2.sin_port = htons(22222);

// 2つの別々のポートで待つようにbindします
bind(sock1, (struct sockaddr *)&addr1, sizeof(addr1));
bind(sock2, (struct sockaddr *)&addr2, sizeof(addr2));

// fd_setの初期化します
FD_ZERO(&readfds);

// selectで待つ読み込みソケットとしてsock1を登録します
FD_SET(sock1, &readfds);
// selectで待つ読み込みソケットとしてsock2を登録します
FD_SET(sock2, &readfds);

// 無限ループです
// このサンプルでは、この無限ループを抜けません
while (1) {
// 読み込み用fd_setの初期化
// selectが毎回内容を上書きしてしまうので、毎回初期化します
memcpy(&fds, &readfds, sizeof(fd_set));

// fdsに設定されたソケットが読み込み可能になるまで待ちます
select(0, &fds, NULL, NULL, NULL);

// sock1に読み込み可能データがある場合
if (FD_ISSET(sock1, &fds)) {
// sock1からデータを受信して表示します
memset(buf, 0, sizeof(buf));
recv(sock1, buf, sizeof(buf), 0);
printf("%s\n", buf);
}

// sock2に読み込み可能データがある場合
if (FD_ISSET(sock2, &fds)) {
// sock2からデータを受信して表示します
memset(buf, 0, sizeof(buf));
recv(sock2, buf, sizeof(buf), 0);
printf("%s\n", buf);
}
}

// このサンプルでは、ここへは到達しません
closesocket(sock1);
closesocket(sock2);

WSACleanup();

return 0;
}

selectを使うサンプルコードにデータを送信するコード

selectのサンプルコードは、UDPの11111番と22222番のポートでデータを待っています。 UDPを使って11111番と22222番ポートにデータを送信するサンプルを以下に示します。


#include
#include

int
main()
{
SOCKET sock;
struct sockaddr_in dest1, dest2;
char buf[1024];
WSADATA wsaData;

WSAStartup(MAKEWORD(2,0), &wsaData);

sock = socket(AF_INET, SOCK_DGRAM, 0);

dest1.sin_family = AF_INET;
dest2.sin_family = AF_INET;

dest1.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
dest2.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

dest1.sin_port = htons(11111);
dest2.sin_port = htons(22222);

memset(buf, 0, sizeof(buf));
_snprintf(buf, sizeof(buf), "data to port 11111");
sendto(sock,
buf, strlen(buf), 0, (struct sockaddr *)&dest1, sizeof(dest1));

memset(buf, 0, sizeof(buf));
_snprintf(buf, sizeof(buf), "data to port 22222");
sendto(sock,
buf, strlen(buf), 0, (struct sockaddr *)&dest2, sizeof(dest2));

closesocket(sock);

WSACleanup();

return 0;
}

最後に

ここでは、UDPを使ったサンプルを示しましたが、TCPを使っても同様の事が出来ます。 TCPの場合は、acceptした後のソケットだけではなく、listenしているソケットに対するselectも可能です。また、winsockにはWindowsイベント方式でイベントが飛んでくるselectもあります。

SOCKETプログラミング

このページではBerkeley SOCKET、Winsock及びJava Socketのプログラミングについて紹介する。SOCKETも好きだな。

 記述例およびサンプルに含まれるファイルの全部、または一部を使用したことによる損害等について、一切の責任を負いません。また、サンプルの文字コードはS-JISで提供しますので、ご使用の際はWindowsからFTPするなどして適切な文字コードに変換してください。尚、サンプル中には説明の簡略化のため意味のないコードや、実用上問題のあるコードも含まれていますのでご注意ください。

1. [UNIX]スレッドでrecv
2. [UNIX]selectを使う
3. [Winsock]スレッドでrecv
4. [Winsock]selectを使う
5. [Winsock]イベントを使う
6. [Java]Socket(クライアント)
7. [Java]ServerSocket(サーバー)

[UNIX]スレッドでrecv
 SOCKETを使う上で考慮しなければならないのが受信の実現方法だ。データを受信する関数であるrecvがブロッキングするからだ。僕が一番好きなのはスレッドを使用する方法だ。受信専用のスレッドを新たに生成すればrecvがブロッキングしても影響はそのスレッドのみに限定されるので、例えばキー入力などは平行して処理されるので問題はなくなる。

 今までスレッドを使用したことがない方は、スレッドと聞くだけで躊躇されるかもしれないが、実際にやってみればそれほど難しいものではない。スレッドに関しては別にページを用意しているのでそちらも参照して欲しい。

<受信スレッドの生成>

/* 受信スレッド生成 */
pthread_create(&tid,NULL,waitReceiveThread,NULL);
pthread_join(tid,NULL); /* 受信スレッド終了待ち */

<受信スレッドルーチン>

void*
waitReceiveThread(void* pParam)
{
char buf[1024];
int recvSize;

while(1)
{
/* データ受信待ち */
recvSize = recv(fd,buf,1024,0);

printf("Received %d bytes\n",recvSize);

if(recvSize == 0) /* 相手CLOSE? */
{
printf("check\n");
close(fd);
break;
}
}

return NULL;
}

 受信スレッドルーチンは単にrecvのループになっている。recvはデータを受信するまで戻ってこないのでCPU時間は消費しない。SOCKETがクローズされるとrecvは戻り値=0で戻ってくるので、その場合にはループをbreakする処理を入れている。受信スレッドの生成元スレッドは pthread_joinで新たに生成したスレッドの終了を待機しているので、受信スレッドルーチンが終了すれば生成元スレッドも終了する。
[UNIX]selectを使う
 正直言って僕はselectはあまり好きではない。でも、とりあえず載せておく。まず、FD_ZEROマクロでfd_setを初期化します。次にFD_SETマクロでselectで待ちたいSOCKETの fdを設定する。このfd_setを使ってselectを呼び出すと、データを受信するまでselectはブロックするがCPU時間は消費しない。 selectから戻ったところでFD_ISSETマクロを用いて該当fdの読み込みデータがあるかどうかを調べ、データがある場合はrecvを呼び出す。

int
waitReceive(void)
{
fd_set fds;
char buf[1024];
int recvSize;
/* バッチ共通初期処理 */
while(1) /* メッセージループ */
{
FD_ZERO(&fds); /* fd_set初期化 */
FD_SET(fd,&fds); /* fd設定 */
select(fd + 1,&fds,NULL,NULL,NULL); /* データ受信待ち */

if(FD_ISSET(fd,&fds)) /* 該当fd読み込みデータあり? */
{
/* データ受信 */
recvSize = recv(fd,buf,1024,0);

printf("Received %d bytes\n",recvSize);

if(recvSize == 0) /* 相手CLOSE? */
{
printf("check\n");
close(fd);
break;
}
}
}

return 0;
}

[Winsock]スレッドでrecv
 Win32でもスレッドが使用できるので、新たにスレッドを生成して通常の処理は平行して行いながらrecvで待つことができる。Win32のスレッドモデルはpthreadに比べるとやや煩雑になっており、加えてWinsockでは様々な処理方式を選択可能なのだが、スレッドを使用した方式が最もシンプルであろう。この例ではCのライブラリを使用するために、_beginthreadexを使用しているが、CreateThreadでも同じだ。スレッドに関しては別にページを用意しているのでそちらも参照して欲しい。

<受信スレッドの生成>

/* 受信スレッド生成 */
hThread = (HANDLE)_beginthreadex(NULL,0,waitReceiveThread,NULL,0,&threadId);

WaitForSingleObject(hThread,INFINITE); /* 受信スレッド終了待ち */
CloseHandle(hThread); /* ハンドルクローズ */

<受信スレッドルーチン>

DWORD WINAPI
waitReceiveThread(LPVOID pParam)
{
char buf[1024];
int recvSize;

while(1)
{
/* データ受信待ち */
recvSize = recv(fd,buf,1024,0);

printf("Received %d bytes\n",recvSize);

if(recvSize == 0) /* 相手CLOSE? */
{
printf("check\n");
closesocket(fd); /* CLOSE */
break;
}
}

return 0;
}

 この例でも受信スレッドルーチンの生成元スレッドは、pthreadのときと同様に新たに生成したスレッドの終了を待機している。Win32ではスレッドのハンドルがシグナル状態になるのをWaitForSingleObjectで待機する。受信スレッドルーチンが終了すれば生成元スレッドも終了する。

スレッドrecvのサンプル(ソースはLinuxでもコンパイルできるようになっている。UNIXとWindowsの違いを見てみて欲しい。)
[Winsock]selectを使う
 selectを使う場合はUNIXのSOCKETとまったく同じだ。よりWindowsっぽく書くなら、WSAAsyncSelectを使うという手があるが、こちらはメッセージを使用する。メッセージを使用するにはウィンドウが必要になる僕はメッセージの仕組みをあまり詳しくないので専らイベントを使用する。
[Winsock]イベントを使う
 Winsock2.0ではWin32のイベントオブジェクトを使用することができる。メッセージを利用しないのでウィンドウが必要なく、GUIがないプログラムで使いやすい。Anpsockもこの方式を使っている。ところが、イベントを使ったWinsockの解説書はあまりない。そもそも、Winsock2.0の解説書が少ない。

 イベントに関しては別にページを用意しているのでそちらも参照して欲しい。

int
waitReceive(void)
{
char buf[1024];
int recvSize;
int nRet;
HANDLE hEvent = WSACreateEvent();
WSANETWORKEVENTS events;

WSAEventSelect(fd,hEvent,FD_READ|FD_CLOSE);

while(1)
{
/* イベント待ち */
nRet = WSAWaitForMultipleEvents(1,&hEvent,FALSE,WSA_INFINITE,FALSE);

if(nRet == WSA_WAIT_FAILED)
{
printf("!!!!!!!!!!!!!!!!!!!!1\n");
break;
}

/* イベントの調査 */
if(WSAEnumNetworkEvents(fd,hEvent,&events) == SOCKET_ERROR)
{
printf("???\n");
}
else
{
/* CLOSE */
if(events.lNetworkEvents & FD_CLOSE)
{
printf("close\n");
closesocket(fd);
break;
}
/* READ */
if(events.lNetworkEvents & FD_READ)
{
recvSize = recv(fd,buf,1024,0);
printf("Received %d bytes\n",recvSize);
}
}
}

WSACloseEvent(hEvent); /* イベントクローズ */

return 0;
}

イベントrecvのサンプル
[Java]Socket(クライアント)
 Javaでクライアントのソケットを作成するにはホストとポートを指定してSocketをnewするだけである。たったのこれだけでCで言うところのsocketからconnectの呼び出しまで済んでしまうから驚きである。

// クライアントソケット作成
Socket conn = new Socket("localhost",port);
System.out.println("Connecting to port " + port);
// データ受信スレッド生成
RecvData rd = new RecvData(conn.getInputStream());
rd.start();

 データの受信は他の言語と同様スレッドを用いている。実際にサンプルを見ていただければお分かりになると思うが、わずか5、60行でクライアントのプログラムが書けるのである。素晴らしい!

class RecvData extends Thread {
InputStream st;

public RecvData(InputStream st) {
this.st = st;
}

public void run() {
int data;

try {
// データ読み込み
while((data = st.read()) != -1) {
System.out.print((char)data);
}
} catch(IOException e) {
System.out.println(e);
}
}
}

[Java]ServerSocket(サーバー)
 Javaでサーバーソケットを作成する場合はポートを指定してServerSocketをnewする。

// サーバーソケット作成
ServerSocket server = new ServerSocket(port);
System.out.println("Listening on port " + port);

 作成したサーバーソケットがAcceptするとクライアントの項で既出のSocketを返すので、以降同様に処理してやれば良い。サンプルでは受信スレッドのクラスはクライアントと共有している。

// Accept
Socket conn = server.accept();
System.out.println("Accept");
// データ受信スレッド生成
RecvData rd = new RecvData(conn.getInputStream());
rd.start();

ソケットのサンプル(サーバー、クライアント)