现在的位置: 首页 > 综合 > 正文

<摘录>详谈高性能UDP服务器的开发

2012年11月04日 ⁄ 综合 ⁄ 共 5534字 ⁄ 字号 评论关闭

上一篇文章我详细介绍了如何开发一款高性能的TCP服务器的网络传输层.本章我将谈谈如何开发一个高性能的UDP服务器的网络层.UDP服务器的网络层开 发相对与TCP服务器来说要容易和简单的多,UDP服务器的大致流程为创建一个socket然后将其绑定到完成端口上并投递一定数量的recv操作.当有 数据到来时从完成队列中取出数据发送到接收队列中即可。
  测试结果如下:
    WindowsXP Professional,Intel Core Duo E4600 双核2.4G , 2G内存。同时30K个用户和该UDP服务器进行交互其CPU使用率为10%左右,内存占用7M左右。
  下面详细介绍该服务器的架构及流程: 
1. 首先介绍服务器的接收和发送缓存UDP_CONTEXT。

 1    class UDP_CONTEXT : protected NET_CONTEXT
 2    {
 3        friend class UdpSer;
 4    protected:
 5        IP_ADDR m_RemoteAddr;            //对端地址
 6
 7        enum
 8        {
 9            HEAP_SIZE = 1024 * 1024 * 5,
10            MAX_IDL_DATA = 10000,
11        }
;
12
13    public:
14        UDP_CONTEXT() {}
15        virtual ~UDP_CONTEXT() {}
16
17        void* operator new(size_t nSize);
18        void operator delete(void* p);
19
20    private:
21        static vector<UDP_CONTEXT* > s_IDLQue;
22        static CRITICAL_SECTION s_IDLQueLock;
23        static HANDLE s_hHeap;    
24    }
;


UDP_CONTEXT的实现流程和TCP_CONTEXT的实现流程大致相同,此处就不进行详细介绍。

2. UDP_RCV_DATA,当服务器收到客户端发来的数据时会将数据以UDP_RCV_DATA的形式放入到数据接收队列中,其声明如下:

 1    class DLLENTRY UDP_RCV_DATA
 2    {
 3        friend class UdpSer;
 4    public:
 5        CHAR* m_pData;                //数据缓冲区
 6        INT m_nLen;                    //数据的长度
 7        IP_ADDR m_PeerAddr;            //发送报文的地址
 8
 9        UDP_RCV_DATA(const CHAR* szBuf, int nLen, const IP_ADDR& PeerAddr);
10        ~UDP_RCV_DATA();
11
12        void* operator new(size_t nSize);
13        void operator delete(void* p);
14
15        enum
16        {
17            RCV_HEAP_SIZE = 1024 * 1024 *50,        //s_Heap堆的大小
18            DATA_HEAP_SIZE = 100 * 1024* 1024,    //s_DataHeap堆的大小
19            MAX_IDL_DATA = 250000,
20        }
;
21
22    private:
23        static vector<UDP_RCV_DATA* > s_IDLQue;
24        static CRITICAL_SECTION s_IDLQueLock;
25        static HANDLE s_DataHeap;        //数据缓冲区的堆
26        static HANDLE s_Heap;            //RCV_DATA的堆
27    }
;


UDP_RCV_DATA的实现和TCP_RCV_DATA大致相同, 此处不在详细介绍.

下面将主要介绍UdpSer类, 该类主要用来管理UDP服务.其定义如下:

 1    class DLLENTRY UdpSer
 2    {
 3    public:
 4        UdpSer();
 5        ~UdpSer();
 6
 7        /************************************************************************
 8        * Desc : 初始化静态资源,在申请UDP实例对象之前应先调用该函数, 否则程序无法正常运行
 9        ************************************************************************/

10        static void InitReource();
11
12        /************************************************************************
13        * Desc : 在释放UDP实例以后, 掉用该函数释放相关静态资源
14        ************************************************************************/

15        static void ReleaseReource();
16
17        //用指定本地地址和端口进行初始化
18        BOOL StartServer(const CHAR* szIp = "0.0.0.0", INT nPort = 0);
19
20        //从数据队列的头部获取一个接收数据, pCount不为null时返回队列的长度
21        UDP_RCV_DATA* GetRcvData(DWORD* pCount);
22
23        //向对端发送数据
24        BOOL SendData(const IP_ADDR& PeerAddr, const CHAR* szData, INT nLen);
25
26        /****************************************************
27        * Name : CloseServer()
28        * Desc : 关闭服务器
29        ****************************************************/

30        void CloseServer();
31
32    protected:
33        SOCKET m_hSock;
34        vector<UDP_RCV_DATA* > m_RcvDataQue;                //接收数据队列
35        CRITICAL_SECTION m_RcvDataLock;                        //访问m_RcvDataQue的互斥锁
36        long volatile m_bThreadRun;                                //是否允许后台线程继续运行
37        BOOL m_bSerRun;                                            //服务器是否正在运行
38
39        HANDLE *m_pThreads;                //线程数组
40        HANDLE m_hCompletion;                    //完成端口句柄
41
42        void ReadCompletion(BOOL bSuccess, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped);
43
44        /****************************************************
45        * Name : WorkThread()
46        * Desc : I/O 后台管理线程
47        ****************************************************/

48        static UINT WINAPI WorkThread(LPVOID lpParam);
49    }
;


1. InitReource() 主要对相关的静态资源进行初始化.其实大致和TcpServer::InitReource()大致相同.在UdpSer实例使用之前必须调用该函数进行静态资源的初始化, 否则服务器无法正常使用.

2.ReleaseReource() 主要对相关静态资源进行释放.只有在应用程序结束时才能调用该函数进行静态资源的释放.

3. StartServer()
该函数的主要功能启动一个UDP服务.其大致流程为先创建服务器UDP socket, 将其绑定到完成端口上然后投递一定数量的recv操作以接收客户端的数据.其实现如下:

 1    BOOL UdpSer::StartServer(const CHAR* szIp /* =  */, INT nPort /* = 0 */)
 2    {
 3        BOOL bRet = TRUE;
 4        const int RECV_COUNT = 500;
 5        WSABUF RcvBuf = { NULL, 0 };
 6        DWORD dwBytes = 0;
 7        DWORD dwFlag = 0;
 8        INT nAddrLen = sizeof(IP_ADDR);
 9        INT iErrCode = 0;
10
11        try
12        {
13            if (m_bSerRun)
14            {
15                THROW_LINE;
16            }

17
18            m_bSerRun = TRUE;
19            m_hSock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
20            if (INVALID_SOCKET == m_hSock)
21            {
22                THROW_LINE;
23            }

24            ULONG ul = 1;
25            ioctlsocket(m_hSock, FIONBIO, &ul);
26
27            //设置为地址重用,优点在于服务器关闭后可以立即启用
28            int nOpt = 1;
29            setsockopt(m_hSock, SOL_SOCKET, SO_REUSEADDR, (char*)&nOpt, sizeof(nOpt));
30
31            //关闭系统缓存,使用自己的缓存以防止数据的复制操作
32            INT nZero = 0;
33            setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&nZero, sizeof(nZero));
34            setsockopt(m_hSock, SOL_SOCKET, SO_RCVBUF, (CHAR*)&nZero, sizeof(nZero));
35
36            IP_ADDR addr(szIp, nPort);
37            if (SOCKET_ERROR == bind(m_hSock, (sockaddr*)&addr, sizeof(addr)))
38            {
39                closesocket(m_hSock);
40                THROW_LINE;
41            }

42
43

抱歉!评论已关闭.