echoクライアント完成
いくつかのしょうもないミスを乗り越え、ようやっとechoクライアント&サーバ完成。最後まで悩んでいたのがポインタの不理解によるものだったのが何とも恥ずかしい。
…今、emacsの癖が初めて出た。一文消そうと思って何気なくC-kしたら、検索窓にカーソルが移動した*1…!emacs中毒の初期症状だ!
それはさておき、echoサーバとクライアントがやっと作れました。同時に組み立てていたのでバグ探しが大変でねえ(言い訳)。仕様を何度か変えたのは公然の秘密です。
簡単に解説します。今日はクライアント。
クライアントのプログラムの構成は単純。
・socket()を使ってsocket descriptorを返してもらう
・connect()を使ってサーバとハンドシェイクしてもらう
・send()を使って送りたい文字列を一括送信する
・recv()をくり返し使って文字列を受信する
・close()を使って後処理をする
send()は送信を下層に任せられますが、recv()は一度に届く量が決まっていないので、何度か繰り返して受信する必要があります。
実際に使ってみた感じでは、基本的には4byteずつで通信していました。末尾では短くなるのはもちろんですが、受信メッセージ自体が短いときには4byteよりも大きい量での通信もありました。localhost同士の通信でも、近道をしようとしないところはちょっと驚きです。(何かしら上の層を使ってメッセージの一括送受信できそうなものだと思う。しかしその場合はUDPで済むのか)
受信部のソース。
int readecho(const int sock, char *buf, const int buflen, int sendlen){ int iter, readlen; int remains = 0; for( iter = 0; iter < buflen; ){ remains = buflen - iter; readlen = recv(sock, buf+iter, remains, 0); if(readlen < 0){ printf("recv() failed.\n"); return -1; }else if(readlen == 0){ printf("connection finished.\n"); return 0; } iter += readlen; buf[iter]='\0'; printf("buffer : %s\n", buf); if(iter == sendlen){ break; } } return iter; }
バッファをひとつ用意(呼び出し元から渡されている)して、そこにrecvによって受信したメッセージを積み重ねています。前に受信したメッセージの後ろに新しく受信したメッセージを書き込むので、recvの第二引数(受信用バッファの先頭アドレス)には「バッファの先頭からこれまで書き込んだ長さ分だけ先のアドレス」を渡しています。
繰り返しの最後の方で文字列の最後に'\0'を加えているのは、たんにprintf()の都合です。