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

boost庫之udp client例項

//UdpLinkClient.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);//設定工作引數

class UdpLinkClient
{
public:
	UdpLinkClient(char *pUrl,char *pServiceName,unsigned short usPort);
	virtual ~UdpLinkClient(void);

	//

	//-------------------------------------------------------------------------------------
	// 功能:傳送資料處理函式
	// 引數:nReserved1,nReserved2:保留
	// 返回:NULL:建立失敗		其他:物件地址
	//-------------------------------------------------------------------------------------
	typedef boost::function<void* (const boost::system::error_code& error,std::size_t bytes_transferred,DWORD dwUserData1,DWORD dwUserData2)>	SendDataCallbackHandler;

	//開始
	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);

	//啟用接收資料服務(自動)
	int AutoRecvData();

	//啟用接收資料服務(人工)
	int MmanualRecvData();

	//獲取伺服器端點
	udp::endpoint & GetServerEndPoint();

	//當收到對方端資料時,就進入本函式響應處理
	void handleRecvData(const boost::system::error_code& error,std::size_t bytes_transferred);
	//當傳送資料給對方端成功之後響應處理
	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();

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

private:
	udp::socket	*m_sockUdp;										//伺服器的SOCKET
	udp::endpoint m_endpointRemote;								//收到資料時的端點資訊
	udp::endpoint m_endpointServer;								//伺服器的端點資訊
	boost::array<char,UDP_DATA_PACKAGE_MAX_LENGTH> m_recvBuf;	//接收資料緩衝區
	bool m_bStop;												//停止服務
	char m_szUrl[256];											//伺服器url
	char m_szServiceName[32];									//服務名,比如daytime,http
	unsigned short m_usPort;									//埠
	bool m_bReviceData;											//是否接收過資料	
	bool m_bReviceServerUse;									//是否啟用接收服務
};

//UdpLinkClient.cpp

#include "StdAfx.h"
#include "UdpLinkClient.h"
#include <boost/exception/all.hpp>

UdpLinkClient::UdpLinkClient(char *pUrl,char *pServiceName,unsigned short usPort)
{
	m_bStop = false;
	m_usPort = usPort;
	m_sockUdp = NULL;
	memset(m_szUrl,0,sizeof(m_szUrl));
	memset(m_szServiceName,0,sizeof(m_szServiceName));
	if(pUrl != NULL)
	{
		strcpy(m_szUrl,pUrl);
	}
	if(pServiceName != NULL)
	{
		strcpy(m_szServiceName,pServiceName);
	}
	m_bReviceData = false;
	m_bReviceServerUse = false;
}

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

//開始
int UdpLinkClient::Start(boost::asio::io_service& ioService)
{
	try
	{
		if(strlen(m_szServiceName) > 0)
		{
			//查詢連線的IP域名和埠
			udp::resolver resolver(ioService);
			//udp::resolver::query query("www.google.com","http");
			udp::resolver::query query(m_szUrl,m_szServiceName);
			m_endpointServer = *resolver.resolve(query);
		}
		else
		{
			m_endpointServer = udp::endpoint(boost::asio::ip::address_v4::from_string(m_szUrl), m_usPort);
		}
		m_sockUdp = new udp::socket(ioService);
		m_sockUdp->open(udp::v4());
 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)
	{
		//diagnostic_information(e);
		return -1;
	}

    m_bStop = false;
	m_bReviceData = false;
	m_bReviceServerUse = false;
	return 0;
}

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

	return 0;
}

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

//獲取伺服器端點
udp::endpoint & UdpLinkClient::GetServerEndPoint()
{
	return m_endpointServer;
}

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

//傳送資料
int UdpLinkClient::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(&UdpLinkClient::handleSendDataInner,this,pfunc,dwUserData1,dwUserData2,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));

	return 0;
}

//傳送資料
int UdpLinkClient::SendData(char *pBuffer,int nBufferSize,bool bAsync)
{
	if(!m_bReviceData)
	{
		if(bAsync)
		{
			//非同步傳送
			m_sockUdp->async_send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointServer,boost::bind(&UdpLinkClient::handleSendData,this,pBuffer,nBufferSize,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
		}
		else
		{
			//同步傳送
			std::size_t nSize = m_sockUdp->send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointServer);
			if(nSize > 0)
			{
				AutoRecvData();
			}
		}
	}
	else
	{
		//更新了對方端點資訊
		if(bAsync)
		{
			//非同步傳送
			m_sockUdp->async_send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointRemote,boost::bind(&UdpLinkClient::handleSendData,this,pBuffer,nBufferSize,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
		}
		else
		{
			//同步傳送
			std::size_t nSize = m_sockUdp->send_to(boost::asio::buffer(pBuffer,nBufferSize),m_endpointRemote);
			if(nSize > 0)
			{
				AutoRecvData();
			}
		}
	}

	return 0;
}

//啟用接收資料服務(自動)
int UdpLinkClient::AutoRecvData()
{
	if(!m_bReviceServerUse)
	{
		RecvDataProcess();
		m_bReviceServerUse = true;
	}

	return 0;
}

//啟用接收資料服務(人工)
int UdpLinkClient::MmanualRecvData()
{
	RecvDataProcess();

	return 0;
}

//接收資料
void UdpLinkClient::RecvDataProcess()
{
	//非同步接收資料
	m_sockUdp->async_receive_from(boost::asio::buffer(m_recvBuf),m_endpointRemote,boost::bind(&UdpLinkClient::handleRecvData,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}

//當收到客戶端資料時,就進入本函式響應處理  
void UdpLinkClient::handleRecvData(const boost::system::error_code& error,std::size_t bytes_transferred)
{
	if(IsStop())
		return;

	//如果沒有出錯
	if(!error || error==boost::asio::error::message_size)
	{
		if(!m_bReviceData)
		{
			m_bReviceData = true;
		}

		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"));
		std::string strMessage = "bbbbbbbbb";
		//SendData((char *)strMessage.data(),strMessage.size());
		SendDataEx(m_endpointRemote,(char *)strMessage.data(),strMessage.size(),SendDataCallbackOuter,(int)this,0);

		//下一次接收
		RecvDataProcess();
	}
}

//當傳送資料給客戶端成功之後響應。  
void UdpLinkClient::handleSendData(char *pBuffer,int nBufferSize,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	if(error != NULL)
	{
		//列印錯誤資訊
		printf("%s", boost::system::system_error(error).what());
		return;
	}
	AutoRecvData();

	int n = 0;
}
void UdpLinkClient::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());
		return;
	}
	if(pfunc != NULL)
	{
		pfunc(error,bytes_transferred,dwUserData1,dwUserData2);
	}
	int n = 0;
}
/*
void UdpLinkClient::handleSendData(boost::shared_ptr<std::string> strMessage,const boost::system::error_code& error,std::size_t bytes_transferred)
{
	 int n = 0;
}
*/
//呼叫例項程式碼
// UdpClient.cpp : 定義控制檯應用程式的入口點。
//

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

//#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;
	UdpLinkClient usUdpClient("127.0.0.1",NULL,7001);
	int nRet = usUdpClient.Start(ioService);
	if(nRet != 0)
	{
		return 0;
	}

	//傳送資料
	nRet = usUdpClient.SendData("abcdedfg",8,true);
	
#ifdef THREAD_RUN
	//執行緒poll等待
	boost::thread thrd(WorkThreadFunByDeviceServiceProcess,&ioService);
	thrd.join();
	g_WorkThreadExit = true;
#else
	//阻塞等待,否則就直接退出了程式,io_service無法訊息迴圈處理
	//io_service run也可以不呼叫,但該程序不能直接退出,需要阻塞。
	ioService.run();
#endif

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