udp服务器设计过程总结

文章标题:udp服务器设计过程总结
原 作 者:李良刚
原 出 处:vczx.com
发 布 者:李良刚
发布类型:原创
发布日期:2004-08-25
最近做一个视频传输的项目,考虑到实时性,选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(); 
}; 
注意的地方就是安全处理种种联接请求和断开请求。 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页