人气 229

[游戏程序] udp服务器设计过程总结 [复制链接]

九艺网 2017-3-10 17:01:49

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
文章标题: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 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 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();
};
注意的地方就是安全处理种种联接请求和断开请求。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

QQ|手机版|小黑屋|九艺游戏动画论坛 ( 津ICP备2022000452号-1 )

GMT+8, 2024-4-25 12:16 , Processed in 0.125402 second(s), 23 queries .

Powered by Discuz! X3.4  © 2001-2017 Discuz Team.