socket之udp传输

在项目中,需要客户端从服务器端下载一些配置文件。由于这些文件个头不是很大,最大的也不超过2M,另外,传输是在局域网中进行的,为了传输速度和节省资源,采用了UDP传输。

UDP传输有对等传输,有C/S方式的传输,项目中选用的是C/S传输。

Client头文件代码

#ifndef _CUDP_SEND_FILE
#define _CUDP_SEND_FILE
#ifndef _CUDP_SEND_FILE_EXP
#define _CUDP_SEND_FILE_EXP extern "C" __declspec(dllimport)
#endif
/** szFileName 文件的名字:如apr.xml
szPathFile:保存到本地的文件的路径
return : 是否获取文件是成功的
*/
_CUDP_SEND_FILE_EXP bool GetFile(const char* szFileName, const char* szPathFile, unsigned short local_port,
	const char* remote_ip, unsigned short remote_port);
#endif

Client源文件

#include "StdAfx.h"
#define _CUDP_SEND_FILE_EXP extern "C" __declspec(dllexport)
#include "UdpSendFile.h"
#include <Winsock2.h>
#pragma comment(lib, "WS2_32.lib")
#include<iostream>
using std::cout;
using std::endl;

#define RECV_BUF 1024 * 1024
#define MAX_BAG_SIZE 2000

_CUDP_SEND_FILE_EXP bool GetFile(const char* szFileName, const char* szPathFile,unsigned short local_port,
	const char* remote_ip, unsigned short remote_port)
{
	if(szFileName == NULL || szPathFile == NULL || remote_ip == NULL)
		return false;
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		WSACleanup( );
		return false;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return false;
	}
	SOCKET client_socket = socket(PF_INET,SOCK_DGRAM,0);
	if(client_socket == SOCKET_ERROR)
	{
		int nError = WSAGetLastError();
		WSACleanup( );
		return false;
	}
	SOCKADDR_IN local_addr;
	memset(&local_addr, 0 ,sizeof(SOCKADDR));
	local_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	local_addr.sin_family=AF_INET;
	local_addr.sin_port=htons(local_port);
	int nBindingError = 0;
	if(SOCKET_ERROR == bind(client_socket, (SOCKADDR*)&local_addr,sizeof(SOCKADDR)))
	{
		nBindingError = WSAGetLastError();
		WSACleanup( );
		return false;
	}

	SOCKADDR_IN scp_addr;
	int nAddressLen = sizeof(scp_addr);
	memset(&scp_addr, 0 , nAddressLen);
	scp_addr.sin_addr.S_un.S_addr=inet_addr(remote_ip);
	scp_addr.sin_family=AF_INET;
	scp_addr.sin_port=htons(remote_port);

	sendto(client_socket,szFileName,strlen(szFileName) + 1,0,(SOCKADDR*)&scp_addr,nAddressLen);
	//
	timeval   outtime   ;     //   超时结构 
	FD_SET   fdr   =   {1,   client_socket};    
	outtime.tv_sec   =   10;      
	outtime.tv_usec   = 0;  
	int   nSelectRet;
	nSelectRet=::select(0,   &fdr,   NULL,   NULL,   &outtime);   //检查可读状态 
	if(SOCKET_ERROR==nSelectRet)    
	{  
		int nErrorCode   =   WSAGetLastError();  
		closesocket(client_socket);
		WSACleanup( );
		return   false;  
	}  
 	if(nSelectRet==0)   //超时发生,无可读数据    
	{  
		int nErrorCode   =   WSAGetLastError();  
		cout<<"客户端-超时-超时-"<<"error code is"<<nErrorCode<<endl;
	}  
	else
	{
		int recvCount = 0;
		char* recv = new char[RECV_BUF]; 
		memset(recv,0,RECV_BUF);

		if(SOCKET_ERROR == recvfrom(client_socket,recv, RECV_BUF,0, (SOCKADDR*)&scp_addr, &nAddressLen))
		{
				int nErrorCode = WSAGetLastError();
				if(WSAEMSGSIZE == nErrorCode)
				{
					cout<<"数据包太大,超出缓存区"<<endl;
				}
				else if(WSAECONNABORTED == nErrorCode)
				{
					cout<<"超时"<<endl;
				}
				else if(WSAEFAULT == nErrorCode)
				{
					cout<<"fromLen没有被初始化,Unit中不会对此进行要求"<<endl;
				}
				else if(WSAESHUTDOWN == nErrorCode)
				{
					cout<<"本地套接字被关闭"<<endl;
				}
		}
		int recvNumber = atoi(recv);
		char tempRecBuff[MAX_BAG_SIZE];
		char* pResult = recv;
		for(int ii = 0; ii < recvNumber; ++ii)
		{
			nSelectRet=::select(0,   &fdr,   NULL,   NULL,   &outtime);   //检查可读状态 
			if(SOCKET_ERROR==nSelectRet)    
			{  
				int nErrorCode   =   WSAGetLastError();  
				closesocket(client_socket);
				WSACleanup( );
				return   false;  
			}  
			if(nSelectRet==0)   //超时发生,无可读数据    
			{  
				int nErrorCode   =   WSAGetLastError();  
				cout<<"客户端-超时-超时-"<<"error code is"<<nErrorCode<<endl;
			}  
			else
			{
				memset(tempRecBuff,0,MAX_BAG_SIZE);
				if(SOCKET_ERROR == recvfrom(client_socket,tempRecBuff, MAX_BAG_SIZE,0, (SOCKADDR*)&scp_addr, &nAddressLen))
				{
					int nErrorCode = WSAGetLastError();
					if(WSAEMSGSIZE == nErrorCode)
					{
						cout<<"数据包太大,超出缓存区"<<endl;
					}
					else if(WSAECONNABORTED == nErrorCode)
					{
						cout<<"超时"<<endl;
					}
					else if(WSAEFAULT == nErrorCode)
					{
						cout<<"fromLen没有被初始化,Unit中不会对此进行要求"<<endl;
					}
					else if(WSAESHUTDOWN == nErrorCode)
					{
						cout<<"本地套接字被关闭"<<endl;
					}
				}
				int nLen = strlen(tempRecBuff);
				memcpy(pResult,tempRecBuff,nLen);
				//
				//::sendto(client_socket,"o",2,0,(SOCKADDR*)&scp_addr,nAddressLen);
				pResult +=  nLen;
				cout << ii <<endl;
			}
		}

		cout<<recv<<endl;
		FILE* fp = fopen(szPathFile,"w");
		fprintf(fp,recv);
		fflush(fp);
		fclose(fp);
		delete []recv;
	}
	closesocket(client_socket);
	WSACleanup();
	return true;
}

服务器端代码

#include "stdafx.h"
//无连接套接字
#include<stdio.h>
#include<WinSock2.h>
#include<iostream>
#include<Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

#define MTU 1024

bool StartUDPScp(int& nErrorCode);
int _tmain(int argc, _TCHAR* argv[])
{
	int ErrorCode = 0;
	StartUDPScp(ErrorCode);
	system("pause");

	return 0;
}



bool StartUDPScp(int& nErrorCode)
{

	unsigned short local_port(6000);
	unsigned short rev_size(1024);
	WORD wVersionRequested;
	WSADATA wsaData;
	int WSStartUpErr;
	wVersionRequested = MAKEWORD( 1, 1 );
	WSStartUpErr = WSAStartup( wVersionRequested, &wsaData );
	if ( WSStartUpErr != 0 ) {
		nErrorCode = WSStartUpErr;
		return false;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 )
	{
			WSACleanup();
			return false;
	}
	/**创建套接字
	*/
	SOCKET host_sock = socket(AF_INET, SOCK_DGRAM, 0);
	/**创建三元素
	*/
	SOCKADDR_IN host_addr;//大小和SOCKADDR的大小是相同的,都是16
	host_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	host_addr.sin_family = AF_INET;
	host_addr.sin_port = htons(local_port);
	if(bind(host_sock,(sockaddr*)&host_addr,sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		nErrorCode = WSAGetLastError();
		WSACleanup();
		return false;
	}	
	char* pRev_Buf = new char[rev_size];
	while(1){
		sockaddr scu_addr;
		int fromLen = sizeof(sockaddr);

		timeval   outtime   ;     //   超时结构 
		FD_SET   fdr   =   {1,   host_sock};    
		outtime.tv_sec   =   5;      
		outtime.tv_usec   = 0;  
		int   nSelectRet;
		nSelectRet=::select(0,   &fdr,   NULL,   NULL,   &outtime);   //检查可读状态 
		if(SOCKET_ERROR==nSelectRet)    
		{  
			nErrorCode   =   WSAGetLastError();  
			closesocket(host_sock);
			delete []pRev_Buf;
			return   false;  
		}  
		if(nSelectRet==0)   //超时发生,无可读数据    
		{  
			nErrorCode   =   WSAGetLastError();  
			cout<<"服务端-超时-超时-"<<"error code is"<<nErrorCode<<endl;
		}  
		else
		{
			if(SOCKET_ERROR == recvfrom(host_sock,pRev_Buf, rev_size,0, &scu_addr, &fromLen))//实际上这里的长度是不用初始化的,在linux中是这样的,但windows中,必须要进行初始化
			{
				nErrorCode = WSAGetLastError();
				if(WSAEMSGSIZE == nErrorCode)
				{
					cout<<"数据包太大,超出缓存区"<<endl;
				}
				else if(WSAECONNABORTED == nErrorCode)
				{
					cout<<"超时"<<endl;
				}
				else if(WSAEFAULT == nErrorCode)
				{
					cout<<"fromLen没有被初始化,Unit中不会对此进行要求"<<endl;
				}
				else if(WSAESHUTDOWN == nErrorCode)
				{
					cout<<"本地套接字被关闭"<<endl;
				}
				delete []pRev_Buf;
				return false;
			}
			SOCKADDR_IN* scu_addr_in = (SOCKADDR_IN*)(&scu_addr);
			//判断是否是授权的客户端进行连接
			cout<<"从"<<inet_ntoa(scu_addr_in->sin_addr)<<":"<<scu_addr_in->sin_port<<"收到的数据是:"<<pRev_Buf<<endl;
			//进行发送实验
			char fileName[256];
			memset(fileName,0,256);
			sprintf(fileName,"F:\\%s",pRev_Buf);
			FILE* fp = fopen(fileName, "r");
			if(fp)
			{
				fseek(fp,0,SEEK_END);
				int filelength = ftell(fp);
				filelength++;										//加入一个空格
				int fileOrignalLength = filelength;			
				rewind(fp);
				char* pBuff = new char[filelength];
				char* pOriginBuff = pBuff;
				memset(pBuff, 0, filelength);
				while(!feof(fp))
				{
					fgets(pBuff,filelength,fp);
					int nlen = strlen(pBuff);
					filelength -= nlen;
					pBuff += nlen;
				}
				fclose(fp);
				//分批发送
				int n = fileOrignalLength / MTU;
				int nn = fileOrignalLength % MTU;
				int nT = n + ((nn > 0) ? 1 : 0);
				cout << "has " << nT << "bag"<<endl;
				char* temp = pOriginBuff;
				//
				char szSendNum[64];
				memset(szSendNum, 0, sizeof(szSendNum));
				sprintf(szSendNum,"%d",nT);
				//
				::sendto(host_sock,szSendNum,strlen(szSendNum) + 1,0,(sockaddr*)&scu_addr,sizeof(SOCKADDR_IN));
				for(int ii = 0; ii < n;++ii)
				{
					::sendto(host_sock,temp,MTU,0,(sockaddr*)&scu_addr,sizeof(SOCKADDR_IN));
					temp += MTU;
					Sleep(2);

					//::recvfrom(host_sock,pRev_Buf, rev_size,0, &scu_addr, &fromLen);
				}
				::sendto(host_sock,temp,nn,0,(sockaddr*)&scu_addr,sizeof(SOCKADDR_IN));
				//

				delete []pOriginBuff;
			}
		}
	}
	delete []pRev_Buf;
	if(0 != closesocket(host_sock))
	{
		nErrorCode = WSAGetLastError();
		return false;
	}
	return true;
};

在实际的应用中,如果发送的频率太高,很容易丢包。 采用了Sleep的方式,但这种方式不是解决问题的好方法,但确实是一个有效的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值