C++中用Socket实现对结构体、长字符串和图片的传输

首先说明下,本文的Socket传输引用了CBlockingSocket封装类

这个类比较特殊的是Send和Receive的最后一个参数是超时时间,其它与C库里的类似

首先说结构体吧,这里传输的结构体含有八个整型,如下

 

typedef struct exceptiontypecount{ 
 int img_num;                                  
 int ptz_num;                                   
 int preset_num;                              
 int video_num;                             
 int device_num;                           
 int total_num;                              
 int total_channel;                         
 int useless;                                 
}ExceptionCount,*pExceptionCount;


关于Socket的创建和连接这里就不说了,参见点击打开链接

这里只说下收发部分的实现

服务端:

    
    memset(counttemp,0,256);//清空缓存
    memcpy(counttemp,&ExCount,sizeof(ExCount));//将结构体转为字符串
    int countlen = sockCon.Send(counttemp,256,1000);

其中,char countemp[256], ExCount是实例化的结构体。

客户端:

        memset(countbuff,0,256);
        int len = sClient.Receive(countbuff,256,1000); // 接受故障数据
	memset( &count,0,sizeof(count));
	memcpy( &count, countbuff, sizeof(count) ); 

这里的count即为输出的结构体,countbuff同样也是个char[256]。

memset这一步非常重要,如果没有预先清空缓存,就容易出现乱码。

==================================================================================================

字符串:

要传一个500K左右的string字符串,肯定不能直接传,因为Socket传输是有字节上限的,好像是16000+字节

所以我们采用分段传输的策略

服务端:

	sockCon.Send(allSize,allNum.GetLength(),100);
	if(!(sockCon.Receive(resbuff,2,10)))
	{
	    printf("数据量信息发送失败\n");
	    return ;
	}
	else
	    printf("数据量信息发送成功\n");

			 
	    temp = AllExDataPtr;
	    BytesSent = 0;
	    while(BytesSent<AllExDataSize)//分段传输数据
	    {
	     int BytesSentThisTime=sockCon.Send(temp,((AllExDataSize-BytesSent)<256)?(AllExDataSize-BytesSent):256,1000);
	     BytesSent = BytesSent + BytesSentThisTime;
             temp = temp + BytesSentThisTime;
	    }

其中,AllExDataPtr是指向需要传输的string的一个char*指针,定义为:AllExDataPtr = (char*) AllExData.c_str();

           AllSize就是这段数据的字节数,需要首先传到客户端,客户端才可以正常接收

客户端:

		int sizelen = sClient.Receive(sizebuff,10,50);//接受数据大小
		if(sizelen !=0)
		{
			char flag[]="1";
			sClient.Send(flag,2,10);
		}
		sizebuff[sizelen]='\0';
		exsize = atoi(sizebuff);
		printf("数据大小:%d\n",exsize);
		extemp = new char[exsize+256];//初始化存储信息的数据,留出冗余空间,以防溢出
		exdata = extemp;//新建指针指向起始地址,方便接收后调用

		int BytesReceivedThisTime = 0;
		while(BytesReceived < exsize)
		{
			BytesReceivedThisTime = sClient.Receive(temp,256,1000);
			strcpy(extemp,temp);
			BytesReceived = BytesReceived + BytesReceivedThisTime;
			extemp = extemp + BytesReceivedThisTime;
		}
                std::string out = exdata;

其中out即为要输出的string,这里建了两个指针指向新建的char数组,其中extemp用于接收时的迭代,exdata用于调用该数组

 

================================================================================================

图片:

要实现的就是收发图片,这里只写客户端发服务端收的例子,其他均类似

客户端:

int ImageSocketClient::UploadFileBySocket(std::string filename, int type)
{
	CFileFind Finder;//确保本地有该文件
	if (!Finder.FindFile(filename.c_str()))
	{
		return -1;//文件未找到
	}

	char inbuff[2];
	sockType = SOCKET_UPLOAD; //上传操作标志位
	CString str; //将整形标志位转成字符型
	str.Format("%d", sockType);
	char* flag = str.GetBuffer(0);

	if(!m_bConnectOK) //如果Socket没有连接
	{   
		try
		{
			CSockAddr saClient(m_strSocketAddress,5858);//设IP和端口
			m_SocketClient.Create();
			//创建套接字
			m_SocketClient.Connect(saClient);
			//发起连接
			m_SocketClient.Send(flag,str.GetLength(),4); // 发送上传标识符
			int len=m_SocketClient.Receive(inbuff,2,4);
			if(len == 0)
				return 0;
			else
				m_bConnectOK = TRUE; //连接成功

		}
		catch (CBlockingSocketException* e)
		{
			delete(e);
			return 0;
		}
	}
	else
	{
		try
		{
			m_SocketClient.Send(flag,str.GetLength(),4); // 发送上传标识符
			int len=m_SocketClient.Receive(inbuff,2,4);
			if(len == 0)
				return 0;
		}
		catch (CBlockingSocketException* e)
		{
			delete(e);
			return 0;
		}
	}

	// 获取本地路径
	char szPath[MAX_PATH];
	GetCurrentDirectory(MAX_PATH, szPath);
	CString strpath(szPath);
	int idx = strpath.ReverseFind(_T('\\'));
	if( idx != strpath.GetLength()-1)//如果最后一个字符不是\(非根目录),则添加\。
	{
		strpath = strpath+"\\";
	}
	//设置本地路径+文件名
	CString strLocalFile;
	strLocalFile.Format("%s%s",strpath,filename.c_str()); 

	CString strRemoteFile;
	//服务器保存路径
	switch (type)
	{
	case 1:
		strRemoteFile.Format("\\Preset\\%s",filename.c_str());
		break;
	case 2:
		strRemoteFile.Format("\\IMG_Exception\\%s",filename.c_str());
		break;
	case 3:
		strRemoteFile.Format("\\PTZ_Exception\\%s",filename.c_str());
		break;
	case 4:
		strRemoteFile.Format("\\PRESET_Exception\\%s",filename.c_str());
		break;
	default:
		strRemoteFile = filename.c_str();
		break;
	}

	//发送服务器保存路径,方便后续存储
	try
	{
		char inbuff[2];
		char * buf = strRemoteFile.GetBuffer(0);
		m_SocketClient.Send(buf,MAX_PATH,5);
		int len = m_SocketClient.Receive(inbuff,2,5);
		if(len == 0)
			return 0; //发送失败
	}
	catch (CBlockingSocketException* e)
	{
		delete(e);
		return 0;
	}



	FILE * fstream = fopen(strLocalFile,"rb"); // 读取文件数据

	if ( NULL == fstream )
	{
		printf( "打开文件失败,错误码:%d", GetLastError() );
		return 0;
	}

	int nNumRead = 0;
	char temp[256];

	if(NULL != fstream)
	{
		WaitForSingleObject(m_hMutex,MutexTime_ImageSocket);
		printf("开始上传\n");
		while( !feof( fstream ) )//未到文件末尾,则继续发送
		{
			nNumRead = fread( temp, 1, 256, fstream );
			m_SocketClient.Send( temp, nNumRead, 500 );
		}
		printf("上传成功\n");
		fclose( fstream );
		ReleaseMutex(m_hMutex);
	}
	m_SocketClient.Close();
	m_SocketClient.Cleanup();
	return 1;
}


 

这里首先传操作标识符,让服务器准备好接收图片,

然后根据文件名和类型确定服务端的图片存储路径,并传给服务端

然后再分段存储图片

 

服务端:

int ReceiveImage(CBlockingSocket& sockCon)
{
	char szPath[MAX_PATH];//获取本地路径
	GetCurrentDirectory(MAX_PATH, szPath);
	CString strpath(szPath);
	
	// 获取客户端传来的存储路径
	char pathtemp[MAX_PATH];
	int len = sockCon.Receive( pathtemp,MAX_PATH,5 );
	if(len == 0)
		return 0;
	else // 若获取成功则返回成功标志位
	{
		char sucflag[2] = "1";
		sockCon.Send(sucflag,2,5);
	}
	CString filepath(pathtemp);
	
	//设置存储文件路径
	CString strSaveFile;
	strSaveFile.Format("%s%s",strpath,filepath);
	printf("存储路径:%s\n",strSaveFile);

	CFileFind Finder;//确保本地没有该文件

	if (Finder.FindFile(strSaveFile))
	{

		DeleteFile(strSaveFile);
	}

	WaitForSingleObject(m_hMutex,MutexTime_ImageSocket);
	FILE * fstream = fopen(strSaveFile, "wb" );//打开文件操作
	if ( NULL == fstream )
	{
		return 0;
	}

	//分段接受并存储文件
	char temp[256];
	int nNumRead = 0;
	printf("开始接收图片\n");
	while( true )
	{

		nNumRead = sockCon.Receive(temp,256,500);//文件分段接收
		if ( 0 == nNumRead )//若仍有数据,则持续接受
			break;
		fwrite( temp, 1, nNumRead, fstream );
		
	}
	ReleaseMutex(m_hMutex);
	printf("图片接收成功\n");
	fclose( fstream );
	return 1;

}


 

最后我再说几条在用CBlockingSocket封装类需要注意的几点

1. 由于Send和Receive函数最后的参数是超时时间,所以在传输大文件时,尽量将其设得高点,我在传输大图片时就遇到过超时而只传一半的情况。

2. 若客户端和服务端有多次Send和Receive交互,对应的Send和Receive之间的接受字节大小(即第二个参数)一定要对应。

3. 在连续两个Send或Receive时一定要注意发送方和接收方的字节长度,若有偏差则很可能会将Send的字符串分割到两个Receive中去。

4. 全局变量指针在新开的线程中无法调用,报错为Bad Ptr,只能在线程中定义新指针。


  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值