4

サーバーから一度に 1 つのパケットを受信しようとしています。パケットの速度が速すぎて、それぞれのサイズが定義されていないため、読み取るバイト数を指定して recv() を呼び出すと、最初のパケットが読み取られ、おそらく2番目のパケット。各パケットは NULL 終端されているため、NULL バイトが受信されるまでバイトごとに読み取ることを考えました。

    int recvLen = 0;
    char TB;
    char recvBuffer[1024];
    while (recv(Socket, &TB, 1, 0) > 0 && TB != 0 && recvLen < 1024)
    {
        recvBuffer[recvLen] = TB;
        recvLen++;
    }

この方法はまったく効率的ではないと思います。サーバーが 1024 バイトを送信した場合、recv()1024 回呼び出されます。

NULL 文字が受信されるまで recv() する他の方法はありますか、または私が使用している方法よりも優れた方法はありますか?


EDIT:サーバーから送信されたデータの前にパケットサイズを追加しましたが、今では、誤ったパケットの場合、または理由がない場合でも、パケットがめちゃくちゃになり、正しいデータが受信されません。ここに私のコードがあります

#define UPLOAD_LEN 2755
int PacketSize, recvLen;
char Size[4];
char recvBuffer[UPLOAD_LEN+1];
while(1)
{
    if(recv(Socket,Size,4,0)>0)
    {
        Size[4] = '\0';
        PacketSize = atoi(Size);
        if (PacketSize > UPLOAD_LEN || PacketSize <= 0) continue;
        recvLen = recv(Socket, recvBuffer, PacketSize, 0);
    } else recvLen = -1;
    if (recvLen > 0)
    {
        recvBuffer[recvLen] = '\0';
        ProcessData(recvBuffer);
    }
    else
    {
        closesocket(Socket);
    }
}
4

3 に答える 3

5

通信プロトコルが、プログラマーが期待する1つのユースケースをサポートしない理由を理解したことはありません。任意のサイズのblobを、境界に揃えられたsendとrecvと交換します。

したがって、ここには実際のショートカットはありません。前回のrecvの呼び出しで残ったデータを保持する永続バッファーを保持する必要があります。データを受け取ったら最後にデータを追加し続け、データを見つけるたびに終了ゼロに戻ります。おそらく少なくとも部分的な後続パケットがあるので、それをバッファの先頭に移動して、次の呼び出しの初期状態として機能させます。

于 2011-02-19T15:58:17.080 に答える
2

バッファを作成し、そこからプロトコル メッセージを抽出します。バッファーに完全なメッセージが含まれていない場合は、メッセージが含まれるまで recv() を実行します。これは、ソケットをバッファリングするための単純な C 実装です (軽くテストされ、MS VS2008 でコンパイルされます)。

#include <winsock2.h>
#include <string.h>

typedef struct buffsock {
    SOCKET s;
    char* buf;
    size_t maxlen;
    size_t curlen;
} buffsock_t;

void buffsock_init(buffsock_t* bs,SOCKET s,size_t maxlen)
{
    bs->s = s;
    bs->buf = malloc(maxlen);
    bs->maxlen = maxlen;
    bs->curlen = 0;
}

void buffsock_free(buffsock_t* bs)
{
    free(bs->buf);
    bs->buf = NULL;
    bs->maxlen = 0;
    bs->curlen = 0;
    bs->s = INVALID_SOCKET;
}

/* Attempt to fill internal buffer.
 * Returns 0 if socket closed.
 * Returns number of additional bytes in buffer otherwise.
 */
int buffsock_fill(buffsock_t* bs)
{
    int bytes;
    bytes = recv(bs->s,bs->buf + bs->curlen,bs->maxlen - bs->curlen,0);
    if(bytes == SOCKET_ERROR)
        return -1;
    bs->curlen += bytes;
    return bytes;
}

/* Return up to <bytes> from buffered socket.
 * If return value 0 socket was closed.
 * If return value >0 and <bytes socket received partial message.
 */
int buffsock_bytes(buffsock_t* bs,size_t bytes,void* msg)
{
    while(bs->curlen < bytes)
    {
        int result;
        result = buffsock_fill(bs);
        if(result == -1)
            return -1; /* error on socket */
        if(result == 0)
            break;
    }
    if(bytes > bs->curlen)
        bytes = bs->curlen;
    memcpy(msg,bs->buf,bytes);
    bs->curlen -= bytes;
    memmove(bs->buf,bs->buf + bytes,bs->curlen);
    return bytes;
}

/* Implmementation of a protocol with two big-endian bytes indicating
 * msg size followed by <size> bytes of message.
 * Returns -1 if error on socket.
 * Returns -2 if partial message recv'd (shouldn't happen as long as
 * internal buffer is bigger than max message size).
 * Returns -3 if user buffer not big enough to hold message.
 * Returns size of message otherwise.
 */
int get_protocol_message(buffsock_t* bs,void* msg,size_t maxlen)
{
    int bytes;
    u_short len;
    bytes = buffsock_bytes(bs,sizeof(u_short),&len);
    if(bytes == 0)
        return 0;  /* socket closed, no more messages */
    if(bytes == -1)
        return -1; /* error on socket */
    if(bytes < sizeof(u_short))
        return -2; /* partial message */
    len = ntohs(len);
    if(len > maxlen)
        return -3; /* message exceeds user buffer */
    bytes = buffsock_bytes(bs,len,msg);
    if(bytes < len)
        return -2; /* partial message */
    return bytes;
}

次のように使用します。

int len;
char msg[256];
buffsock_t bs;
/* open a socket */
buffsock_init(&bs,sock,1024);
len = get_protocol_message(&bs,msg,sizeof(msg));

重要なのは、TCP/IP にはメッセージ境界の概念がないため、recv() は 1 から要求されたバイト数を返すことができるということです。受信したバッファには、複数のメッセージまたは部分的なメッセージが含まれている可能性があります。

このコードは、受信したデータをバッファに追加するだけです。プロトコルはバッファからのバイトを要求し、バッファはソケットから満たされます。バイトが削除されると、残りのバッファリングされたデータがバッファの先頭にシフトされます。

この場合、2 バイトが要求され、長さに変換されてから、残りのバイトが要求されます。要求を満たすことができない場合は、さらにデータが受信されます。

お役に立てれば。

于 2011-02-19T18:54:41.763 に答える
0

これを行う方法はいくつかあります。

オプション #1: 情報を送信する前に、パケットのサイズを含む int をパケットの前に送信します。この int を読み取り、受信した int の長さのバッファーを割り当てます。その後、一度にパケット全体を recv() できます。

オプション #2: 一度に 1024 バイトを読み取ります。recv() は、読み取ったバイト数を返します。その後、 strlen() を使用して、バッファに複数のパケットがあるかどうかを確認できます。これを再帰的にするのがおそらく最も理にかなっているでしょう (1024 バイトで複数のパケットを持つことができると仮定して)。NULL バイトに基づいてパケットを分割するようにします。

于 2011-02-19T16:04:19.507 に答える