一个简单的UDP Server实现
作者:武乃辉
一、思路
1、建立接收socket,将socket和指定的端口绑定
2、创建接收线程,在线程中调用ioctlsocket()判断是否接收到数据,接收到数据时调用OnReceive()(类似CSocket中的OnReceive())
3、在OnReceive中申请空间调用recvfrom接收数据
4、建立发送socket,和INADDR_ANY绑定
5、调用sendto发送数据
二、本代码直接使用socket API实现UDP通讯
相关的函数有:
WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);//初始化socket库
WSACleanup();//释放socket库
socket(int af,int type,int protocol); //建立socket
setsockopt(SOCKET s,int level,int optname,const char FAR * optval,int optlen);//设置socket或者协议的属性
bind(SOCKET s,const struct sockaddr FAR * name,int namelen);//地址和socket绑定
sendto(SOCKET s,const char FAR * buf,int len,int flags,const struct sockaddr FAR * to,int tolen);//发送消息
ioctlsocket(SOCKET s,long cmd,u_long FAR * argp);//获取socket状态,可以获取socket缓冲中数据长度,利用这个函数可以实现类似CSocket中的OnReceive()函数(在接收数据之前调用)
recvfrom(SOCKET s,char FAR * buf,int len,int flags,struct sockaddr FAR * from,int FAR * fromlen);//接收数据
三、代码说明
1、UDPServer.h和UDPServer.cpp是实现类CUDPServer的头文件和实现文件,udpdemo.cpp是演示程序
2、编译环境在win2000 vc6.0下编译通过
3、编译时需要在vc的project的setting中连接Ws2_32.lib库
// UDPServer.h: interface for the CUDPServer class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(UDPSERVER_H_INCLUDED_)
#define UDPSERVER_H_INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include // Ws2_32.lib
#define CLOSE_SOCKET(X) if(X!=INVALID_SOCKET){closesocket(X);X=INVALID_SOCKET;}
class CUDPServer
{
private:
char m_szLocalIP[20];
char m_szHostName[30];
int m_RecvPort;
char m_szRemoteIP[20];
int m_RemotePort;
HANDLE m_RecvThread;
DWORD m_ThreadID;
private:
void InitMemberVariable();
public:
SOCKET m_SckRecive;
SOCKET m_SckSend;
bool m_bReciveFlag;
CUDPServer();
virtual ~CUDPServer();
void Initialize();
void Destory();
//初始化socket lib
bool InitSocketLib(WORD wVersion=0x0202);
void CleanSocketLib();
//创建发送socket
bool CreateSendSocket();
//创建接收socket
bool CreateRecvSocket(int RecvPort=9527);
//发送
int SendMsg(char * szBuf,int length,char * szremoteIP=NULL,int port=0);
//启动接收线程
bool StartRecv();
//停止接收线程
bool StopRecv();
//接收线程函数,
static DWORD WINAPI ReceiveThread(LPVOID lpParameter); // 线程函数
//当有数据时,调用OnReceive,length表示数据长度
void OnReceive(long length);
static DWORD WINAPI OnReceiveThread(LPVOID lpParameter); // 线程函数
};
#endif // !defined(UDPSERVER_H_INCLUDED_)
// UDPServer.cpp: implementation of the CUDPServer class.
//
//////////////////////////////////////////////////////////////////////
#include "UDPServer.h"
#include
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//********************************************************************
CUDPServer::CUDPServer()
{
Initialize();
}
//********************************************************************
CUDPServer::~CUDPServer()
{
Destory();
}
//********************************************************************
void CUDPServer::Initialize()
{
InitSocketLib(0x0202);
InitMemberVariable();
}
//********************************************************************
void CUDPServer::Destory()
{
if(m_bReciveFlag)
{
StopRecv();
}
CleanSocketLib();
}
//********************************************************************
void CUDPServer::InitMemberVariable()
{
memset(m_szLocalIP,0,20);
strcpy(m_szLocalIP,"127.0.0.1");
memset(m_szHostName,0,30);
m_RecvPort = 9527;
m_SckRecive = INVALID_SOCKET;
m_SckSend = INVALID_SOCKET;
memset(m_szRemoteIP,0,20);
strcpy(m_szRemoteIP,"127.0.0.1");
m_RemotePort = 9527;
m_RecvThread = NULL;
m_ThreadID = 0;
m_bReciveFlag = false;
}
//********************************************************************
bool CUDPServer::InitSocketLib(WORD wVersion)
{
WSADATA wsd;
int ret = WSAStartup(wVersion,&wsd);
if(ret!=0)
{
WSACleanup();
return false;
}
return true;
}
//********************************************************************
void CUDPServer::CleanSocketLib()
{
WSACleanup();
}
//********************************************************************
//创建发送socket
bool CUDPServer::CreateSendSocket()
{
int ret;
bool flag;
SOCKADDR_IN addr;
CLOSE_SOCKET(m_SckSend);
m_SckSend = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
if(m_SckSend==INVALID_SOCKET)
{
return false;
}
flag = true;
//设置允许地址复用
ret = setsockopt(m_SckSend,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag));
if(ret!=0)
{
CLOSE_SOCKET(m_SckSend);
return false;
}
//绑定
ZeroMemory(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
//addr.sin_port = htons(m_RecvPort);
//addr.sin_port = m_RecvPort;
ret = bind(m_SckSend,(struct sockaddr *)&addr,sizeof(addr));
if(ret!=0)
{
CLOSE_SOCKET(m_SckSend);
return false;
}
return true;
}
//********************************************************************
//创建接收socket
bool CUDPServer::CreateRecvSocket(int RecvPort)
{
int ret;
bool flag;
SOCKADDR_IN addr;
CLOSE_SOCKET(m_SckRecive);
m_RecvPort = RecvPort;
//创建socket
m_SckRecive = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
if(m_SckRecive==INVALID_SOCKET)
{
return false;
}
flag = true;
//设置允许地址复用
ret = setsockopt(m_SckRecive,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag));
if(ret!=0)
{
CLOSE_SOCKET(m_SckRecive);
return false;
}
/*const int routenum = 10;
ret = setsockopt(m_SckMultiCastSend,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&routenum,sizeof(routenum));
if(ret!=0)
{
CLOSE_SOCKET(m_SckMultiCastSend);
return false;
}
const int loopback = 1; //禁止回馈
ret = setsockopt(m_SckMultiCastSend,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)&loopback,sizeof(loopback));
if(ret!=0)
{
CLOSE_SOCKET(m_SckMultiCastSend);
return false;
}
flag=true;
//设置该套接字为广播类型,
setsockopt(m_SckMultiCastSend,SOL_SOCKET,SO_BROADCAST,(char FAR *)&flag,sizeof(flag));*/
//绑定
ZeroMemory(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(m_RecvPort);
//addr.sin_port = m_MultiCastPort;
ret = bind(m_SckRecive,(struct sockaddr *)&addr,sizeof(addr));
if(ret!=0)
{
CLOSE_SOCKET(m_SckRecive);
return false;
}
return true;
}
//********************************************************************
//发送
int CUDPServer::SendMsg(char * szBuf,int length,char * szremoteIP,int port)
{
int ret;
SOCKADDR_IN addr;
if(m_SckSend==INVALID_SOCKET)
{
return -1;
}
if(szremoteIP!=NULL)
{
strcpy(m_szRemoteIP,szremoteIP);
m_RemotePort = port;
}
ZeroMemory(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(m_szRemoteIP);
addr.sin_port = htons(m_RemotePort);
ret = sendto(m_SckSend,szBuf,length,0,(sockaddr *)&addr,sizeof(addr));
if(ret == SOCKET_ERROR)
{
return -1;
}
return ret;
}
//********************************************************************
//启动接收线程
bool CUDPServer::StartRecv()
{
if(m_SckRecive==INVALID_SOCKET)
{
if(!CreateRecvSocket())
{
return false;
}
}
if(m_bReciveFlag)
{
return true;
}
//m_RecvThread = CreateThread(NULL,0,ReceiveThread,this,0,&m_ThreadID);
m_RecvThread = CreateThread(NULL,0,OnReceiveThread,this,0,&m_ThreadID);
if(m_RecvThread==NULL)
{
return false;
}
return true;
}
//********************************************************************
//停止接收线程
bool CUDPServer::StopRecv()
{
if(!m_bReciveFlag)
{
return true;
}
CLOSE_SOCKET(m_SckRecive);
if(m_RecvThread!=NULL)
{
WaitForSingleObject(m_RecvThread,INFINITE);
m_RecvThread = NULL;
m_ThreadID = 0;
}
return true;
}
//********************************************************************
//接收线程函数,
DWORD WINAPI CUDPServer::ReceiveThread(LPVOID lpParameter)
{
int addr_len;
struct sockaddr_in addr;
char szRecBuf[10000];
int iRecLen;
CUDPServer * pUDPServer;
pUDPServer = (CUDPServer *)lpParameter;
pUDPServer->m_bReciveFlag = true;
printf("Receive Thread Started.\n");
/*addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(pNetMultiCast->m_MultiCastPort);*/
while(pUDPServer->m_bReciveFlag)
{
addr_len = sizeof(addr);
iRecLen = recvfrom(pUDPServer->m_SckRecive,szRecBuf,10000,0,(struct sockaddr *)&addr,&addr_len);
if(iRecLen==SOCKET_ERROR||iRecLen {
pUDPServer->m_bReciveFlag = false;
}
else
{
szRecBuf[iRecLen] = 0;
printf("Receive from %s :%s\n",inet_ntoa(addr.sin_addr),szRecBuf);
}
}
printf("Receive Thread Ended.\n");
return 0;
}
//********************************************************************
void CUDPServer::OnReceive(long length)
{
//length 是socket列队中的第一个报文长度
//在本函数中可以进行业务的处理
char *pbuf;
int addr_len,iRecLen;;
struct sockaddr_in addr;
pbuf = new char[length+1];
memset(pbuf,0,length+1);
addr_len = sizeof(addr);
iRecLen = recvfrom(m_SckRecive,pbuf,length,0,(struct sockaddr *)&addr,&addr_len);
if(iRecLen==SOCKET_ERROR||iRecLen {
printf("Receive Failed!");
}
else
{
pbuf[length] = 0;
printf("Receive from %s :%s\n",inet_ntoa(addr.sin_addr),pbuf);
}
delete [] pbuf;
pbuf = NULL;
}
//********************************************************************
DWORD WINAPI CUDPServer::OnReceiveThread(LPVOID lpParameter)
{
int ret;
unsigned long lLength;
CUDPServer * pUDPServer;
pUDPServer = (CUDPServer *)lpParameter;
pUDPServer->m_bReciveFlag = true;
printf("Receive Thread Started.\n");
while(pUDPServer->m_bReciveFlag)
{
ret = ioctlsocket(pUDPServer->m_SckRecive,FIONREAD,&lLength);
if(ret!=0)
{
pUDPServer->m_bReciveFlag = false;
}
else
{
if(lLength>0)
{
pUDPServer->OnReceive(lLength);
}
}
}
printf("Receive Thread Ended.\n");
return 0;
}
//udpdemo.cpp
#include
#include "UDPServer.h"
using namespace std;
int main(int argc,char *argv)
{
char szBuf[100];
CUDPServer pUDPServer;
cout pUDPServer.CreateRecvSocket(9000);
pUDPServer.CreateSendSocket();
pUDPServer.StartRecv();
while(1)
{
memset(szBuf,0,100);
cout cin>>szBuf;
if(strcmp(szBuf,"quit")==0)
{
pUDPServer.StopRecv();
break;
}
pUDPServer.SendMsg(szBuf,strlen(szBuf),"127.0.0.1",9000);
}
return 0;
}
四、结束语
欢迎对代码不足之处提出宝贵意见。wnh5@tom.com