1. 程式人生 > >boost庫之udp廣播例項

boost庫之udp廣播例項

//UdpLinkServer.h

//udp服務

#pragma once

#include <boost/asio/ip/tcp.hpp>
#include <boost/asio.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/array.hpp>

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<void* (const boost::system::error_code& error,std::size_t bytes_transferred,DWORD dwUserData1,DWORD dwUserData2)>	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<std::string> 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<char,UDP_DATA_PACKAGE_MAX_LENGTH> 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 <boost/exception/all.hpp>

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));
		}
	
	}
	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<std::string> 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<std::string> strMessage,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	 int n = 0;
}
*/
//廣播發送程式碼
// UdpbroastSender.cpp : 定義控制檯應用程式的入口點。
//Udpbroast Sender

#include "stdafx.h"
#include "UdpLinkServer.h"
#include <boost/thread/thread.hpp>


//裝置資訊
typedef struct tagDeviceInfo{
	unsigned short	usFunction;					//功能碼
	unsigned short	usVersionFlag;				//版本標記
	unsigned int	uiCompanyId;				//企業id
	char			szDeviceSerialNo[24];		//裝置序列號
	unsigned short	usServicePort;				//資料服務埠
	char			szExtend[38];				//擴充套件
}DeviceInfo;

//工作執行緒引數
typedef struct tagWorkThreadParameter{
	boost::asio::io_service * pIoService;
	UdpLinkServer * pUdpService;
}WorkThreadParameter;

//工作執行緒函式處理函式
bool g_WorkThreadExit = false;
int  g_nBroastDataSendInteral = 5000;					//廣播發送間隔,單位:毫秒
DeviceInfo g_diDeviceInfo = {0};						//裝置資訊
unsigned int __stdcall WorkThreadFunByDeviceServiceProcess(PVOID pParam)
{
	int nn = 0;
	int nDataSize = sizeof(DeviceInfo);
	WorkThreadParameter *pAllParameter = (WorkThreadParameter *)pParam;
	while(true)
	{
		if(g_WorkThreadExit)
		{
			break;
		}

		//傳送資料
		pAllParameter->pUdpService->SendData((char *)&g_diDeviceInfo,nDataSize,true);

		pAllParameter->pIoService->poll();
		for(nn=g_nBroastDataSendInteral;nn>0; nn-=200)
		{
			if(g_WorkThreadExit)
			{
				break;
			}
			Sleep(200);
		}
	}
	return 0;
}

int main(int argc, char* argv[])
{
	boost::asio::io_service ioService;
	UdpLinkServer usUdpService(9090,true);//true,為廣播模式;false,非廣播模式
	usUdpService.Start(ioService);
	g_diDeviceInfo.usFunction = 1;
	g_diDeviceInfo.usVersionFlag = 0x0001;
	strcpy(g_diDeviceInfo.szDeviceSerialNo,"ABCDEFG111111111");
	g_diDeviceInfo.usServicePort = 9001;
	
	WorkThreadParameter wtpWorkThreadParameter;
	wtpWorkThreadParameter.pIoService = &ioService;
	wtpWorkThreadParameter.pUdpService = &usUdpService;
	//開啟執行緒
	boost::thread thrd(WorkThreadFunByDeviceServiceProcess,&wtpWorkThreadParameter);
	//執行緒poll等待
	thrd.join();
	g_WorkThreadExit = true;

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

//廣播接收程式碼
// UdpbroastRevicer.cpp : 定義控制檯應用程式的入口點。
//Udpbroast Revicer

#include "stdafx.h"
#include "UdpLinkServer.h"
#include <boost/thread/thread.hpp>


//裝置資訊
typedef struct tagDeviceInfo{
	unsigned short	usFunction;					//功能碼
	unsigned short	usVersionFlag;				//版本標記
	unsigned int	uiCompanyId;				//企業id
	char			szDeviceSerialNo[24];		//裝置序列號
	unsigned short	usServicePort;				//資料服務埠
	char			szExtend[38];				//擴充套件
}DeviceInfo;

//工作執行緒函式處理函式
bool g_WorkThreadExit = false;
int  g_nBroastDataSendInteral = 5000;					//廣播發送間隔,單位:毫秒
DeviceInfo g_diDeviceInfo = {0};						//裝置資訊
unsigned int __stdcall WorkThreadFunByDeviceServiceProcess(PVOID pParam)
{
	int nn = 0;
	boost::asio::io_service * pIoService  = (boost::asio::io_service *)pParam;
	while(true)
	{
		if(g_WorkThreadExit)
		{
			break;
		}

		pIoService->poll();
		for(nn=g_nBroastDataSendInteral;nn>0; nn-=200)
		{
			if(g_WorkThreadExit)
			{
				break;
			}
			Sleep(200);
		}
	}
	return 0;
}

static void WINAPI BroastDeviceInfoRecvDataCallback(const boost::system::error_code& error,char *pData,int nDataLength,char *pPeerIp,unsigned short usPeerPort,DWORD dwUserData1,DWORD dwUserData2)
{
	SYSTEMTIME sm;
	GetLocalTime(&sm);
	char szInfo[256] = {0};
	DeviceInfo *pDeviceInfo = (DeviceInfo *)pData;
	sprintf(szInfo,"%s %s:%d time:%04d-%02d-%0d %02d:%02d:%02d\n",pDeviceInfo->szDeviceSerialNo,pPeerIp,usPeerPort,sm.wYear,sm.wMonth,sm.wDay,sm.wHour,sm.wMinute,sm.wSecond);
	printf(szInfo);
}

int main(int argc, char* argv[])
{
	boost::asio::io_service ioService;
	UdpLinkServer usUdpService(9090,false);
	usUdpService.SetRecvDataCallback(true,BroastDeviceInfoRecvDataCallback,0,0);
	usUdpService.Start(ioService);
	
	//開啟執行緒
	boost::thread thrd(WorkThreadFunByDeviceServiceProcess,&ioService);
	//執行緒poll等待
	thrd.join();
	g_WorkThreadExit = true;

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