1

TCP ソケットを介してデータをブロックごとに送信しようとしています。サーバー コードは次のことを行います。

#define CHECK(n) if((r=n) <= 0) { perror("Socket error\n"); exit(-1); }
int r;

//send the number of blocks
CHECK(write(sockfd, &(storage->length), 8)); //p->length is uint64_t

for(p=storage->first; p!=NULL; p=p->next) {
  //send the size of this block
  CHECK(write(sockfd, &(p->blocksize), 8)); //p->blocksize is uint64_t

  //send data
  CHECK(write(sockfd, &(p->data), p->blocksize));
}

クライアント側では、サイズを読み取ってからデータを読み取ります (同じ CHECK マクロ):

CHECK(read(sockfd, &block_count, 8));
for(i=0; i<block_count; i++) {
  uint64_t block_size;
  CHECK(read(sockfd, &block_size, 8));

  uint64_t read_in=0;
  while(read_in < block_size) {
    r = read(sockfd, data+read_in, block_size-read_in); //assume data was previously allocated as char*
    read_in += r;
  }
}

クライアントとサーバーの両方が同じマシンで実行されている限り、これは完全に正常に機能しますが、ネットワーク経由でこれを試みるとすぐに、ある時点で失敗します。特に、最初の 300 ~ 400 ブロック (約 587 バイト) 程度は正常に動作しますが、block_size の読み取り値が正しくありません。

received block #372 size : 586
read_in: 586 of 586
received block #373 size : 2526107515908

そして、明らかにクラッシュします。TCP プロトコルは、データが失われず、すべてが正しい順序で受信されることを保証するという印象を受けましたが、ローカルで既に動作していることを考えると、これはどのように可能であり、ここでの私の間違いは何ですか?

4

4 に答える 4

4

読み取り時に、8 バイトすべてを一度に読み取るという保証はありませんblock_countblock_size

于 2012-11-06T08:45:02.220 に答える
1

同じマシンで動作する理由は、block_size と block_count がバイナリ値として送信され、クライアントが受信して解釈すると、同じ値になるためです。

ただし、通信する 2 つのマシンで整数を表すバイト順が異なる場合 (x86 と SPARC など)、または sizeof(int) が異なる場合 (64 ビットと 32 ビットなど)、コードは正しく機能しません。

両方のマシンの sizeof(int) とバイト順が同じであることを確認する必要があります。サーバー側では、sizeof(int) と storage->length および p->blocksize の値を出力します。クライアント側で sizeof(int) と block_count と block_size の値を出力します。

うまくいかないときは、同じではないことに気付くと思います。これが当てはまる場合、データにバイナリ データが含まれていると、データの内容も誤って解釈されます。

于 2012-11-10T07:13:12.377 に答える
1

TCP プロトコルにより、データが失われることはなく、すべてが正しい順序で受信されることが保証されるという印象を受けました。

はい。ただし、TCP が保証するのはそれだけです。データが単一のパケットで送受信されることを保証するものではありません。データをコピーする前に、必要なブロックサイズになるまで、データを収集してバッファにまとめる必要があります。

于 2012-11-06T08:45:22.393 に答える
1

おそらく、8 バイト全体を読み取らずに読み取り呼び出しが返される可能性があります。彼らが報告した長さをチェックします。

コードがこのように動作する理由をよりよく理解するために、valgrind または strace が参考になる場合もあります。短い読み取りを取得している場合、strace はシステムコールが何を返したかを通知し、valgrind は長さ変数で初期化されていないバイトを読み取っていることを通知します。

于 2012-11-06T08:47:22.953 に答える