前腐后继 发表于 2006-12-13 11:22:00

udp服务器设计过程总结

<strong><br/><br/><br/></strong>文章标题:udp服务器设计过程总结<br/>原&nbsp;作&nbsp;者:李良刚<br/>原&nbsp;出&nbsp;处:vczx.com<br/>发&nbsp;布&nbsp;者:李良刚<br/>发布类型:原创<br/>发布日期:2004-08-25<br/>最近做一个视频传输的项目,考虑到实时性,选udp为主要网络通讯技术。&nbsp;<br/>由于,要能对多个客户端的管理。需要通过udp,模拟多个客户端联接验证的情况。&nbsp; <p></p><p>设计选型:&nbsp;<br/>为了尽量提高可靠性和稳定性,我选用事件模型的winsock&nbsp;api的异步重叠模式。只所以没有选完成端,是因为我们的客户端并不是很多,200以内就够了。这个200是因为我每秒钟要把1--4k左右的图象数据发给200个客户端&nbsp;20次左右。如果再多,不可能完成。&nbsp;</p><p>消息模式是基于消息的,可靠性和效率没有事件模型高。&nbsp;<br/>Winsock1.1不考虑。定为winsock2.2,&nbsp;幸好,连win98都自带winsock2.2。&nbsp;</p><p>为什么不用多播?&nbsp;<br/>一是,有些网络不支持多播,二是,多播实现起来更麻烦一些,以后再考虑这方面的实现。&nbsp;</p><p>设计思路:&nbsp;<br/>创建一个SOCKET.&nbsp;并监听事件。&nbsp;启动线程接收数据.&nbsp;<br/>用一个连表CobList,&nbsp;保存所有联上的客户,并通知联接成功。这样客户有机会,处理这一事件并作一些动作。&nbsp;<br/>当客户断开时,向服务器发一个事件,这样,服务器也可以做一些收尾的事情,当然,这一些要安全的发生。&nbsp;</p><p>关键的地方是收发部分,和数据处理部分。&nbsp;<br/>收发部分,要尽量的运用重叠模式的优势,即高效又不占CPU。如下代码&nbsp;<br/>发:&nbsp;<br/>BOOL&nbsp;CUdpSock::SendBuffer(char&nbsp;*buff,&nbsp;DWORD&nbsp;dwBufSize,struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpTo)&nbsp;<br/>{&nbsp;<br/>m_lock.Lock();&nbsp;<br/>WSABUF&nbsp;wsabuf;&nbsp;<br/>WSAOVERLAPPED&nbsp;over;&nbsp;<br/>DWORD&nbsp;dwRecv;&nbsp;<br/>DWORD&nbsp;dwFlags=0;&nbsp;<br/>DWORD&nbsp;dwRet;&nbsp;<br/>BOOL&nbsp;fPending;&nbsp;<br/>int&nbsp;nRet;&nbsp;</p><p>//&nbsp;<br/>//&nbsp;Setup&nbsp;the&nbsp;WSABUF&nbsp;and&nbsp;WSAOVERLAPPED&nbsp;structures&nbsp;<br/>//&nbsp;<br/>memset(&amp;over,0,sizeof(WSAOVERLAPPED));&nbsp;<br/>wsabuf.buf&nbsp;=&nbsp;buff;&nbsp;<br/>wsabuf.len&nbsp;=&nbsp;dwBufSize;&nbsp;<br/>over.hEvent&nbsp;=&nbsp;WSACreateEvent();&nbsp;</p><p>fPending&nbsp;=&nbsp;FALSE;&nbsp;<br/>nRet&nbsp;=&nbsp;WSASendTo(m_Socket,&nbsp;//&nbsp;Socket&nbsp;<br/>&amp;wsabuf,&nbsp;//&nbsp;WSABUF&nbsp;<br/>1,&nbsp;//&nbsp;Number&nbsp;of&nbsp;buffers&nbsp;<br/>&amp;dwRecv,&nbsp;//&nbsp;Bytes&nbsp;received&nbsp;<br/>dwFlags,&nbsp;//&nbsp;Flags&nbsp;<br/>lpTo,&nbsp;<br/>sizeof(sockaddr),&nbsp;<br/>&amp;over,&nbsp;//&nbsp;WSAOVERLAPPED&nbsp;<br/>NULL);&nbsp;//&nbsp;Completion&nbsp;function&nbsp;</p><p>if&nbsp;(nRet&nbsp;!=&nbsp;0)&nbsp;<br/>{&nbsp;<br/>int&nbsp;erro&nbsp;=&nbsp;WSAGetLastError();&nbsp;<br/>if&nbsp;(erro&nbsp;==&nbsp;WSA_IO_PENDING)&nbsp;<br/>fPending&nbsp;=&nbsp;TRUE;&nbsp;<br/>else&nbsp;<br/>{&nbsp;<br/>TRACE1("CUdpSock::SendBuffer&nbsp;erro&nbsp;%d\n",erro);&nbsp;<br/>CloseHandle(over.hEvent);&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>}&nbsp;</p><p>//&nbsp;<br/>//&nbsp;If&nbsp;the&nbsp;I/O&nbsp;isn't&nbsp;finished...&nbsp;<br/>//&nbsp;<br/>if&nbsp;(fPending)&nbsp;<br/>{&nbsp;<br/>//&nbsp;<br/>//&nbsp;Wait&nbsp;for&nbsp;the&nbsp;request&nbsp;to&nbsp;complete&nbsp;<br/>//&nbsp;or&nbsp;the&nbsp;exit&nbsp;event&nbsp;to&nbsp;be&nbsp;signaled&nbsp;<br/>//&nbsp;<br/>dwRet&nbsp;=&nbsp;WaitForSingleObject(over.hEvent,60000);&nbsp;<br/>//&nbsp;<br/>//&nbsp;Was&nbsp;the&nbsp;recv&nbsp;event&nbsp;signaled?&nbsp;<br/>//&nbsp;<br/>if&nbsp;(dwRet&nbsp;==&nbsp;WAIT_TIMEOUT)//WAIT_OBJECT_0/WAIT_TIMEOUT&nbsp;<br/>{&nbsp;<br/>CloseHandle(over.hEvent);&nbsp;<br/>TRACE("WAIT_TIMEOUT发送失败\n",NULL);&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>if&nbsp;(dwRet&nbsp;!=&nbsp;WAIT_OBJECT_0)//WAIT_OBJECT_0/WAIT_TIMEOUT&nbsp;<br/>{&nbsp;<br/>CloseHandle(over.hEvent);&nbsp;<br/>TRACE("发送失败\n",NULL);&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;</p><p>//&nbsp;<br/>//&nbsp;Get&nbsp;I/O&nbsp;result&nbsp;<br/>//&nbsp;<br/>if&nbsp;(!WSAGetOverlappedResult(m_Socket,&nbsp;<br/>&amp;over,&nbsp;<br/>&amp;dwRecv,&nbsp;<br/>FALSE,&nbsp;<br/>&amp;dwFlags))&nbsp;<br/>{&nbsp;<br/>CloseHandle(over.hEvent);&nbsp;<br/>TRACE("WSAGetOverlappedResult发送失败\n",NULL);&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>}&nbsp;</p><p>CloseHandle(over.hEvent);&nbsp;<br/>TRACE("发送成功\n",NULL);&nbsp;<br/>m_lock.Unlock();&nbsp;</p><p>return&nbsp;TRUE;&nbsp;<br/>}&nbsp;<br/>收:&nbsp;<br/>BOOL&nbsp;CUdpSock::RecvRequest(LPBYTE&nbsp;pBuf,&nbsp;DWORD&nbsp;dwBufSize,struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom)&nbsp;<br/>{&nbsp;<br/>WSAOVERLAPPED&nbsp;over;&nbsp;<br/>WSABUF&nbsp;wsabuf;&nbsp;<br/>DWORD&nbsp;dwRecv;&nbsp;<br/>DWORD&nbsp;dwFlags;&nbsp;<br/>DWORD&nbsp;dwRet;&nbsp;<br/>HANDLE&nbsp;hEvents;&nbsp;<br/>BOOL&nbsp;fPending;&nbsp;<br/>int&nbsp;nRet;&nbsp;</p><p>//&nbsp;<br/>//&nbsp;Zero&nbsp;the&nbsp;buffer&nbsp;so&nbsp;the&nbsp;recv&nbsp;is&nbsp;null-terminated&nbsp;<br/>//&nbsp;<br/>memset(pBuf,&nbsp;0,&nbsp;dwBufSize);&nbsp;</p><p>//&nbsp;<br/>//&nbsp;Setup&nbsp;the&nbsp;WSABUF&nbsp;and&nbsp;WSAOVERLAPPED&nbsp;structures&nbsp;<br/>//&nbsp;<br/>wsabuf.buf&nbsp;=&nbsp;(char*)pBuf;&nbsp;<br/>wsabuf.len&nbsp;=&nbsp;dwBufSize;&nbsp;</p><p>memset(&amp;over,0,sizeof(WSAOVERLAPPED));&nbsp;<br/>over.hEvent&nbsp;=&nbsp;m_hEventSock;&nbsp;</p><p>dwFlags&nbsp;=&nbsp;0;&nbsp;<br/>fPending&nbsp;=&nbsp;FALSE;&nbsp;<br/>int&nbsp;sizeAddr&nbsp;=&nbsp;sizeof(sockaddr_in);&nbsp;<br/>nRet&nbsp;=&nbsp;WSARecvFrom(m_Socket,&nbsp;//&nbsp;Socket&nbsp;<br/>&amp;wsabuf,&nbsp;//&nbsp;WSABUF&nbsp;<br/>1,&nbsp;//&nbsp;Number&nbsp;of&nbsp;buffers&nbsp;<br/>&amp;dwRecv,&nbsp;//&nbsp;Bytes&nbsp;received&nbsp;<br/>&amp;dwFlags,&nbsp;//&nbsp;Flags&nbsp;<br/>lpFrom,&nbsp;<br/>&amp;sizeAddr,&nbsp;<br/>&amp;over,&nbsp;//&nbsp;WSAOVERLAPPED&nbsp;<br/>NULL);&nbsp;//&nbsp;Completion&nbsp;function&nbsp;<br/>if&nbsp;(nRet&nbsp;!=&nbsp;0)&nbsp;<br/>{&nbsp;<br/>if&nbsp;(WSAGetLastError()&nbsp;!=&nbsp;WSA_IO_PENDING)&nbsp;<br/>{&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>else&nbsp;<br/>fPending&nbsp;=&nbsp;TRUE;&nbsp;<br/>}&nbsp;</p><p>//&nbsp;<br/>//&nbsp;If&nbsp;the&nbsp;I/O&nbsp;isn't&nbsp;finished...&nbsp;<br/>//&nbsp;<br/>if&nbsp;(fPending)&nbsp;<br/>{&nbsp;<br/>//&nbsp;<br/>//&nbsp;Wait&nbsp;for&nbsp;the&nbsp;request&nbsp;to&nbsp;complete&nbsp;or&nbsp;the&nbsp;exit&nbsp;event&nbsp;<br/>//&nbsp;<br/>hEvents&nbsp;=&nbsp;over.hEvent;&nbsp;<br/>hEvents&nbsp;=&nbsp;m_hEventExit;&nbsp;<br/>dwRet&nbsp;=&nbsp;WaitForMultipleObjects(2,&nbsp;<br/>&nbsp;&nbsp;hEvents,&nbsp;<br/>&nbsp;&nbsp;FALSE,&nbsp;<br/>&nbsp;&nbsp;INFINITE);&nbsp;<br/>//&nbsp;<br/>//&nbsp;Was&nbsp;the&nbsp;recv&nbsp;event&nbsp;signaled?&nbsp;<br/>//&nbsp;<br/>if&nbsp;(dwRet&nbsp;!=&nbsp;0)&nbsp;<br/>{&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>if&nbsp;(!WSAGetOverlappedResult(m_Socket,&nbsp;<br/>&amp;over,&nbsp;<br/>&amp;dwRecv,&nbsp;<br/>FALSE,&nbsp;<br/>&amp;dwFlags))&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;</p><p>//&nbsp;<br/>//&nbsp;Recv&nbsp;event&nbsp;is&nbsp;complete&nbsp;--&nbsp;keep&nbsp;statistics&nbsp;<br/>//&nbsp;<br/>m_translate&nbsp;=&nbsp;dwRecv;&nbsp;<br/>return&nbsp;TRUE;&nbsp;<br/>}&nbsp;<br/>数据处理部分。&nbsp;<br/>BOOL&nbsp;CUdpSock::DelWithResData(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom)&nbsp;<br/>{&nbsp;<br/>DWORD&nbsp;lenPag&nbsp;=&nbsp;sizeof(PackHead);&nbsp;<br/>DWORD&nbsp;start&nbsp;=&nbsp;0;&nbsp;<br/>DWORD&nbsp;onePagLeft&nbsp;=&nbsp;0;&nbsp;<br/>SockPags&nbsp;pags;&nbsp;<br/>if(m_bFillHead)&nbsp;<br/>{&nbsp;<br/>onePagLeft&nbsp;=&nbsp;m_PackHead.len&nbsp;-&nbsp;lenPag;&nbsp;<br/>if(m_SimpleIOBuffer.GetBufferLen()&nbsp;&lt;&nbsp;onePagLeft)&nbsp;<br/>{&nbsp;<br/>TRACE("There&nbsp;is&nbsp;no&nbsp;enough&nbsp;packege&nbsp;length!&nbsp;1\n");&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;</p><p>ASSERT(onePagLeft&nbsp;&lt;=&nbsp;IOBUFFLEN);&nbsp;<br/>pags.buff&nbsp;=&nbsp;new&nbsp;char;&nbsp;<br/>if(m_SimpleIOBuffer.Read(pags.buff&nbsp;,onePagLeft))&nbsp;<br/>{&nbsp;<br/>pags.len&nbsp;=&nbsp;onePagLeft;&nbsp;<br/>pags.cm&nbsp;=&nbsp;m_PackHead.cm;&nbsp;<br/>if(m_pResInterFace)&nbsp;<br/>{&nbsp;<br/>m_pResInterFace-&gt;Excute(&amp;pags,lpFrom);&nbsp;<br/>m_bFillHead&nbsp;=&nbsp;FALSE;&nbsp;<br/>DelWithResData(lpFrom);&nbsp;<br/>}&nbsp;<br/>}&nbsp;<br/>delete&nbsp;[]pags.buff;&nbsp;</p><p>}else&nbsp;<br/>{&nbsp;<br/>while(m_SimpleIOBuffer.Read((char*)&amp;m_PackHead,lenPag))&nbsp;<br/>{&nbsp;<br/>if(m_PackHead.ID&nbsp;!=&nbsp;'T'&nbsp;&amp;&amp;&nbsp;m_PackHead.ID&nbsp;!=&nbsp;'P')&nbsp;<br/>{&nbsp;<br/>m_SimpleIOBuffer.Reset();&nbsp;<br/>m_bFillHead&nbsp;=&nbsp;FALSE;&nbsp;<br/>TRACE("There&nbsp;is&nbsp;packege2&nbsp;is&nbsp;erro!\n");&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;</p><p>m_bFillHead&nbsp;=&nbsp;TRUE;&nbsp;</p><p>onePagLeft&nbsp;=&nbsp;m_PackHead.len&nbsp;-&nbsp;lenPag;&nbsp;<br/>if(m_SimpleIOBuffer.GetBufferLen()&nbsp;&lt;&nbsp;onePagLeft)&nbsp;<br/>{&nbsp;<br/>TRACE("There&nbsp;is&nbsp;no&nbsp;enough&nbsp;packege&nbsp;length!&nbsp;2\n");&nbsp;<br/>return&nbsp;FALSE;&nbsp;<br/>}&nbsp;</p><p>ASSERT(onePagLeft&nbsp;&lt;=&nbsp;IOBUFFLEN);&nbsp;<br/>pags.buff&nbsp;=&nbsp;new&nbsp;char;&nbsp;<br/>if(m_SimpleIOBuffer.Read(pags.buff&nbsp;,onePagLeft))&nbsp;<br/>{&nbsp;<br/>pags.len&nbsp;=&nbsp;onePagLeft;&nbsp;<br/>pags.cm&nbsp;=&nbsp;m_PackHead.cm;&nbsp;<br/>if(m_pResInterFace)&nbsp;<br/>{&nbsp;<br/>m_pResInterFace-&gt;Excute(&amp;pags,lpFrom);&nbsp;<br/>m_bFillHead&nbsp;=&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>}&nbsp;<br/>delete&nbsp;[]pags.buff;&nbsp;<br/>}&nbsp;<br/>}&nbsp;</p><p>return&nbsp;TRUE;&nbsp;<br/>}&nbsp;<br/>发送没什么可说的,主要在处理部分。如下:&nbsp;<br/>void&nbsp;CUdpSock::OnRead()&nbsp;<br/>{&nbsp;<br/>m_translate&nbsp;=&nbsp;0;&nbsp;<br/>sockaddr_in&nbsp;addrfro;&nbsp;<br/>memset(&amp;addrfro,0,sizeof(sockaddr_in));&nbsp;<br/>addrfro.sin_family&nbsp;=&nbsp;AF_INET;&nbsp;<br/>if&nbsp;(!RecvRequest(&nbsp;(LPBYTE)m_wsaInBuffer.buf,&nbsp;sizeof(m_byInBuffer),(sockaddr*)&amp;addrfro))&nbsp;<br/>{&nbsp;<br/>TRACE("CClientOverlappedSock::OnRead\n");&nbsp;<br/>return&nbsp;;&nbsp;<br/>}&nbsp;</p><p>if(m_translate)&nbsp;<br/>{&nbsp;<br/>m_SimpleIOBuffer.Write(m_wsaInBuffer.buf,m_translate);&nbsp;<br/>try{&nbsp;<br/>DelWithResData((sockaddr*)&amp;addrfro);&nbsp;<br/>}catch&nbsp;(...)&nbsp;{&nbsp;<br/>TRACE("Udp&nbsp;DelWithResData&nbsp;erro!\n");&nbsp;</p><p>memset(&amp;m_PackHead,0,sizeof(PackHead));&nbsp;<br/>m_bFillHead&nbsp;=&nbsp;FALSE;&nbsp;<br/>}&nbsp;<br/>m_SimpleIOBuffer.Notify();&nbsp;<br/>}&nbsp;</p><p>return&nbsp;;&nbsp;</p><p>}&nbsp;</p><p>一,&nbsp;注意有一个缓冲区m_SimpleIOBuffer主要用来保证每次收发的完整性。然后就是c++异常机制,主要是为了稳定性。&nbsp;<br/>二,&nbsp;在CUdpSock::DelWithResData的处理部分,有很多保护措施。这很重要。&nbsp;</p><p>然后从CUdpSock派生一个CSverUdpSock如下:&nbsp;<br/>#include&nbsp;"UdpSock.h"&nbsp;<br/>#include&nbsp;"ClientUdpConnect.h"&nbsp;<br/>#include&nbsp;"afxtempl.h"&nbsp;</p><p>class&nbsp;CSverUdpSock&nbsp;:&nbsp;public&nbsp;CUdpSock&nbsp;&nbsp;<br/>{&nbsp;<br/>public:&nbsp;<br/>virtual&nbsp;void&nbsp;Close();&nbsp;<br/>int&nbsp;GetClientCount();&nbsp;<br/>CClientUdpConnect*&nbsp;GetClient(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>virtual&nbsp;void&nbsp;OnRead();&nbsp;<br/>virtual&nbsp;void&nbsp;OnAccept(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>virtual&nbsp;void&nbsp;ShutDown(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>virtual&nbsp;void&nbsp;ShutDown(CClientUdpConnect&nbsp;*_pClient);&nbsp;<br/>virtual&nbsp;void&nbsp;OnShutDown(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>void&nbsp;CloseAllClients();&nbsp;<br/>CSverUdpSock();&nbsp;<br/>virtual&nbsp;~CSverUdpSock();&nbsp;<br/>CObList&nbsp;m_clients;&nbsp;<br/>CObList&nbsp;m_willbedeleteclients;&nbsp;<br/>CCriticalSection&nbsp;m_lockFreeClients;&nbsp;<br/>private:&nbsp;<br/>virtual&nbsp;BOOL&nbsp;Accept(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>BOOL&nbsp;IsAlreadyExit(struct&nbsp;sockaddr&nbsp;FAR&nbsp;*lpFrom);&nbsp;<br/>CCriticalSection&nbsp;m_lockClients;&nbsp;<br/>CEvent&nbsp;m_timer;&nbsp;<br/>protected:&nbsp;<br/>void&nbsp;AddDeathClient(CClientUdpConnect&nbsp;*_pClient);&nbsp;<br/>void&nbsp;FreeClients();&nbsp;<br/>};&nbsp;<br/>注意的地方就是安全处理种种联接请求和断开请求。&nbsp;<br/></p>
页: [1]
查看完整版本: udp服务器设计过程总结