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