socket编程中recv的错误使用

7 篇文章 0 订阅

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

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

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

从一个套接口接收数据,表头文件:
 #include<sys/types.h>
 #include<sys/socket.h>
 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!"<<endl;
		file.close();
		delete []Data;
		return false;
	}
	bytesReceivedAll += bytesReceivedOnce;
		
	file.write(Data, bytesReceivedOnce);		
}

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



  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Linux使用Socket网络编程实现FTP的put上传功能,可以通过以下步骤来实现: 1. 导入必要的头文件,包括sys/socket.h、netinet/in.h和arpa/inet.h,以及stdio.h、string.h和unistd.h。 2. 创建客户端Socket使用函数socket()创建一个套接字。套接字使用AF_INET作为地址族,并使用SOCK_STREAM作为套接字类型。 3. 使用函数connect()连接到服务器的FTP端口。为此,需要定义服务器的IP地址和端口号,并将其传递给connect()函数。 4. 获取要上传的文件的文件名,并使用open()函数打开该文件。如果文件打开失败,则发送错误消息到服务器,并退出程序。 5. 使用send()函数将要上传的文件名发送到服务器。 6. 使用read()函数从文件读取数据,并使用send()函数将数据发送到服务器。发送的数据可以使用固定大小的缓冲区。 7. 当文件读取完成后,发送一个结束标志,并关闭文件。这应该在循环完成,直到文件被完全上传。 8. 使用recv()函数接收从服务器返回的响应,并根据响应进行相应的处理。例如,如果服务器返回成功的响应,表示文件上传成功。 9. 最后,使用close()函数关闭套接字连接,并释放所有资源。 上述是使用Socket网络编程在Linux实现FTP的put上传的基本步骤。需要注意的是,FTP协议本身是非常复杂的,还需要考虑进行用户身份验证、传输模式选择等更多功能的实现。这只是一个简单的上传示例,更完整和高级的FTP上传功能可能需要更多的代码和功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值