パケットは勝手に分割され統合される

  • 投稿日:
  • by
  • カテゴリ:



幸運にして私はパケット分割統合についての演説を複数回聞いたことがあります。




送り側がsend()で10バイト送ると、受け側はrecv()で10バイト受信する。



という幻影が存在します。


あるいは、



送り側がsend()でnバイト送ると、受け側はrecv()でnバイト受信する。



という前提で組まれた実装は、たくさんあります。つまり"heeeloo\n"をsend()したら充分大きなサイズのバッファを指定したrecv()では"heeeloo\n"が受信できると期待した実装です。


これがきちんと動いているように見えるのはプロトコルに、"後付で"リトライ機構が備わっている場合も多いです。ですが、既存のプロトコルの実装ではそうもいきません。



パケットは経路上で分割・統合されます。


send,recvはそのパケットとはあまり関係ありません。プロトコルスタック上のバッファの都合が主でしょう。


さて、終端記号を含むコマンドを送受信しあうプロトコルを考えるときに、recv()したデータが



データ・データ・データ・終端・データ・データ



となっていたら、破綻する実装が多く実在するということです。


# 次のrecv()で頭が欠ける



シリアル通信を例に出すまでも無く、もっとも単純な正しい実装は、



while(1){


// 1文字受信してバッファに詰める


// 終端記号を受信したら抜ける


}


// バッファを処理する



です。1文字ずつrecv()するなんて、効率が悪いという意見はごもっともですが、それは実装の腕の見せ所であり自由です。ここでは原理を示しているのですから。


例えば手元の古いHTTPの実装の教科書には以下のようなサンプルが示されています。



♯include<sys/types.h>
♯include<sys/socket.h>
//telnet eolシーケンスで終了するコマンドラインを受信する
int RecvLine(int iSocket, char szBuf, int iLen)
{
int iBytesRead, iIdx, bNotDone;
iBytesRead=recv(iSocket, &szBuf[0], 1, 0);
iIdx = 1;
bNotDone=TRUE;
while (bNotDone == TRUE){
iBytesRead = recv(iSocket, &szBuf[iIdx], 1, 0);
if (iBytesRead < 0){
return (-1); // エラー受信
}
iIdx++;
if((szBuf[iIdx - 2] == 'r') &&
(szBuf[iIdx - 1] == 'n') ){
bNotDone = FALSE; //telnet eolを取得
}
if (iIdx == iLen){
return(-1); // エラー。バッファが小さすぎる
}
}
szBuf[iIdx - 2] = NULL; //NULL終端を追加
return (TRUE);
}

すぐあとに、以下のような説明があります。



この方法の欠点は、一度に1文字しか受信しないので操作効率が悪いことです



この実装は、シリアル通信の受信部分ではよくみかける形でもあります。シリアル通信では終端記号だけでなく、頭にも記号をつけるのが普通ですが。


# 頭が欠けてもなんとかなる、Web時代のプロトコルとは慎重さが違うので


この基本を知っている人は、受信部分をちらっと見ただけで、ため息をつきます。


この問題の厄介なところは、さきほどの、



送り側がsend()でnバイト送ると、受け側はrecv()でnバイト受信する。



という幻影が成り立たない場合の現象を起こすのが結構大変なことです。


なので、



そんな超レアケースに備えるのはバカのやること



と主張する人たちを説得するのがまた大変なのです。


なので、基本を知っているだけでなく、苦労まで経験した人は、そんな実装をみかけると演説したくなる気持ちも分からないではありません。


ですが、私がいましたように原典ぽいものを示さないと説得も困難なのです。


私のhttpの教科書はこれです。これでどこかの誰かが書いたいい加減なWebサーバを直したこともあります。


もちろん、ソケットプログラミングの純然たる教科書である以下の本でも同じことが書いてあるわけですが、分厚すぎて答えを得るまでに仕事が終わってしまうかもしれません。


答えを探している人はこの本で見つけられることも多いでしょうが、もう古すぎる気もします。


とにかく世間ですでに答えが出ていることを毎度毎度解決して回らないといけないのも困ったものです。


しかも「そんな面倒な実装、ほんとうに必要なのか?」と詐欺師呼ばわれしながら。