2

Winsock を使用してファイル ダウンローダを作成しようとしましたが、インターネット接続が遅いと、クライアントがサーバーから送信されたデータと一緒に不要なデータを受信することに気付きました。
そこで、簡単なテストを行いました:
サーバーから 1 から 30000 までの数字を送信しています:

char* buf = (char*)malloc(BUFLEN);          
for( int i = 0;i < 30000;++i ){
     ZeroMemory(buf, BUFLEN);
     itoa(i+1, buf, 10);
     send(current_client, buf, BUFLEN, 0);
}
free(buf);

クライアントはそれらを受け取り、保存します。

char *buf = (char*)malloc(DEFAULT_BUFLEN);
ofstream out;
out.open(filename, ios::binary);
for( int i = 0;i < 30000;++i ){
     ZeroMemory(buf, DEFAULT_BUFLEN);
             recv(ConnectSocket, buf, DEFAULT_BUFLEN, 0);
             out.write(buf, DEFAULT_BUFLEN);
             out << endl;
}
out.close();
free(buf);

そして、ファイルには次のようなものが含まれていると予想します:
1
2
3
4
...
30000
しかし、代わりに、'/0' を含む余分なパケットが送信され、ファイルは次のようになります:
1
2
3

4
5

6
...
2600
「/0」パケットをスキップしようとすると、サーバーからのデータも次のようにスキップされます:
1
2
3
4
5
6
9 <- 7 と 8 がありませ
ん ...
2600
何が間違っていますか? ?

4

3 に答える 3

3

マニュアルページからrecv

接続指向のソケット (たとえば SOCK_STREAM 型) の場合、recv を呼び出すと、指定されたバッファーのサイズまで、現在利用可能なデータが返されます。

これは、recv不完全なパケットが得られる可能性があることを意味します。パケットに固定長またはターミネータがある場合は、recvすべてを受信するまでバッファに追加するループを呼び出す必要があります。ただし、これは別の問題をrecv引き起こします。2 番目の呼び出しでは、最後のパケットの残りだけでなく、別のパケットの一部も提供される可能性があります。

あなたの場合、送信者が受信者が受信できるよりも速く送信している可能性もあります。その場合recv、ブロックするか (ソケットがブロックしている場合)、エラーを返します (ソケットがブロックしていない場合)。から返された値を確認して、recvそれが何であるかを知る必要があります。ソケットがノンブロッキングの場合、 は(つまり)をrecv返し、を返します。SOCKET_ERROR-1WSAGetLastErrorWSAEWOULDBLOCK

于 2012-08-01T17:51:41.583 に答える
3

recv()実際に受信したバイト数を示します。バッファが完全にいっぱいになっていない場合でも、バッファ全体を出力するという値を無視しています。サーバー側でも同じです-フォーマットしているよりも多くのデータを送信しています。

send()また、との両方が、recv()要求したよりも少ないバイトを送受信できることも考慮していません。

代わりにこれを試してください:

bool sendraw(SOCKET socket, void *buf, int buflen)
{
    unsigned char *p = (unsigned char*) buf;
    while (buflen > 0)
    {
        int sent = send(socket, p, buflen, 0);
        if (sent < 1) return false;
        p += sent;
        buflen -= sent;
    }
    return true;
}   

for( int i = 0; i < 30000;++i )
{   
    int j = htonl(i+1);
    if (!sendraw(current_client, &j, sizeof(int)))
        break;
}   

.

bool recvraw(SOCKET socket, void *buf, int buflen)
{
    unsigned char *p = (unsigned char*) buf;
    while (buflen > 0)
    {
        int received = recv(socket, p, buflen, 0);
        if (received < 1) return false;
        p += received;
        buflen -= received;
    }
    return true;
}   

ofstream out; 
out.open(filename, ios::binary); 
for( int i = 0; i < 30000;++i )
{ 
    int j;
    if (!recvraw(ConnectSocket, &j, sizeof(int)))
        break;
    out << ntohl(j) << endl; 
} 
out.close(); 
于 2012-08-01T19:51:43.780 に答える
-1

私は何が問題なのかを理解しました。
Joachim と Remy が言ったように、クライアントがバッファ全体を受信しない場合があるため、コードを少し変更しました:
サーバー側から:

             sent = 0;
             while( sent != BUFLEN ){
                    sent += send(current_client, (buf+sent), BUFLEN-sent, 0);
             }

とクライアント:

             recvd = 0;  
             while( recvd != DEFAULT_BUFLEN ){  
                    recvd += recv(ConnectSocket, (buf+recvd), DEFAULT_BUFLEN-recvd, 0);  
             }  

そして、バッファーが完全に満たされていないことを知っています。通常、バイナリデータを整数だけでなく char* として送信しています。

于 2012-08-01T21:45:42.370 に答える