⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vczx_udp.cpp

📁 用事件方式
💻 CPP
字号:
/*
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(); 
}; 
注意的地方就是安全处理种种联接请求和断开请求
*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -