2016-04-09 11 views
-1

私は2台のUbuntu 14.04 PCを持っています。 1つはサーバーとして使用され、もう1つはクライアントとして使用されます。クライアントは、いくつかのパケットを戻すサーバーへのTCP接続をセットアップします。サーバー上のコードは次のとおりです。Ubuntuソケットプログラミング:パケットはTXとRXの間で再パッケージされます

send(sd、pkt、pkt_len、MSG_NOSIGNAL);

クライアント側のコードも非常に簡単である:

リード(SD、BUF、buf_sizeの)。

サーバー上の送信が間隔を空けている場合は、問題はありません。しかし、サーバーが急速な送信を行っている場合、事は醜いように見えます。ここでは、サーバーが8つのパケットを連続して送信しているときの例を示します。

サーバコードは、これらの8つのパケットのサイズを示してはいる:752(バイト:サーバ上の752(バイト)、713、713、713、396、398、396、396

tcpdumpが4つのTXパケットをキャプチャ752(バイト)、2796年、929

クライアントコードが、それは、それぞれ3548バイト、929バイト、とのみ2パケットを受信する例を示します。)、1398年には、1398は、クライアント上で929

tcpdumpが3つのRXパケットを捕捉します。

サーバーから送信されたすべてのバイトがクライアントによって受信されたことがわかります。しかし、パケットは、伝送経路の様々なポイントで結合される。私はこれがTSO、GSO、GROなどに起因すると考えています。しかし、パケットが受信アプリケーションに配信されるときに、これらの最適化が正しい形式にパケットを再アセンブルしてはいけませんか?

この問題を回避するにはどうすればよいですか?

+0

注:このコードは正常に関係なく、それぞれのrecv()の呼び出しによって返されるバイト数、受信データを処理しないために持っているので、それは少し複雑送信コードよりもですあなたが各recv()呼び出しから出てくるバイトシーケンスのサイズは、ネットワークパケットのサイズとは無関係であることに注意してください。 –

+0

答えは、TCPがストリーミングプロトコルであるため、TCPではなくUDPを使用することです。あなたはパケットのようなものがないので、どのようにパケットが受信されるのかは、サーバまたはクライアント上で決して頼りすぎてはいけません。これは、カーネルの動作に影響を与える遅延のために経験した方法で受信されるようなバイトの連続ストリームです。 –

答えて

2

TCPは、あなたが見ているものを許可するだけでなく実装するように慎重に設計されています。これはバイトストリームプロトコルです。メッセージが必要な場合は、スーパーインポーズされたアプリケーションプロトコルを使用してメッセージを実装する必要があります。

+0

これは正解です。しかし、Nagleアルゴリズムを無効にしたりSO_LINGERを有効にするなど、カーネルがTCPパケットを扱う方法を変更する方法があります。しかし、他のルータが同じパケットをどのように扱うかは変わりません。 – slushie

+0

@slushie SO_LINGERはそれとは何の関係もなく、Nagleアルゴリズムは問題の一部にすぎません。 – EJP

+0

これはあなたの答えが正しい理由です。なぜなら、Nagleはカーネル内だけに適用されるからです。あるいは、UDPを使用して信頼性を犠牲にすることもできます。 – slushie

0

この問題を回避するにはどうすればよいですか?

したがって、TCP(バイトストリーム指向の転送メカニズム)を使用していますが、メッセージ指向の動作が必要です。 TCPが動作する方法を変更することはできません(バイトがすべて受信され、同じ順序で受信される限り、どのようなサイズのグループでもバイトを転送することができます)。しかし、TCPの上にレイヤーを追加して、パケット指向の動作をシミュレートすることができます。

たとえば、1000バイトの「パケット」の送信をシミュレートするとします。

size_t myPacketSize = 1000; // or whatever the size of your packet is 
uint32_t bePacketSize = htonl(myPacketSize); // convert native-endian to big-endian for cross-platform compatibility 
if (send(sd, &bePacketSize, sizeof(bePacketSize), 0) != sizeof(bePacketSize)) 
{ 
    perror("send(header)"); 
} 

....そして、その直後:あなたの送信プログラムは、最初(4バイト、のは言わせて)「パケット」は含まれていますバイトどのように多くの受信機を言うだろうヘッダーを固定サイズを送ることができあなたは、パケットのペイロードデータを送信したい:

if (send(sd, packetDataPtr, myPacketSize, 0) != myPacketSize) 
{ 
    perror("send(body)"); 
} 

受信機は、ヘッダ/サイズ値を受け取り、その大きさの配列を割り当て、そこにペイロードデータを受信する必要があります。それは、パケットが様々に組み合わされていることではないということ

void HandleReceivedPseudoPacket(const char * packetBytes, uint32_t packetSizeBytes) 
{ 
    // Your received-packet-handling code goes here 
} 

// Parses an incoming TCP stream of header+body data back into pseudo-packets for handling 
void ReadPseudoPacketsFromTCPStreamForever(int sd) 
{ 
    uint32_t headerBuf;    // we'll read each 4-byte header's bytes into here 
    uint32_t numValidHeaderBytes = 0; // how many bytes in (headerBuf) are currently valid 

    char * bodyBuf = NULL;   // will be allocated as soon as we know how many bytes to allocate 
    uint32_t bodySize;    // How many bytes (bodyBuf) points to 
    uint32_t numValidBodyBytes = 0; // how many bytes in (bodyBuf) are currently valid 
    while(1) 
    { 
     if (bodyBuf == NULL) 
     { 
     // We don't know the bodySize yet, so read in header bytes to find out 
     int32_t numBytesRead = recv(sd, ((char *)&headerBuf)+numValidHeaderBytes, sizeof(headerBuf)-numValidHeaderBytes, 0); 
     if (numBytesRead > 0) 
     { 
      numValidHeaderBytes += numBytesRead; 
      if (numValidHeaderBytes == sizeof(headerBuf)) 
      { 
       // We've read the entire 4-byte header, so now we can allocate the body buffer 
       numValidBodyBytes = 0; 
       bodySize = ntohl(headerBuf); // convert from big-endian to the CPU's native-endian 
       bodyBuf = (char *) malloc(bodySize); 
       if (bodyBuf == NULL) 
       { 
        perror("malloc"); 
        break; 
       } 
      } 
     } 
     else if (numBytesRead < 0) 
     { 
      perror("recv(header)"); 
      break; 
     } 
     else 
     { 
      printf("TCP connection was closed while reading header bytes!\n"); 
      break; 
     } 
     } 
     else 
     { 
     // If we got here, then we know the bodySize and now we need to read in the body bytes 
     int32_t numBytesRead = recv(sd, &bodyBuf[numValidBodyBytes], bodySize-numValidBodyBytes, 0); 
     if (numBytesRead > 0) 
     { 
      numValidBodyBytes += numBytesRead; 
      if (numValidBodyBytes == bodySize) 
      { 
       // At this point the pseudo-packet is fully received and ready to be handled 
       HandleReceivedPseudoPacket(bodyBuf, bodySize); 

       // Reset our state variables so we'll be ready to receive the next header 
       free(bodyBuf); 
       bodyBuf = NULL; 
       numValidHeaderBytes = 0; 
      } 
     } 
     else if (numBytesRead < 0) 
     { 
      perror("recv(body)"); 
      break; 
     } 
     else 
     { 
      printf("TCP connection was closed while reading body bytes!\n"); 
      break; 
     } 
     } 
    } 

    // Avoid memory leak if we exited the while loop in the middle of reading a psuedo-packet's body 
    if (bodyBuf) free(bodyBuf); 
} 
関連する問題