VC学习资料收集(10):udp服务器设计过程总结


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

并附上执行程序.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值