[英]Winsock receiving extra data
我尝试使用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
五
6
...
2600
如果我尝试跳过“ / 0”数据包,则来自服务器的数据也会被这样跳过:
1
2
3
4
五
6
9 <-7和8丢失
...
2600
我究竟做错了什么 ?
在recv
的手册页中:
对于面向连接的套接字(例如,类型为SOCK_STREAM),调用recv将返回当前可用的尽可能多的数据-达到指定缓冲区的大小。
这意味着recv
可能会给您不完整的数据包。 如果数据包上有固定长度或终结符,则需要在附加到缓冲区的循环中调用recv
,直到接收到全部。 但是,这带来了另一个问题,第二次调用recv
可能会给您最后一个数据包的其余部分,也可能给您另一个数据包的一部分。
在您的情况下,发送方的发送速度可能快于接收方的接收速度,在这种情况下, recv
将阻塞(如果套接字正在阻塞)或返回错误(如果套接字未阻塞)。 您必须检查recv
返回的值才能知道它是什么。 如果套接字是非阻塞的,则recv
将返回SOCKET_ERROR
(即-1
),而WSAGetLastError
将返回WSAEWOULDBLOCK
。
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();
我找出了问题所在。
就像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 *发送,而不仅仅是整数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.