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