1

I have a trouble, my server application sends packet 8 bytes length - AABBCC1122334455 but my application receives this packet in two parts AABBCC1122 and 334455, via "recv" function, how can i fix that?

Thanks!

4

4 に答える 4

7

少し要約すると:

  1. TCP 接続は、アプリケーション レベルのパケットまたはメッセージでは動作しません。バイト ストリームを扱っています。この観点からは、ファイルの書き込みと読み取りに似ています。
  2. sendとはどちらもrecv、引数で指定されたよりも少ないデータを送受信できます。正しく処理する必要があります (通常は、呼び出しの周りに適切なループを適用することによって)。
  3. ストリームを扱うときは、ストリームをアプリケーションで意味のあるデータに変換する方法を見つける必要があります。つまり、シリアル化プロトコルを設計する必要があります。

すでに述べたことから、おそらく何らかのメッセージを送信したいと思うでしょう (まあ、それは通常人々が行うことです)。重要なことは、メッセージの境界を適切に発見することです。メッセージが固定サイズの場合は、ストリームから同じ量のデータを取得してメッセージに変換するだけです。それ以外の場合は、別のアプローチが必要です。

  • メッセージに存在しない文字を思いつくことができれば、それが区切り文字になる可能性があります。その後、キャラクターに到達するまでストリームを読むことができ、それがあなたのメッセージになります. ASCII 文字 (文字列) を転送する場合は、区切り文字としてゼロを使用できます。

  • バイナリ データ (生の整数など) を転送する場合、すべての文字がメッセージに表示される可能性があるため、区切り文字として機能するものは何もありません。おそらく、この場合の最も一般的なアプローチは、メッセージのサイズを含む固定サイズのプレフィックスを使用することです。この追加フィールドのサイズは、メッセージの最大サイズによって異なります (おそらく 4 バイトで安全ですが、最大サイズがわかっている場合は、より低い値を使用できます)。次に、パケットは(バイトSSSS|PPPPPPPPP...のストリーム) のようにSなりPます。すべてのパケットが 4 つの特殊なバイト (PSSバイト)、32 ビット整数として読み取ることができます。カプセル化されたメッセージのサイズがわかったら、すべてのPバイトを読み取ります。1 つのパケットの処理が完了したら、ソケットから別のパケットを読み取る準備が整います。

良いニュースですが、まったく異なるものを思い付くことができます。知っておく必要があるのは、バイト ストリームからメッセージをデシリアライズする方法と、send/がどのようにrecv動作するかだけです。幸運を!

編集:

任意のバイト数を配列に受け取る関数の例:

bool recv_full(int sock, char *buffer, size_t size)
{
  size_t received = 0;
  while (received < size)
  {
    ssize_t r = recv(sock, buffer + received, size - received, 0);
    if (r <= 0) break;
    received += r;
  }

  return received == size;
}

ペイロードのサイズを定義する 2 バイトのプレフィックスを持つパケットを受信する例 (ペイロードのサイズは 65kB に制限されます):

uint16_t msgSize = 0;
char msg[0xffff];

if (recv_full(sock, reinterpret_cast<char *>(&msgSize), sizeof(msgSize)) &&
    recv_full(sock, msg, msgSize))
{
  // Got the message in msg array
}
else
{
  // Something bad happened to the connection
}
于 2012-05-25T17:55:39.073 に答える
1

これrecv()がほとんどのプラットフォームで機能する方法です。受信したバイト数を確認し、必要な数が得られるまでループで呼び出し続ける必要があります。

于 2012-05-25T17:06:17.340 に答える
1

アプリケーションにとって意味のある十分なバイト数が得られるまで、ループ内のTCP ソケットから読み取ることにより、それを「修正」します。

于 2012-05-25T17:09:28.703 に答える
0

私のサーバーアプリケーションは8バイト長のパケットを送信します

あまり。サーバーは、8 バイト長のパケットではなく、8 バイトずつ送信します。TCP データは、パケット ストリームではなく、バイト ストリームで送信されます。TCP は、念頭に置いている可能性のある「パケット」境界を尊重も維持もしません。

データが N バイト単位で提供されることがわかっている場合はrecv、ループで呼び出します。

std::vector<char> read_packet(int N) {
  std::vector buffer(N); 
  int total = 0, count;
  while ( total < N && (count = recv(sock_fd, &buffer[N], N-total, 0)) > 0 )
    total  += count;
  return buffer;
}

    std::vector<char> packet = read_packet(8);

パケットが可変長の場合は、データ自体の前に送信してみてください。

int read_int() {
  std::vector<char> buffer = read_packet(sizeof (int));
  int result;
  memcpy((void*)&result, (void*)&buffer[0], sizeof(int));
  return result;
}

  int length = read_int();
  std::vector<char> data = read_buffer(length); 
于 2012-05-25T17:10:05.360 に答える