7

(TCP) ソケットを介したデータ転送で小さな問題が発生しました。私がやっていることの小さな背景:

A 側から B 側にデータを送信しています。最大サイズが 1096 バイトであると仮定すると、送信されるデータは可変長にすることができます。

A) send(clientFd, buffer, size, NULL)

B では、予想されるサイズがわからないため、常に 1096 バイトを受信しようとします。

B) int receivedBytes = receive(fd, msgBuff, 1096, NULL)

ただし、これを行ったとき: A がデータの小さなチャンクを送信していることに気付きました..たとえば、約 80 ~ 90 バイトです。数回の送信バーストの後、B はそれらを組み合わせて receiveBytes を 1096 にしました。これにより明らかにデータが破損し、地獄が解き放たれました。

これを修正するために、データをヘッダーとデータの 2 つの部分に分けました。

struct IpcMsg
{
   long msgType;
   int devId;
   uint32_t senderId;
   uint16_t size; 
   uint8_t value[IPC_VALUES_SIZE]; 
};

A面:

A) send(clientFd, buffer, size, NULL)

B では、最初にヘッダーを受信し、受信するペイロードのサイズを決定します。次に、残りのペイロードを受信します。

B) int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof( ((IpcMsg*)0)->value ), 0);
int sizeToPoll = ((IpcMsg*)buffer)->size;
printf("Size to poll: %d\n", sizeToPoll);

if (sizeToPoll != 0)
{
        bytesRead = recv(clientFd, buffer + receivedBytes, sizeToPoll, 0); 
}

そのため、ペイロードを持つ送信ごとに、受信を 2 回呼び出すことになります。これは私にとってはうまくいきましたが、これを行うためのより良い方法があるかどうか疑問に思っていましたか?

4

2 に答える 2

4

次のデータに関する基本情報を含むヘッダーを送信し、その後にデータ自体を送信するという考えは正しいです。ただし、これは常に機能するとは限りません。

int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof( ((IpcMsg*)0)->value ), 0);
int sizeToPoll = ((IpcMsg*)buffer)->size;

その理由は、TCP が、いわゆる輻輳制御戦略に適用される基本的なネットワーク条件の独自の評価に基づいて、ヘッダーを自由にフラグメント化して送信できるためです。LAN では、ほぼ常に 1 つのパケットでヘッダーを取得しますが、インターネットを介して世界中で試してみると、一度に取得できるバイト数がはるかに少なくなる場合があります。

答えは、TCP の 'receive' (通常はrecv) を直接呼び出すのではなく、実際に受信する必要があるサイズとそれを入れるバッファーを取る小さなユーティリティ関数に抽象化することです。すべてのデータが到着するかエラーが発生するまで、パケットを受信して​​追加するループに入ります。

非同期にして複数のクライアントに同時にサービスを提供する必要がある場合は、同じプリンシパルが適用されますが、データが到着したときに通知を受け取ることができる「select」呼び出しを調査する必要があります。

于 2014-09-12T08:32:24.073 に答える
2

TCP/IP は、データを送信するための「生の」インターフェースです。バイトが送信された場合、それらがすべて正しい順序であることが保証されますが、チャンクについては保証されず、送信しているデータについては何もわかりません。

したがって、そのように処理される TCP/IP を介して「パケット」を送信する場合は、次の手法のいずれかによって完全なパケットがあることを知る必要があります。

  • 固定サイズのパケット。あなたの場合、1096バイト
  • 最初に、送信されるパケットのサイズを示す既知の「ヘッダー」を送受信します。
  • ある種の「パケットの終わり」記号を使用します。

最初の 2 つのいずれかで、受信すると予想されるバイト数がわかっているので、メッセージ全体が得られるまで受信したものをすべてバッファリングしてから、それを処理する必要があります。

予想以上に受信した場合、つまり次のパケットにあふれた場合は、それを分割し、完了したパケットを処理して、残りを後で処理するためにバッファに残します。

パケット シンボルの終わりがある後者の場合、それはメッセージのどこにでもある可能性があるため、それに続くものはすべて、次のパケットのためにバッファリングします。

于 2014-09-12T10:09:55.843 に答える