简体   繁体   English

Winsock收到额外的数据

[英]Winsock receiving extra data

I tried to make a file downloader using Winsock but I realized that if the Internet connection is slower the client receives some unwanted data along with the data sent by the server. 我尝试使用Winsock制作文件下载器,但我意识到,如果Internet连接速度较慢,客户端会收到一些不需要的数据以及服务器发送的数据。
So I made a simple test: 所以我做了一个简单的测试:
From the server I am sending the numbers from 1 to 30000: 我从服务器发送的数字从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);

The client receives and saves them: 客户端接收并保存它们:

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);

And I expect in the file to see something like that: 我希望在文件中看到类似的内容:
1 1
2 2
3 3
4 4
... ...
30000 30000
But instead there are some extra packets, containing '/0', transmitted and the file looks like that: 但是取而代之的是传输了一些额外的包含“ / 0”的数据包,文件看起来像这样:
1 1
2 2
3 3

4 4
5

6 6
... ...
2600 2600
If I try to skip the '/0' packets, data from the server is also skipped like that: 如果我尝试跳过“ / 0”数据包,则来自服务器的数据也会被这样跳过:
1 1
2 2
3 3
4 4
5
6 6
9 <- 7 and 8 are missing 9 <-7和8丢失
... ...
2600 2600
What am I doing wrong ? 我究竟做错了什么 ?

From the manual page of recv : recv手册页中:

For connection-oriented sockets (type SOCK_STREAM for example), calling recv will return as much data as is currently available—up to the size of the buffer specified. 对于面向连接的套接字(例如,类型为SOCK_STREAM),调用recv将返回当前可用的尽可能多的数据-达到指定缓冲区的大小。

This means that recv might give you incomplete packets. 这意味着recv可能会给您不完整的数据包。 If you have a fixed length or a terminator on the packets, you need to call recv in a loop appending to the buffer until all is received. 如果数据包上有固定长度或终结符,则需要在附加到缓冲区的循环中调用recv ,直到接收到全部。 However this presents another problem, that the second call to recv might give you the remaining of the last packet, but also parts of another packet. 但是,这带来了另一个问题,第二次调用recv可能会给您最后一个数据包的其余部分,也可能给您另一个数据包的一部分。

In your case it might also be that the sender is sending faster than the receiver can receive them, in which case recv will either block (if socket is blocking) or return an error (if socket is non-blocking). 在您的情况下,发送方的发送速度可能快于接收方的接收速度,在这种情况下, recv将阻塞(如果套接字正在阻塞)或返回错误(如果套接字未阻塞)。 You have to check the value returned from recv to know what it is. 您必须检查recv返回的值才能知道它是什么。 If the socket is non-blocking, then recv will return SOCKET_ERROR (ie -1 ) and WSAGetLastError will return WSAEWOULDBLOCK . 如果套接字是非阻塞的,则recv将返回SOCKET_ERROR (即-1 ),而WSAGetLastError将返回WSAEWOULDBLOCK

recv() tells you how many bytes were actually received. recv()告诉您实际接收了多少个字节。 You are ignoring that value that outputting the entire buffer even if it was not filled up completely. 您会忽略输出整个缓冲区的那个值,即使它没有完全填满也是如此。 Same thing in the server end - you are sending more data then you are formatting. 服务器端也是如此-发送更多数据然后进行格式化。

You are also not taking into account that both send() and recv() can send/receive fewer bytes then you requested. 您也没有考虑到send()recv()可以发送/接收的字节数少于您请求的字节数。

Try this instead: 尝试以下方法:

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(); 

I figured out what was the problem. 我找出了问题所在。
Exactly as Joachim and Remy said sometimes the client doesn't receive the entire buffer so I modified the code a little: 就像Joachim和Remy所说的那样,有时客户端不会收到整个缓冲区,因此我对代码做了一些修改:
from the server side: 从服务器端:

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

and client: 和客户:

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

and I know that the buffer is not filled entirely, normally I'm sending binary data as char* , not just integers. 而且我知道缓冲区没有完全填满,通常我将二进制数据作为char *发送,而不仅仅是整数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM