socket编程中recv的错误使用

在昨天的测试中,终于发现了APICollector上传样本至Executor时样本错误的问题。问题原因在对socket中recv函数的理解错误。

之前的做法是:在发送端APICollector首先发送样本的信息至Executor,包括样本名称、大小等,然后将样本分成一块一块传送,每块2048字节,在Executor端收到样本信息后,计算数据块的数目N,然后recv这么多次数,每次都保存到缓冲区Data(char [BUFFER_LENGTH],BUFFER_LENGTH=2048),然后写入文件中,均以二进制方式读和写。

一直运行都没发现问题,后来出现了传入的样本无法执行的问题才了解到在运行的过程中传入的样本的MD5码有时和原样本的MD5码不同的情况。昨天详细分析了一下两个样本的十六进制内容,发现一开始的内容都是相同的。但是到中间的时候,明明是一个块的内容,在传入样本中却被一大块的0隔开了,而且经过多次测试,并不是在固定的地方被隔开,而且0的个数也是不定的,也就是说,原本一块的内容中穿插了不定数目的0。问题应该在接收函数这里,即recv函数,它的介绍如下:

从一个套接口接收数据,表头文件:
 #include
 #include
 int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags);
 s:一个标识已连接套接口的描述字。
 buf:用于接收数据的缓冲区。
 len:缓冲区长度。
 flags:指定调用方式。
这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,  
(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR
(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

之后又查询了一下关于大数据recv的资料。突然发现问题在我对recv这个函数的理解错误,APICollector一块一块的发送,Executor并不一定就是一块一块的接收的,也就是说recv接收时可能会出现一次接收不足buf的情况,当然也有可能出现多于buf的情况,因为出现了前者情况,才会使一块中间插入了一堆0的情况,那是因为 接收不足buf,而buf每次接收数据前都memset为0了,所以将这个buf情况存到文件中时,就出现一堆0的情况。

找到原因就好解决了,不能依靠数据块的数目N来recv,而是通过recv的返回值,即其实际copy到buf的字节数来判断,即累加每次的copy的字节数,若和大于等于文件大小,即结束recv。如下:

ULONGLONG bytesAll = FileInfo->FileLen;
char *Data = new char[BUFFER_LENGTH];
int bytesReceivedOnce = 0;	//一次接收的数据
ULONGLONG bytesReceivedAll = 0;
while(bytesReceivedAll < bytesAll)
{
	memset((char *)Data, 0, BUFFER_LENGTH);
	if ((bytesReceivedOnce = recv(client, Data, BUFFER_LENGTH, 0)) <= 0)
	{
		cout<<"SocketServer: Download file error!"<

修改后,经过多次测试传入大文件,完全正确,传入的样本的MD5码现在和原文件的一样了~



你可能感兴趣的:(C/C++,网络)