boost库之udp server实例

//UdpLinkServer.h

 

//udp服务

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using boost::asio::ip::udp;

#define  UDP_DATA_PACKAGE_MAX_LENGTH		1024

//发送数据回调函数
typedef void (CALLBACK *SendDataCallback)(const boost::system::error_code& error,std::size_t bytes_transferred,DWORD dwUserData1,DWORD dwUserData2);
//接收数据回调函数
typedef void (CALLBACK *RecvDataCallback)(const boost::system::error_code& error,char *pData,int nDataLength,char *pPeerIp,unsigned short usPeerPort,DWORD dwUserData1,DWORD dwUserData2);

class UdpLinkServer
{
public:
	UdpLinkServer(unsigned short usPort,bool bBroadcast);
	virtual ~UdpLinkServer(void);

	//发送数据回调函数 boost function定义
	typedef boost::function	SendDataCallbackHandler;

	/*
	功能:设置接收数据回调函数
	参数:bAutoRecvData:是否自动接收数据;pfunc:接收数据回调函数;dwUserData1:接收数据回调函数用户数据1;dwUserData2:接收数据回调函数用户数据2
	返回值:无
	*/
	void SetRecvDataCallback(bool bAutoRecvData,RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2);

	//开始
	int Start(boost::asio::io_service& ioService);

	//停止
	int Stop();

	//发送数据
	int SendDataEx(udp::endpoint endpointRemote,char *pBuffer,int nBufferSize,SendDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2);

	//发送数据
	int SendData(char *pBuffer,int nBufferSize,bool bAsync);

	//获取广播端点
	udp::endpoint & GetBroadcastEndPoint();

	//接收数据处理(手动)
	void handleRecvDataByManual(RecvDataCallback pfunc=NULL,DWORD dwUserData1=0,DWORD dwUserData2=0);
	//当发送数据给客户端成功之后响应处理
	void handleSendData(char *pBuffer,int nBufferSize,const boost::system::error_code& error,std::size_t bytes_transferred);
	void handleSendDataInner(SendDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2,const boost::system::error_code& error,std::size_t bytes_transferred);
	//void handleSendData(boost::shared_ptr strMessage,const boost::system::error_code& error,std::size_t bytes_transferred);

	static void WINAPI SendDataCallbackOuter(const boost::system::error_code& error,std::size_t bytes_transferred,DWORD dwUserData1,DWORD dwUserData2);


protected:
	//接收数据
	void RecvDataProcess(RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2);
	//接收数据处理(自动)
	void handleRecvData(const boost::system::error_code& error,std::size_t bytes_transferred,RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2);

	//是否停止服务
	bool IsStop();

private:
	udp::socket	*m_sockUdp;										//服务器的SOCKET
	udp::endpoint m_endpointRemote;								//收到数据时的端点信息
	udp::endpoint m_endpointBroadcast;							//广播端点信息
	boost::array m_recvBuf;	//接收数据缓冲区
	bool m_bStop;												//停止服务
	bool  m_bBroadcast;											//是否广播
	unsigned short m_usPort;									//端口
	bool m_bAutoRecvData;										//是否自动接收数据,true,表示自动接收数据;false,表示外部手动调用函数接收数据
	RecvDataCallback											m_pfuncRecvDataCallback;		//接收数据回调函数
	DWORD														m_dwRecvDataCallbackUserData1;	//接收数据回调函数用户数据1
	DWORD														m_dwRecvDataCallbackUserData2;  //接收数据回调函数用户数据2
};

 

 

 

 

 

 

//UdpLinkServer.cpp

 

#include "StdAfx.h"
#include "UdpLinkServer.h"
#include 

UdpLinkServer::UdpLinkServer(unsigned short usPort,bool bBroadcast)
{
	m_bStop = false;
	m_bBroadcast = bBroadcast;
	m_usPort = usPort;
	m_sockUdp = NULL;
	m_bAutoRecvData = true;
	m_pfuncRecvDataCallback = NULL;
	m_dwRecvDataCallbackUserData1 = 0;
	m_dwRecvDataCallbackUserData2 = 0;
}

UdpLinkServer::~UdpLinkServer(void)
{
	if(m_sockUdp != NULL)
	{
		m_sockUdp->close();
		delete m_sockUdp;
		m_sockUdp = NULL;
	}
}

//设置接收数据回调函数
void UdpLinkServer::SetRecvDataCallback(bool bAutoRecvData,RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2)
{
	m_bAutoRecvData = bAutoRecvData;
	m_pfuncRecvDataCallback = pfunc;
	m_dwRecvDataCallbackUserData1 = dwUserData1;
	m_dwRecvDataCallbackUserData2 = dwUserData2;
}

//开始
int UdpLinkServer::Start(boost::asio::io_service& ioService)
{
	try
	{
		if(m_bBroadcast)
		{
			//广播
			m_sockUdp = new udp::socket(ioService,udp::endpoint(udp::v4(),0));
			m_sockUdp->set_option(boost::asio::socket_base::reuse_address(true));
			m_sockUdp->set_option(boost::asio::socket_base::broadcast(true));
			m_endpointBroadcast = udp::endpoint(boost::asio::ip::address_v4::broadcast(), m_usPort);
		}
		else
		{
			m_sockUdp = new udp::socket(ioService,udp::endpoint(udp::v4(),m_usPort));
			if(!m_sockUdp->is_open())
			{
				//端口被占用
				return -1;
			}
			m_sockUdp->set_option(boost::asio::socket_base::reuse_address(true));
 //使用WSAIoctl设置UDP socket的工作模式,让其忽略这个错误(Windows UDP socket recvfrom返回10054错误的解决办法)
 BOOL bNewBehavior = FALSE;
 DWORD dwBytesReturned = 0;
 WSAIoctl(m_sockUdp->native(), SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
		}
	
	}
	catch (boost::exception& e)
	{
		return -1;
	}

    m_bStop = false;
	if(m_bAutoRecvData)
	{
		RecvDataProcess(m_pfuncRecvDataCallback,m_dwRecvDataCallbackUserData1,m_dwRecvDataCallbackUserData2);
	}
	return 0;
}

//停止
int UdpLinkServer::Stop()
{
	m_bStop = true;

	return 0;
}

//是否停止服务
bool UdpLinkServer::IsStop()
{
	return m_bStop;
}

//获取广播端点
udp::endpoint & UdpLinkServer::GetBroadcastEndPoint()
{
	return m_endpointBroadcast;
}

void UdpLinkServer::SendDataCallbackOuter(const boost::system::error_code& error,std::size_t bytes_transferred,DWORD dwUserData1,DWORD dwUserData2)
{
	int i = 0;
}

//发送数据
int UdpLinkServer::SendDataEx(udp::endpoint endpointRemote,char *pBuffer,int nBufferSize,SendDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2)
{
	m_sockUdp->async_send_to(boost::asio::buffer(pBuffer,nBufferSize),endpointRemote,boost::bind(&UdpLinkServer::handleSendDataInner,this,pfunc,dwUserData1,dwUserData2,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));

	return 0;
}

//发送数据
int UdpLinkServer::SendData(char *pBuffer,int nBufferSize,bool bAsync)
{
	if(!m_bBroadcast)
	{
		if(bAsync)
		{
			//异步发送
			m_sockUdp->async_send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointRemote,boost::bind(&UdpLinkServer::handleSendData,this,pBuffer,nBufferSize,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
		}
		else
		{
			//同步发送
			m_sockUdp->send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointRemote);
		}
	}
	else
	{
		//广播
		if(bAsync)
		{
			//异步发送
			m_sockUdp->async_send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointBroadcast,boost::bind(&UdpLinkServer::handleSendData,this,pBuffer,nBufferSize,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
		}
		else
		{
			//同步发送
			m_sockUdp->send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointBroadcast);
		}
	}

	return 0;
}

//接收数据
void UdpLinkServer::RecvDataProcess(RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2)
{
	//异步接收数据
	m_sockUdp->async_receive_from(boost::asio::buffer(m_recvBuf),m_endpointRemote,boost::bind(&UdpLinkServer::handleRecvData,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred,pfunc,dwUserData1,dwUserData2));
}

//接收数据处理(手动),就进入本函数响应处理
void UdpLinkServer::handleRecvDataByManual(RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2)
{
	if(IsStop())
		return;

	//下一次接收
	RecvDataProcess(pfunc,dwUserData1,dwUserData2);
}

//当收到客户端数据时,就进入本函数响应处理  
void UdpLinkServer::handleRecvData(const boost::system::error_code& error,std::size_t bytes_transferred,RecvDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2)
{
	if(IsStop())
		return;

	//如果没有出错
	if(!error || error==boost::asio::error::message_size)
	{
		if(bytes_transferred > UDP_DATA_PACKAGE_MAX_LENGTH)
		{
			//超过最大数据长度
			return;
		}

		/*
		//打印接收的内容
		char szTmp[UDP_DATA_PACKAGE_MAX_LENGTH] = {0};
		memcpy(szTmp,m_recvBuf.data(),bytes_transferred);
		printf("%s\n",szTmp);

		//boost::shared_ptr strMessage(new std::string("aaaaaa"));
		//SendData((char *)strMessage.data(),strMessage.size());
		std::string strMessage = "aaaaaaaaaa";
		SendDataEx(m_endpointRemote,(char *)strMessage.data(),strMessage.size(),SendDataCallbackOuter,(int)this,0);
		*/

		//回调数据
		if(pfunc != NULL)
		{
			pfunc(error,m_recvBuf.data(),bytes_transferred,(char *)m_endpointRemote.address().to_string().c_str(),m_endpointRemote.port(),dwUserData1,dwUserData2);
		}

		//下一次接收
		RecvDataProcess(pfunc,dwUserData1,dwUserData2);
	}
	else
	{
		//出错
		if(pfunc != NULL)
		{
			pfunc(error,NULL,bytes_transferred,NULL,0,dwUserData1,dwUserData2);
		}
	}
}

//当发送数据给客户端成功之后响应。  
void UdpLinkServer::handleSendData(char *pBuffer,int nBufferSize,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	int n = 0;
}
void UdpLinkServer::handleSendDataInner(SendDataCallback pfunc,DWORD dwUserData1,DWORD dwUserData2,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	if(error != NULL)
	{
		//打印错误信息
		printf("%s", boost::system::system_error(error).what());
	}
	if(pfunc != NULL)
	{
		pfunc(error,bytes_transferred,dwUserData1,dwUserData2);
	}
	int n = 0;
}
/*
void UdpLinkServer::handleSendData(boost::shared_ptr strMessage,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	 int n = 0;
}
*/

 

 

 

 

//调用实例代码

 

// UdpServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "UdpLinkServer.h"
#include 

//#define  THREAD_RUN		1

//工作线程函数处理函数
bool g_WorkThreadExit = false;
unsigned int __stdcall WorkThreadFunByDeviceServiceProcess(PVOID pParam)
{
	boost::asio::io_service *pIoService = (boost::asio::io_service *)pParam;
	while(true)
	{
		if(g_WorkThreadExit)
		{
			break;
		}

		pIoService->poll();
		Sleep(100);
	}
	return 0;
}

int main(int argc, char* argv[])
{
	boost::asio::io_service ioService;
	UdpLinkServer usUdpService(7001,false);
	usUdpService.Start(ioService);

	/*
	for(int i=0; i<10; i++)
	{
		usUdpService.SendData("1111111111",10,true);
		Sleep(500);
	}
	*/

	
#ifdef THREAD_RUN
	//线程poll等待
	boost::thread thrd(WorkThreadFunByDeviceServiceProcess,&ioService);
	thrd.join();
	g_WorkThreadExit = true;
#else
	//阻塞等待,否则就直接退出了程序,io_service无法消息循环处理
	//io_service run也可以不调用,但该进程不能直接退出,需要阻塞。
	ioService.run();
#endif

	usUdpService.Stop();
	ioService.stop();
	return 0;
}

注意事项:

1、如果遇到编译错误  error C2632: '__int64' followed by '__int64' is illegal
boost int64_t 与 vc中的int64_t冲突,将在文件头中增加#undef int64_t
如下:
#undef int64_t
#include
#include
#include
#include
#include
#include
#include

2、UDP 10054错误的解决方法
现象:在Windows 7系统上,A使用UDP socket,调用sendto函数向一个目标地址B发送数据,但是目标地址B没有接收数据,如果A此时立即调用recvfrom试图接收目标地址B发回的数据的话,recvfrom会立即返回-1,WSAGetLastError()返回10045。

这是Windows socket的一个bug,当UDP Socket在某次发送后收到一个不可到达的ICMP包时,这个错误将在下一个接收中返回,所以上面的套接字在下一次的接收中返回了SOCKET_ERROR,错误是10054。
解决方法:设置WSA的工作模式。
//udp::socket    *m_sockUdp;
BOOL bNewBehavior = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(m_sockUdp->native(), SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);

3、无法释放udp端口的问题
现象:关闭boost的udp socket,但是 udp的端口一直没有释放,这是一个很严重的问题,因为对于服务器来讲,由于一直运行,端口没释放会导致端口越来越少吗,最后没端口用了。
解决方法:以下两种。
//udp::socket    *m_sockUdp;
m_sockUdp->shutdown(boost::asio::ip::udp::socket::shutdown_both);
m_sockUdp->close();
或者
closesocket(m_sockUdp->native());

你可能感兴趣的:(boost)