ソケットプログラミング.3

今回は,メッセージのやり取りができるようにしてみます.


d:id:pneumaster:20090523:1243088465で,
accept()の返り値について勘違いしていたみたいです.


man -S 3p socketによると,accept()の返り値は

RETURN VALUE
       Upon successful completion, accept() shall return the non-negative file
       descriptor  of the accepted socket. Otherwise, -1 shall be returned and
       errno set to indicate the error.

となっていました.
accepted socketのファイルディスクリプタだそうです.
関数の成功,不成功だけだと思っていました.


ところで,accept()の書式は

       int accept(int socket, struct sockaddr *restrict address,
              socklen_t *restrict address_len);

だそうですが,第1引数のsocketは

       socket Specifies  a  socket  that  was  created with socket(), has been
              bound to an address with bind(), and  has  issued  a  successful
              call to listen().

と説明されています.
つまり,accept()の前の関数(listen())を通るのを成功したファイルディスクリプタ
# 前回のソースのsockのことです
と,accept()の返り値のファイルディスクリプタ(以降「s」)は異なるということですか.


う〜む...別にする理由がよく分からないです.
しかも,メッセージ用のファイルディスクリプタを用意するのはサーバ側だけだそうで.
参考にしている本(asin:4274065847)では

と書かれていました.


送るメッセージはこんな感じに流れていくってことなのでしょうか?

 (メッセージバッファ)===> s ===> sock ===> (ネット) ===> (クライアントPC)

この問題は置いて,メッセージのやり取りについて進めます.


サーバとクライアントが接続完了したら(accept後),メッセージのやり取りを始めます.
ここではとりあえず,CTRL-CでSIGINTを送るまでメッセージのやり取りを行います.


サーバ側

#define RECV_BUFSIZE    8192


	char recv_buf[RECV_BUFSIZE];


	while (1) {
		int i;

		recv_buf[0] = '\0';
		for (i=0; i<RECV_BUFSIZE-1; ++i) {
			if (recv(s, &recv_buf[i], 1, 0) <= 0) {
				errsv = errno;
				strerror(errsv);
				// NOTE: need to make function or to use `goto' ?
				// break;
				return EXIT_FAILURE;
			}
			if (recv_buf[i] == '\n') {
				break;
			}
		}
		recv_buf[i] = '\0';
		printf("%s\n", recv_buf);
		fflush(stdout);
	}

サーバ側ではrecv()を使って,
用意したメッセージバッファ(recv_buf)に
クライアントから受信するデータを格納することにします.


recv()があるifの条件は0と0以下(-1を想定)で,エラーとしてキャッチしておきます.
# 0はメッセージが受信できなかったときか,
# クライアントが正常に接続を切断した場合になるのだと思います.
# それ以外は,-1だそうです*1


recv()の第3引数で1文字ずつずつ受信していますが,
RECV_BUFSIZEとしたら,受信できませんでした.
なぜだろう.


クライアント側

#define MESSAGE_BUFSIZE 8192


	char send_buf[MESSAGE_BUFSIZE];  /* message buffer */


	while (1) {
		printf("%s> ", ipaddr);
		fgets(send_buf, MESSAGE_BUFSIZE, stdin);

		if (send(sock, (char *)send_buf, strlen(send_buf), 0) == -1) {
			errsv = errno;
			strerror(errsv);
			break;
		}
	}

fgets()を使って標準入力から,高々MESSAGE_BUFSIZE-1バイトの文字入力させ,
このバッファをsend()します.


ちょっとしたチャットプログラムが完成しました.
今日のソースプログラムはこちら
http://github.com/pneu/chat/tree/fbc11118bb53108179126905ab51015a7a2e3e35


次は複数のクライアントからメッセージを受けられるようにしてみたいと思います.

参考

基礎からわかるTCP/IP ネットワーク実験プログラミング(asin:4274065847)

*1:man -S 3p recv