3

TCP / IPを介してメッセージを送信しています。メッセージの長さの前にchar配列を付けてから、送信する必要があります。どうすればいいのですか?

また、もう一方の端でそれを抽出する方法の例を提供していただけますか。そして、可能であれば、説明してください。

私はC++とWinsockを使用しています。

編集:

string writeBuffer = "Hello";

unsigned __int32 length = htonl(writeBuffer.length());

正しい長さではなく、非常に大きな数を返します。

受信部分については、ntohl()を使用すると、正しい長さではなく大きな数値も取得されますか?どうしてこんなことに?私はこのように受け取っています

bool Server::Receive(unsigned int socketIndex)
{
    // Read data from the socket
    if (receivingLength)
    {
        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            ((char*)&messageLength) + bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == MESSAGE_LENGTH_SIZE)
        {
            // If uncomment the following line,
            // I won't get the correct length, but a large number
            //messageLength = ntohl(messageLength);
            receivingLength = false;
            bytesReceived = 0;
            bytesLeft = messageLength;
        }
    }
    else
    {
        if (bytesLeft > BUFFER_SIZE)
        {
            return false;
        }

        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            &receiveBuffer[bytesReceived], bytesLeft, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == messageLength)
        {
            // we have received full message
            messageReceived = true;

            receiveBuffer[bytesReceived] = '\0';

            // wait for next message
            receivingLength = true;
        }

        bytesLeft -= bytesReceived;
    }

    return true;
}
4

3 に答える 3

7

TCPストリームで長さフィールドを送信するときは、次の2つのことを決定する必要があります。

  1. 長さはどのくらいにする必要がありますか(1バイト、2バイト、4バイト、可変長)
  2. どのエンディアンを使用する必要がありますか

4バイトの長さとネットワークバイトオーダー(つまりビッグエンディアン)を使用することをお勧めします。ネットワークバイトオーダーの場合、マクロhtonlおよびntohlは、ホスト(ネイティブ)バイトオーダー(この場合はリトルエンディアン)とネットワークバイトオーダーの間で変換されます。

データを送信するには、フラグメントは次のようになります。

size_t length = strlen(data);
uint32_t nlength = htonl(length);
send(sock, &nlength, 4, 0);
send(sock, data, length, 0);

受信側では、最初に長さを抽出し、次にデータを抽出します。

uint32_t length, nlength;
recv(sock, &nlength, 4, 0);
length = ntohl(nlength);
data = malloc(length+1);
recv(sock, data, length, 0);
data[length] = 0;

このコードに欠けているのはエラー処理です。送信呼び出しと受信呼び出しのそれぞれが失敗する可能性があります。recvsが受け取るデータが予想より少ない場合があります。しかし、これはあなたにアイデアを与えるはずです。

編集:recvが返すデータが少なすぎる場合に対処するには、これまでに読んだデータの数を数えながら、ループで実行します。

int length_bytes = 0;
while(length_bytes < 4){
   int read = recv(sock, ((char*)&nLength)+length_bytes, 4-length_bytes, 0);
   if (read == -1) some_error_occurred_check_errno();
   length_bytes += read;
}
于 2009-08-27T07:03:11.953 に答える
1

擬似コード(エラー処理は省略されています。注意してください):

送信者:

u_long converted = htonl( messageLength ); // convert from local byte order to network byte order
send( socket, (char*)&converted, sizeof( converted ), 0 );

受信機:

u_long messageLength;
recv( socket, (char*)&messageLength, sizeof( messageLength ), 0 );
messageLength = ntohl( messageLength ); convert from network byte order to local byte order
于 2009-08-27T07:01:35.653 に答える
0

これがあなたの求めているものかどうかはわかりませんが、固定長の文字列として送信します(たとえば、4文字、実際の長さはあなた次第です)。つまり、データ長が164の場合は、文字列「0164」を送信します。これにより、もう一方の端のバイト順序の問題が解決され、読み取りとデバッグが容易になります。

このような文字列を生成するには、stringstreamを使用できます。

#include <sstream>
#include <iomanip>

std::string MakeLenStr( int n ) {
  std::ostringstream os;
  os << std::setw(4) << std::setfill('0') << n;
  return os.str();
}

送信するには:

std::string s = MakeLenStr( len );
send( sock, s.c_str(), 4 );

もう一方の端でそれを読むには、次のようになります。

char a[5] = {0};
recv( sock, a, 4 );
int n = atoi( a );
于 2009-08-27T07:00:09.150 に答える