您的位置:首页 > 其它

重温WIN32 API ------ 一个简单的UDP服务器类

2014-12-22 18:07 260 查看
最近一个项目需要使用简单的UDP进行通信,为方便调用,使用C++类封装了一个简单的UDP服务器类。

1基本思路

网络通信程序设计中最难的部分就是IO的处理,不同操作系统平台提供不同的IO处理机制,Windows平台有select模型、完成端口等,Linux平台则是poll和epoll。由于本项目要求简单,通信量也不大,所以没有采用这些与平台相关的IO模型,而是采用简单的专用线程来负责侦听。当收到数据包时,自动调用用户指定的回调函数,算是设计模式中”订阅模式“的简单实现,也是来自于模仿C#中的event机制。

多线程程序必须要考虑同步的问题。主线程通过线程安全地设置一个变量来通知UDP侦听线程退出,为防止侦听线程中的recvfrom()一直阻塞而无法退出线程,主线程采用closesocket()来强制侦听线程的recvfrom()返回。(其他让recvfrom退出的方法包括发送专用udp数据包)

另外类使用者需要注意的是,回调函数是在侦听线程中执行,所以要避免非UI线程直接更新UI的问题。回调函数如果涉及到窗口UI操作,需要处理数据后通过SendMessage()的方式通知UI线程,然后由UI线程来实际执行UI更新操作。

2代码实现

就一个UDPServer类,头文件UDPServer.h,实现文件UDPServer.cpp。

#pragmaonce

#include<WinSock2.h>
#include<Ws2tcpip.h>
#include<iphlpapi.h>

#pragmacomment(lib,"IPHLPAPI.lib")
#pragmacomment(lib,"WS2_32")

#include<vector>
usingnamespacestd;

typedefvoid(*UDPRecvFun)(void*sender,BYTE*,int);//收到UDP数据包后的回调函数原型

/*
UDP服务器类
功能描述:
建立UDP侦听,建立一个专用线程负责接收UDP数据包。该类采用类似于C#的事件驱动设计模式。

使用样例:
	voidAfterRecv(void*sender,BYTE*data,intlen)//回调函数实现
	{
	......
	}

	UDPServerpServer=newUDPServer("127.0.0.1",8888);
	pServer.AddCallback(AfterRecv);//增加一个回调,收到UDP包后会自动调用此函数

	pServer->StartUDPServer();//开始侦听

	pServer->StopUDPServer();//可选,因为析构函数会自动调用此方法

	deletepServer;

*/
classUDPServer
{
public:
	UDPServer(unsignedintip,unsignedshortport);
	UDPServer(char*ip,unsignedshortport);
	~UDPServer();

protected:
	staticDWORDWINAPIUDPServerThreadFunc(void*state);
	voidStartUDPServer(unsignedintip,unsignedshortport);
	voidStartUDPServer(char*ip,unsignedshortport);
	voidOnRecv(BYTE*,int);
private:
	unsignedlongIP;
	unsignedshortport;
	SOCKETsock;//socket
	HANDLEtid;//侦听线程ID
	CRITICAL_SECTIONcs;//线程同步用
	intisEnd;//是否终止侦听
	vector<UDPRecvFun>listOnRecv;//收到UDP包后的回调函数列表
public:
	SOCKETGetSocket(){returnthis->sock;}
protected:
	intGetIsEnd();
	voidSetIsEnd(intn);
public:
	voidStartUDPServer();
	voidStopUDPServer();
	voidAddCallback(UDPRecvFuncb);//增加一个回调函数
};

#include"stdafx.h"
#include"UDPServer.h"
#include"LogWriter.h"

UDPServer::UDPServer(unsignedintip,unsignedshortport)
{
	this->sock=NULL;
	this->tid=NULL;
	this->listOnRecv.clear();
	this->IP=ip;
	this->port=port;
	::InitializeCriticalSection(&this->cs);
	isEnd=0;
}

UDPServer::UDPServer(char*ip,unsignedshortport)
{
	this->sock=NULL;
	this->tid=NULL;
	this->listOnRecv.clear();
	this->IP=::inet_addr(ip);
	this->port=port;
	::InitializeCriticalSection(&this->cs);
	isEnd=0;
}
UDPServer::~UDPServer()
{
	if(this->tid!=NULL)
	{
		StopUDPServer();
	}

	::DeleteCriticalSection(&this->cs);
}

/*
功能:线程安全地设置isEnd
*/
voidUDPServer::SetIsEnd(intn)
{
	::EnterCriticalSection(&this->cs);
	this->isEnd=n;
	::LeaveCriticalSection(&this->cs);
}

/*
功能:线程安全地读取isEnd
*/
intUDPServer::GetIsEnd()
{
	::EnterCriticalSection(&this->cs);
	intr=this->isEnd;
	::LeaveCriticalSection(&this->cs);
	returnr;
}
/*
功能:停止UDP服务器(线程)
*/
voidUDPServer::StopUDPServer()
{
	if(this->tid!=NULL)
	{
		this->SetIsEnd(1);		//设置停止标记
		::closesocket(this->sock);//唤醒recvfrom()//::shutdown(this->sock,SD_BOTH);不好用
		this->sock=NULL;

		::WaitForSingleObject(this->tid,INFINITE);//等待UDP专用线程结束
		this->tid=NULL;
	}
}

/*
功能:调用回调列表里的所有函数
*/
voidUDPServer::OnRecv(BYTE*data,intlen)
{
	vector<UDPRecvFun>::iteratorit;
	for(it=this->listOnRecv.begin();it!=this->listOnRecv.end();it++)
	{
		if(*it!=NULL)
		{
			(*it)(this,data,len);
		}
	}
}

/*
功能:启动UDP侦听服务器
参数:ip-侦听IP字符串;port-侦听端口
*/
voidUDPServer::StartUDPServer(char*ip,unsignedshortport)
{
	unsignedlongnIP=::inet_addr(ip);
	if(nIP==INADDR_NONE)
	{
		LOG(L"IP地址%s错误");
		return;
	}
	this->StartUDPServer(nIP,port);
}

/*
线程函数传参结构,用于安全地向子线程传参
*/
classUDPThreadState
{
public:
	UDPServer*obj;
	SOCKETscok;
};

/*
功能:开启UDP侦听线程,本函数不阻塞
参数:ip-侦听IP,port-侦听端口
*/
voidUDPServer::StartUDPServer(unsignedintip,unsignedshortport)
{
	if(this->sock!=NULL)return;

	this->sock=::socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if(sock==INVALID_SOCKET)
	{
		this->sock=NULL;
		LOG(L"UDP服务器创建socket失败");
		return;
	}
	sockaddr_insin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(port);
	sin.sin_addr.S_un.S_addr=ip;

	if(::bind(sock,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
	{
		LOG(L"bind()failed");
		return;
	}

	wchar_tlog[1024];
	swprintf_s(log,L"UDP服务器绑定到了%S:%d",
		::inet_ntoa(sin.sin_addr),::ntohs(sin.sin_port));//注意%S与%s
	LOG(log);

	UDPThreadState*state=newUDPThreadState();
	state->obj=this;
	state->scok=sock;
	this->tid=::CreateThread(NULL,0,UDPServerThreadFunc,(void*)state,NULL,NULL);
	if(tid==NULL)
	{
		deletestate;
	}
}
/*
功能:UDP侦听线程函数,静态函数
参数:符合ThreadProc规范要求
*/
DWORDUDPServer::UDPServerThreadFunc(void*state)
{
	//解析线程函数参数
	UDPThreadState*pstate=(UDPThreadState*)state;
	UDPServer*obj=pstate->obj;
	SOCKETsockServer=pstate->scok;
	deletepstate;pstate=NULL;

	wchar_tlog[100];
	swprintf_s(log,L"UDP侦听线程%d已经启动",::GetCurrentThreadId());
	LOG(log);

	charbuff[1024];
	sockaddr_inremoteAddr;
	intnLen=sizeof(remoteAddr);
	while(1)
	{
		if(1==obj->GetIsEnd())
		{
			break;
		}
		intn=::recvfrom(sockServer,buff,1024,0,(sockaddr*)&remoteAddr,&nLen);
		if(n==SOCKET_ERROR)
		{
			wchar_tlog[128];
			swprintf_s(log,L"recvfrom返回错误号:%d",::WSAGetLastError());
			LOG(log);
		}
		elseif(n==0)
		{
			LOG(L"socket关闭,recvfrom()退出");
		}
		else
		{
			wchar_tlog[128];
			swprintf_s(log,L"接收到UDP包,大小%d字节,来自%S:%d",
				n,::inet_ntoa(remoteAddr.sin_addr),::ntohs(remoteAddr.sin_port));//注意%S与%s
			LOG(log);
			obj->OnRecv((BYTE*)buff,n);
		}
	}

	swprintf_s(log,L"UDP侦听线程%d退出",::GetCurrentThreadId());
	LOG(log);
	return0;
}
/*
功能:启动侦听
*/
voidUDPServer::StartUDPServer()
{
	this->StartUDPServer(this->IP,this->port);
}

/*
功能:向回调函数链表中增加一个回调函数
参数:cb-用户定义的回调函数名
*/
voidUDPServer::AddCallback(UDPRecvFuncb)
{
	this->listOnRecv.push_back(cb);
}

测试代码:
Radar::Radar()
{
	//建立服务器1
	this->pUDPServer=newUDPServer(”172.16.35.144",8888);
	this->pUDPServer->AddCallback(Radar::AfterRecvUDP);

	//建立服务器2
	pUDPServer2=newUDPServer("172.16.35.144",9999);
	pUDPServer2->AddCallback(Radar::AfterRecvUDP);
}
//回调函数
voidRadar::AfterRecvUDP(void*sender,BYTE*data,intlen)
{
data[len]=0;
::MessageBoxA(::AfxGetApp()->GetMainWnd()->m_hWnd,(char*)data,"C",MB_OK);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: