2

我尝试使用 Winsock 制作文件下载器,但我意识到如果 Internet 连接速度较慢,客户端会收到一些不需要的数据以及服务器发送的数据。
所以我做了一个简单的测试:
我从服务器发送从 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可能会给你最后一个数据包的剩余部分,还有另一个数据包的一部分。

在您的情况下,发送者的发送速度也可能比接收者接收它们的速度快,在这种情况下,recv将阻塞(如果套接字阻塞)或返回错误(如果套接字非阻塞)。您必须检查返回的值recv才能知道它是什么。如果套接字是非阻塞的,那么recv将返回SOCKET_ERROR(即-1)并WSAGetLastError返回WSAEWOULDBLOCK

于 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 回答