套接字I/O模型:重叠模型(1.事件通知)

// OverlappedModel.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <afxsock.h>
//#include "winsock2.h"

//1.定义变量
#define DATA_BUFSIZE 4096	//接收缓冲区大小

SOCKET	ListenSocket;//监听套接字
SOCKET	AcceptSocket;//与客户端通信的套接字
WSAOVERLAPPED	AcceptOverlapped;//重叠结构
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];//用来通知重叠操作完成的事件句柄数组
WSABUF DataBuf[DATA_BUFSIZE];//用来接收WSASocket数据的缓冲区 (从系统的内核中获取WSASocket数据,可以设置len的大小来控制你想要获得len长度的数据)
DWORD dwEventTotal = 0;//程序中事件的总数
DWORD dwRecvBytes = 0;//接收到的字符长度
DWORD Flags = 0;//WSARecv的参数

int _tmain(int argc, _TCHAR* argv[])
{
	//2.创建一个套接字,开始在指定的端口上监听连接请求
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);
	ListenSocket = 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(6000);
	bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr));//绑定套接字
	listen(ListenSocket,5);//开始监听

	//3.接受一个入站的请求
	SOCKADDR_IN ClientAddr;//定义一个客户端的地址结构作为参数
	int addr_length = sizeof(ClientAddr);
	AcceptSocket = accept(ListenSocket,(sockaddr*)&ClientAddr,&addr_length);//与客户端通讯的socket
	
	//4.建立并初始化重叠结构
	//创建一个事件对象
	EventArray[dwEventTotal] = WSACreateEvent();//返回值就是一个创建好的事件对象句柄
	
	//ZeroMemory作用是用0来填充一块内存区域
	ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));//置零
	AcceptOverlapped.hEvent = EventArray[dwEventTotal];//为重接结构关联事件

	char buffer[DATA_BUFSIZE];
	ZeroMemory(buffer, DATA_BUFSIZE);
	DataBuf->len = DATA_BUFSIZE;//
	DataBuf->buf = buffer;//初始化一个WSABUF结构
	dwEventTotal++;

	/*
	int WSARecv(
				SOCKET			s,					 //投递这个操作的套接字
				LPWSABUF		lpBuffers,			 //接收缓冲区(一个指向WSABUF结构数组的指针。每一个WSABUF结构包含一个缓冲区的指针buf和缓冲区的长度len)	
				DWORD			dwBufferCount,		 //数组中WSABUF结构的数量
				LPDWORD			lpNumberOfBytesRecvd,//如果接收操作立即完成,这里会返回函数调用所接收到的字节数指针
				LPDWORD			lpFlags,			 //一个指向标志位的指针(通常设置为0)
				LPWSAOVERLAPPED lpOverlapped,		 //“绑定”的重叠结构(对于非重叠套接口则忽略)
				LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//完成例程中会用到的参数,这里设为NULL		
	); 
	返回值:函数返回>0的值表示操作成功;
			 返回0表示连接中断,此时需要释放套接字资源;
			 返回SOCKET_ERROR(-1),表示出错,使用WSAGetLastError()获取出错的原因。
	*/
	//5.以WSAOVERLAPPED结构为参数,在套接字上投递WSARecv请求
	//各变量已经初始化完毕,就可以开始Socket操作了,然后让
	//WSAOVERLAPPED结构来替我们管理I/O请求,我们只用等待时间的触发就可以了
	if (WSARecv(AcceptSocket,&DataBuf,1,&dwRecvBytes,&Flags,&AcceptOverlapped,NULL) == SOCKET_ERROR)
	{
		//返回WSA_IO_PENDING是正常情况,标识IO操作正在进行,不能立即完成
		//如果不是,就关闭socket
		if (WSAGetLastError() != WSA_IO_PENDING)
		{
			closesocket(AcceptSocket);
			WSACloseEvent(EventArray[dwEventTotal]);
		}
	}
	
	//为了持续接收客户端数据,重复6-10步
	while (1)
	{
		/*
		DWORD WSAWaitForMultipleEvents(
								DWORD			cEvents,  //等候事件的总数量
								const WSAEVENT* lphEvents,//事件数组的指针
								BOOL			fWaitAll, //如果设置为TRUE,则事件数组中所有事件被传信的时候函数才会返回
														  //FALSE,则任何一个事件被传信,函数都要返回。我们这里肯定是要设置为FALSE的
								DWORD			dwTimeout,//超时时间。如果超时,函数会返回WSA_WAIT_TIMEOUT
														  //如果设置为0,函数会立即返回
														  //如果设置为WSA_INFINITE只有在某一个事件被传信后才会返回
								BOOL			fAlertable//在完成例程中会用到这个参数,这里我们先设置为FALSE
		); 
		*/
		//6.用WSAWaitForMultipleEvents函数等待重叠操作返回的结果
		//前面已经给WSARecv函数关联的重叠结构AcceptOverlapped赋了一个事件对象句柄,
		//所以这里我们只需等待事件对象的触发即可。
		//需要根据WSAWaitForMultipleEvents函数的返回值来确定事件数组中的 哪一个 事件被触发了
		DWORD dwIndex;
		//等候重叠I/O调用结束,因为我们把事件和Overlapped绑定在一起了(第4步的赋值即完成绑定),重叠操作完成后程序会接到事件通知
		dwIndex = WSAWaitForMultipleEvents(dwEventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
		//注意,这里的dwIndex并非是事件在数组里的Index,而是需要减去WSA_WAIT_EVENT_0
		dwIndex = dwIndex - WSA_WAIT_EVENT_0;

		/*
		BOOL WSAAPI WSAResetEvent( WSAEVENT hEvent );
		hEvent:标识一个开放的事件对象句柄。
		*/
		//7.使用WSAResetEvent函数重设当前这个用完的事件对象
		//事件已经被触发之后,要将它重置一下留作下一次使用
		WSAResetEvent(EventArray[dwIndex]);

		/*
		BOOL WSAGetOverlappedResult(
						SOCKET			s,//套接字句柄
						LPWSAOVERLAPPED	lpOverlapped,//这里是我们想要查询结果的那个重叠结构的指针
						LPDWORD			lpcbTransfer,//本次重叠操作的实际接收(或发送)的字节数
						BOOL			fWait,//确定函数是否等待重叠操作完成的标志。
											  //设置为TRUE时,直到操作完成函数才会返回;
											  //设置为FALSE时,而且操作仍处于挂起状态,那么函数就会返回FALSE
											  //错误为WSA_IO_INCOMPLETE
						LPDWORD			lpdwFlags//指向一个32位变量,该变量存放接收完成状态的附加标志位
		); 
		*/
		//8.使用WSAGetOverlappedResult函数获得重叠操做完成的通知
		//这是我们最关心的:重叠操作究竟是什么结果,是否完成?而唯一需要我们做的就是对方的Socket连接是否已经关闭了
		DWORD dwBytesTransferred;
		WSAGetOverlappedResult(AcceptSocket,AcceptOverlapped,&dwBytesTransferred,FALSE,&Flags);
		//如果参数3:dwBytesTransferred ==0,就表示未接收任何内容,表示对方连接已经关闭,我们也需要关闭
		if (dwBytesTransferred == 0)
		{
			closesocket(AcceptSocket);
			WSACloseEvent(EventArray[dwIndex]);//关闭事件
			return;
		}

		//9.处理接收到的数据:WSABUF结构里就是我们WSARecv来的数据
		//DataBuf->buf就是一个char*字符串指针
		//...处理...

		//10.在套接字上继续投递WSARecv请求
		Flags = 0;
		ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));

		AcceptOverlapped.hEvent = EventArray[dwIndex];
		DataBuf->len = DATA_BUFSIZE;
		DataBuf->buf = buffer;
		
		if (WSARecv(AcceptSocket,&DataBuf,1,&dwRecvBytes,&Flags,&AcceptOverlapped,NULL) ==SOCKET_ERROR)
		{
			if (WSAGetLastError() != WSA_IO_PENDING)
			{
				closesocket(AcceptSocket);
				WSACloseEvent(EventArray[dwIndex]);
			}
		}

	}

	
	return 0;
}

以上!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值