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

📄 serversocket.cpp

📁 几个关于网络模型的代码!有事件选择模型、重叠I/0模型
💻 CPP
字号:
#include "StdAfx.h"
#include ".\serversocket.h"

#define DATA_BUFSIZE 4096

CWinThread* pServerListenThread = NULL;
CWinThread* pOverlappedThread = NULL;

SOCKET sockListen;

SOCKET sockAcceptArray[WSA_MAXIMUM_WAIT_EVENTS]; // 与客户端通信的SOCKET

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];    // 与重叠结构配套的事件组

WSAOVERLAPPED AcceptOverlapped[WSA_MAXIMUM_WAIT_EVENTS]; // 重叠结构,每个SOCKET操作对应一个

WSABUF DataBuf[WSA_MAXIMUM_WAIT_EVENTS];  // 接收缓冲区,WSARecv的参数,每个SCOKET对应一个

DWORD dwEventTotal = 0, 
      dwRecvBytes = 0,
	  Flags = 0;

int  nSockIndex = 0;            // socket数组的编号

BOOL bOverlapped = FALSE;       // 是否处理重叠请求

// 监听线程
UINT _ServerListenThread(LPVOID lParam)
{
	TRACE("服务器端监听中.....\n");

	CServerSocket* pServer = (CServerSocket*)lParam;

	SOCKADDR_IN ClientAddr;                   // 定义一个客户端得地址结构作为参数
	int addr_length=sizeof(ClientAddr);

	while(TRUE)
	{
		if(dwEventTotal>=WSA_MAXIMUM_WAIT_EVENTS)  // 因为超出了Windows的最大等待事件数量
		{
			AfxMessageBox("已达到最大连接数!");
			continue;
		}

		SOCKET sockTemp = accept(sockListen,(SOCKADDR*)&ClientAddr,&addr_length); 
		if(sockTemp  == INVALID_SOCKET)
		{
			AfxMessageBox("Accept Connection failed!");
			continue;
		}

		nSockIndex = pServer->GetEmptySocket();    // 从Socket数组中获得一个空闲的socket

		sockAcceptArray[nSockIndex] = sockTemp;
		
		// 这里可以取得客户端的IP和端口,但是我们只取其中的SOCKET编号
		LPCTSTR lpIP =  inet_ntoa(ClientAddr.sin_addr);
		UINT nPort = ClientAddr.sin_port;
		CString strSock;
		strSock.Format("SOCKET编号:%d",sockAcceptArray[nSockIndex] );
		::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_SOCKET,
			(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)"客户端建立连接!");


		// 接收客户端连接以后,为每一个连入的SOCKET都初始化建立一个重叠结构
		Flags = 0;

		EventArray[nSockIndex] = WSACreateEvent();

		ZeroMemory(&AcceptOverlapped[nSockIndex],sizeof(WSAOVERLAPPED));

		char buffer[DATA_BUFSIZE];
		ZeroMemory(buffer,DATA_BUFSIZE);

		AcceptOverlapped[nSockIndex].hEvent = EventArray[nSockIndex]; // 关联事件

		DataBuf[nSockIndex].len = DATA_BUFSIZE;
		DataBuf[nSockIndex].buf = buffer;


	
		// 投递第一个WSARecv请求,以便开始在套接字上接受数据
		if(WSARecv(sockAcceptArray[nSockIndex] ,&DataBuf[nSockIndex],1,&dwRecvBytes,&Flags,
			& AcceptOverlapped[nSockIndex] ,NULL) == SOCKET_ERROR)
		{
			if(WSAGetLastError() != WSA_IO_PENDING)    
			{
				// 返回WSA_IO_PENDING是正常情况,表示IO操作正在进行,不能立即完成
				// 如果不是WSA_IO_PENDING错误,就表示操作失败了
				AfxMessageBox("错误:第一次投递Recv操作失败!!此套接字将被关闭!");
				closesocket(sockAcceptArray[nSockIndex]);
				sockAcceptArray[nSockIndex] = INVALID_SOCKET;

				WSACloseEvent(EventArray[nSockIndex]);

				continue;
			}
		}

		dwEventTotal ++;

		if(dwEventTotal == 1)                          // 此时如果dwEventTotal加以后等于1 
			                                           // 就说明_OverlappedThread休眠了,此时唤醒之
			pOverlappedThread->ResumeThread();       
	}

	return 0;
}

// 重叠I/O处理线程
UINT _OverlappedThread(LPVOID lParam)
{
	CServerSocket* pServer = (CServerSocket*)lParam;

	while(bOverlapped)                    // 循环检测事件数组中的事件,并对接收的数据进行处理:)
	{
		DWORD dwIndex;

		// 等候重叠I/O调用结束
		// 因为我们把事件和Overlapped绑定在一起,重叠操作完成后我们会接到事件通知
		dwIndex = WSAWaitForMultipleEvents(dwEventTotal,EventArray,FALSE,10,FALSE);

		if(dwIndex == WSA_WAIT_TIMEOUT)
			continue;

		if(dwIndex == WSA_WAIT_FAILED)  // 出现监听错误
		{
			int nErrorCode = WSAGetLastError();

			if(nErrorCode == WSA_INVALID_HANDLE)
			{
				AfxMessageBox("监听出现错误:无效的 lphEvents 参数!");   // 代码经常会出现这种错误,我实在没时间改了,55555555
			}
			else
				if(nErrorCode == WSA_INVALID_PARAMETER)
				{
					AfxMessageBox("监听出现错误:无效的 CEvents 参数!");
				}

			continue;
		}

		// 取得索引值,得知事件的索引号
		dwIndex = dwIndex - WSA_WAIT_EVENT_0;

		// 获得索引号以后,这个事件已经没有利用价值,重置之
		WSAResetEvent(EventArray[dwIndex]);

		// 然后确定当前索引号的SOCKET的重叠请求状态
		DWORD dwBytesTransferred;
		WSAOVERLAPPED& CurrentOverlapped = AcceptOverlapped[dwIndex]; // 这里纯粹为了减少代码长度,付给了一个临时变量
		SOCKET& sockCurrent = sockAcceptArray[dwIndex] ;  // 同上

		WSAGetOverlappedResult(sockCurrent,&CurrentOverlapped ,
			&dwBytesTransferred,FALSE,&Flags);

		// 先检查通信对方是否已经关闭连接
		// 如果==0则表示连接已经,则关闭套接字
		if(dwBytesTransferred == 0)
		{
			CString strSock;
			strSock.Format("SOCKET编号:%d",sockAcceptArray[dwIndex] );
			::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_SOCKET,
				(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)"客户端断开连接!");

			closesocket(sockCurrent);
			sockCurrent = INVALID_SOCKET;
			//WSACloseEvent( EventArray[dwIndex] );

			dwEventTotal--;
			if(dwEventTotal <= 0)                  // 如果没有事件等待则暂停重叠IO处理线程
			{

				pOverlappedThread->SuspendThread();  // 没有需要处理的重叠结构了,则本线程休眠
		    }
			continue;
		}


		// DataBuf中包含接收到的数据,我们发到对话框中给予显示
		CString strSock;
		strSock.Format("SOCKET编号:%d",sockCurrent);
		::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_MSG,
			(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)DataBuf[dwIndex].buf);


		// 然后在套接字上投递另一个WSARecv请求(不要晕,注意看,代码和前面第一次WSARecv一样^_^)
		Flags = 0;
		ZeroMemory(&CurrentOverlapped,sizeof(WSAOVERLAPPED));

		char buffer[DATA_BUFSIZE];
		ZeroMemory(buffer,DATA_BUFSIZE);

		CurrentOverlapped.hEvent = EventArray[dwIndex];
		DataBuf[dwIndex].len = DATA_BUFSIZE;
		DataBuf[dwIndex].buf = buffer;

		// 开始另外一个WSARecv
		if(WSARecv(sockCurrent,&DataBuf[dwIndex],1,&dwRecvBytes,&Flags,
			&CurrentOverlapped ,NULL) == SOCKET_ERROR)
		{
			if(WSAGetLastError() != WSA_IO_PENDING) 
			{
				AfxMessageBox("错误:投递Recv操作失败!!此套接字将被关闭!");

				closesocket(sockCurrent);
				sockCurrent = INVALID_SOCKET;

				WSACloseEvent(EventArray[dwIndex]);

				dwEventTotal--;

				continue;
			}
		}
	}

	return 0;
}

CServerSocket::CServerSocket(void)
{
	for(int i=0;i<WSA_MAXIMUM_WAIT_EVENTS;i++)   // 初始化SOCKET
	{
		sockAcceptArray[i] = INVALID_SOCKET;
	}
}

CServerSocket::~CServerSocket(void)
{
	TRACE("CServerSocket 析构!");

	if(pServerListenThread != NULL)
	{
		DWORD   dwStatus;
		::GetExitCodeThread(pServerListenThread->m_hThread, &dwStatus);

		if (dwStatus == STILL_ACTIVE)
		{
			delete pServerListenThread;
			pServerListenThread = NULL;
		}
	}

	if(pOverlappedThread != NULL)
	{
		DWORD   dwStatus;
		::GetExitCodeThread(pOverlappedThread->m_hThread, &dwStatus);

		if (dwStatus == STILL_ACTIVE)
		{
			delete pOverlappedThread;
			pOverlappedThread = NULL;
		}
	}
	
	WSACleanup();
}

// 这些都是固定套路了,没有区别
int CServerSocket::StartListening(const UINT& port)
{
	m_nPort = port;

	WSADATA wsaData;

	int nRet;

	nRet=WSAStartup(MAKEWORD(2,2),&wsaData);                      //开启winsock.dll
	if(nRet!=0)
	{
		AfxMessageBox("Load winsock2 failed");
		WSACleanup();
		return -1;
	}

	sockListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);         //创建服务套接字(流式)

	SOCKADDR_IN ServerAddr;                                       //分配端口及协议族并绑定

	ServerAddr.sin_family=AF_INET;                                
	ServerAddr.sin_addr.S_un.S_addr  =htonl(INADDR_ANY);          
	ServerAddr.sin_port=htons(port);

	nRet=bind(sockListen,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr)); // 绑定套接字

	if(nRet==SOCKET_ERROR)
	{
		AfxMessageBox("Bind Socket Fail!");
		closesocket(sockListen);
		return -1;
	}

	nRet=listen(sockListen,5);                                   //开始监听,并设置监听客户端数量
	if(nRet==SOCKET_ERROR)     
	{
		AfxMessageBox("Listening Fail!");
		return -1;
	}


	pServerListenThread =  AfxBeginThread(_ServerListenThread,this);    // 开始监听线程

	bOverlapped = TRUE;

	// 开始重叠IO线程,但是先休眠,因为没有需要等待的事件。
	pOverlappedThread = AfxBeginThread(_OverlappedThread,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL); 

	return 0;
}

int CServerSocket::StopListening()
{
	bOverlapped = FALSE;

	return 0;
}

// 返回一个空闲的SOCKET,算法可以改进的,这样穷举效率太低
// 一定要改,不要偷懒啊^_^
int CServerSocket::GetEmptySocket()
{
	for(int i=0;i<WSA_MAXIMUM_WAIT_EVENTS;i++)
	{
		if(sockAcceptArray[i]==INVALID_SOCKET)
		{
			return i;
		}
	}

	return -1;
}

// 获得本机IP
CString CServerSocket::GetLocalIP()
{
	WSADATA wsaData;
	if(WSAStartup(MAKEWORD(2,2),&wsaData)==SOCKET_ERROR)
	{
		AfxMessageBox("取得主机名字失败!");
		return "未能获得";
	}

	int nLen=20;
	char hostname[20];
	gethostname(hostname,nLen);        // 获得本机主机名

	CString strHostName(hostname);

	struct hostent FAR* lpHostEnt = gethostbyname(strHostName);

	if(lpHostEnt == NULL)
	{
		return "0.0.0.0";
	}

	LPSTR lpAddr = lpHostEnt->h_addr_list[0];  // 取得IP地址列表中的第一个为返回的IP

	struct in_addr inAddr;

	memmove(&inAddr,lpAddr,4);

	return inet_ntoa(inAddr);         // 转化成标准的IP地址形式
}

⌨️ 快捷键说明

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