WSAEventSelect编程模型实现

这篇博客详细介绍了如何运用WSAEventSelect编程模型来构建服务器端和客户端的网络通信。通过参考多个技术资源,博主阐述了实现过程,涵盖了socket、事件处理和流对象的关键概念,并提供了相关测试细节。
摘要由CSDN通过智能技术生成

这篇代码的实现过程中参考了:

http://www.cppblog.com/yehao/articles/153729.html

http://fpcfjf.blog.163.com/blog/static/554697932011814102913/

http://blog.csdn.net/wanjingwei/article/details/4306609

服务器端:

//把#include <winsock2.h>放到最前面
//至于原因,那是因为windows.h这个头文件已经包含了winsock.h 
//winsock.h和winsock2.h貌似有冲突 
#include <Winsock2.h> 
#include <stdio.h>
#include <Windows.h>
//#include "winsock.h"

//#pragma comment(lib,"wsock32")
#pragma comment(lib,"Ws2_32")

int main()
{
	int accept_num = 0;
	int recv_num = 0;
	int send_num = 0;
	int close_num = 0;	

	WSADATA wsadata;
	WORD version = MAKEWORD(2,2);
	int ret = WSAStartup(version,&wsadata);

	/*judge the right version of the windows socket*/
	if (ret != 0)
	{
		printf("%s\n","socket create error!");
		return 0;
	}
	if (LOBYTE(wsadata.wVersion)!=2 || HIBYTE(wsadata.wVersion!=2))
	{
		printf("%s\n","socket version is error!");
		WSACleanup();
		return 0;
	}

	WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]; /*the array to store the event object*/
	SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];    /*the array to store the socket object*/
	int nEventTotal = 0;						  /*the total number of the events*/
	USHORT nPort = 6000;

	/*create the listen socket and bind to specific port*/
	SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(nPort);
	addr.sin_addr.S_un.S_addr = INADDR_ANY;
	ret = bind(sListen,(sockaddr *)&addr,sizeof(addr));
	if (SOCKET_ERROR == ret)
	{
		printf("%s\n","socket bind error!");
		WSACleanup();
		return 0;
	}
	ret = listen(sListen,10);
	if (SOCKET_ERROR == ret)
	{
		printf("%s\n","socket listen error!");
		WSACleanup();
		return 0;
	}

	//获取发送缓冲区和接受缓冲区的大小

	printf("%d\n",SO_MAX_MSG_SIZE);

	int recvbuf_len;
	int recvlength = sizeof(recvbuf_len);
	getsockopt(sListen,SOL_SOCKET,SO_RCVBUF,(char *)&recvbuf_len,&recvlength);
	printf("recvbuf is: %d\n",recvbuf_len);

	int sendbuf_len;
	int sendlength = sizeof(sendbuf_len);
	getsockopt(sListen,SOL_SOCKET,SO_SNDBUF,(char *)&sendbuf_len,&sendlength);
	printf("sendbuf is: %d\n",sendbuf_len);

	/*create a event object and bind it to a socket*/
	WSAEVENT Event = WSACreateEvent();  //注意需要手动重置事件或者使用可以重置事件的API操作
	//WSAEVENT Event = CreateEvent();
	//调用这个函数后,套接字被自动设置为非阻塞模式
	WSAEventSelect(sListen,Event,FD_ACCEPT|FD_CLOSE /*|FD_WRITE*/);
	
	//这样做是为了当网络时间发生时,确定是在哪个套接字上发生的。
	//之后就在这个套接字上做相应的操作。
	eventArray[nEventTotal] = Event;
	sockArray[nEventTotal] = sListen;
	nEventTotal++;
	
	/*deal with the network events*/
	while (true)
	{
		//在所有事件对象上等待
		int nIndex = WSAWaitForMultipleEvents(nEventTotal,eventArray,false,WSA_INFINITE,false);
		
		//发生的事件对象的索引,一般是句柄数组中最前面的那一个,
		//然后再用循环依次处理后面的事件对象  
		nIndex = nIndex - WSA_WAIT_EVENT_0;
		
		//对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
		for (int i=nIndex; i<nEventTotal; i++)
		{
			ret = WSAWaitForMultipleEvents(1,&eventArray[i],true,1000,false);
			if (WSA_WAIT_FAILED == ret || WSA_WAIT_TIMEOUT == ret)
			{
				continue;
			}
			else
			{
				//获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件  
				WSANETWORKEVENTS netEvent;
				WSAEnumNetworkEvents(sockArray[i],eventArray[i],&netEvent);

				if(netEvent.lNetworkEvents & FD_ACCEPT)  //处理FD_ACCEPT通知消息
				{
					accept_num++;
					printf("the accept number is: %d\n",accept_num);

					if(netEvent.iErrorCode[FD_ACCEPT_BIT]==0)
					{					
						if (nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
						{
							printf("too many connections \n");
							continue;
						}
						SOCKET sNew = accept(sockArray[i],NULL,NULL);
						WSAEVENT Event1 = WSACreateEvent();
						WSAEventSelect(sNew,Event1,FD_READ|FD_CLOSE|FD_WRITE);
						eventArray[nEventTotal]=Event1;
						sockArray[nEventTotal]=sNew;
						nEventTotal++;
					}
				} 
				else if (netEvent.lNetworkEvents & FD_READ)  //处理FD_READ通知消息 
				{
					recv_num++;
					printf("the receive number is: %d\n",recv_num);
					
					//注意,WSAEventselect模型是和I/O同步通信模型共同使用的
					//这个模型所说的非阻塞只是相对于阻塞模型中send时候无数据
					//发送或recv无数据接收等待而言的,具体当执行这两个函数的时候
					//和阻塞模型的执行过程是相同的,是I/O同步的。
					if(netEvent.iErrorCode[FD_READ_BIT]==0)
					{
						char szText[8192];
						int nRecv = recv(sockArray[i],szText,sizeof(szText),0);
						printf("%d\n",nRecv);
						if (nRecv > 0)
						{
							szText[nRecv]='\0';
							printf("message is : %s\n",szText);
						}						
					}
				}
				else if (netEvent.lNetworkEvents & FD_CLOSE)  //处理FD_CLOSE通知消息 
				{
					close_num++;
					printf("the close number is: %d\n",close_num);

					accept_num--;

					if (netEvent.iErrorCode[FD_CLOSE_BIT]==0)
					{
						closesocket(sockArray[i]);
						WSACloseEvent(eventArray[i]);

						for (int j=i;j<nEventTotal-1;j++)
						{
							sockArray[j] = sockArray[j+1];
							eventArray[j] = eventArray[j+1];
							//sockArray[j] = sockArray[j+1];
						}
						nEventTotal--;
					}
				}
				else if (netEvent.lNetworkEvents & FD_WRITE)
				{
					int ret_num;
					send_num++;
					printf("the send number is: %d\n",send_num);

					if (netEvent.iErrorCode[FD_WRITE_BIT] == 0)
					{
						while (true)
						{
							char msg[47368]="";
							
							ret_num = send(sockArray[i],msg,sizeof(msg),0);
							//ret_num += ret_num;
							//printf("   %d \n",ret_num);

							//这里是为了验证FD_WIRTE触发机制,在这里先填充满套接字缓冲区
							//让后在另一端recv,到一定程度后系统会提示可以继续发送,触发
							//该事件,具体多大空间可以是TCP协议定义的。
							if (ret_num == SOCKET_ERROR)
							{							
								if(WSAGetLastError() == WSAEWOULDBLOCK)
								{
									printf("the buffer is full  \n");
									//Sleep(2000);
									break;
								}
							}
						}
					}
				}

				///BEGIN///
				//这里是为了验证在同一套接字上的绑定的一个event,如果和这个event关联了
				//多个网络事件,当这多个网络事件同时发生的时候会不会由于处理了一个网络
				//事件之后重置了event导致另一个被屏蔽。这里利用在客户端send数据,在服务
				//器端调整处理FD_READ和FD_WRITE的处理顺序实验,说明会屏蔽的。
				/*else if (netEvent.lNetworkEvents & FD_READ)  //处理FD_READ通知消息 
				{
					recv_num++;
					printf("the receive number is: %d\n",recv_num);

					if(netEvent.iErrorCode[FD_READ_BIT]==0)
					{
						char szText[256];
						int nRecv = recv(sockArray[i],szText,sizeof(szText),0);
						if (nRecv > 0)
						{
							szText[nRecv]='\0';
							printf("message is : %s\n",szText);
						}						
					}
				}
				END//
				*/

			}
		}
	}

	return 0;
}


 

客户端:

#include <iostream>
#include <stdio.h>
#include <string>
#include <Windows.h>
#include "winsock.h"
#pragma comment(lib, "wsock32")

using namespace std;

#define COMMAND 100

int main()
{
	int ret = 0;

	//初始化socket环境
	WSADATA wsadata;
	WORD version = MAKEWORD(2,0);
	ret = WSAStartup(version,&wsadata);
	
	//创建套接字并连接服务器
	SOCKET m_hClientSocket;
	m_hClientSocket = socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN m_addr1;
	m_addr1.sin_family = AF_INET;
	m_addr1.sin_addr.S_un.S_addr = inet_addr("192.168.100.57");
	m_addr1.sin_port = htons(6000);

	//在此可以测试FD_ACCEPT事件,以及第一次FD_WRITE事件触发机制
	ret = connect(m_hClientSocket,(LPSOCKADDR)&m_addr1,sizeof(m_addr1));

	//测试FD_READ事件触发机制
	char msg[1]="";
	if (send(m_hClientSocket,msg,sizeof(msg),0)==SOCKET_ERROR)
	{
		break;
	}

	//测试FD_WRITE事件触发机制
	char Clisend_msg[10000]="";
	int recv_len;
	recv_len = recv(m_hClientSocket,Clisend_msg,sizeof(Clisend_msg),0);
	while (recv_len > 0)
	{
		cout << recv_len << endl;
		recv_len = recv(m_hClientSocket,Clisend_msg,sizeof(Clisend_msg),0);
	}
	
	Sleep(10000);

	//测试FD_CLOSE事件触发机制
	closesocket(m_hClientSocket);
	WSACleanup();

	return 0;
}


 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值