Windows完成端口 IOCP模型(二)

1详解完成端口基本使用

1创建完成端口

HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

参数其实就是-1,0,0,0. 最后一个参数代表的就是

NumberOfConcurrentThreads,就是允许应用同时执行的线程数量,

未来避免上下文切换,就是说让每个CPU只允许一个线程,设置为0

就是有多少处理器,就有多少工作线程。

原因就是如果一台机器有两个CPU(两核),如果让系统同时运行的

线程,多于本机CPU数量的话,就没什么意义,会浪费CPU宝贵周期,

降低效率,得不偿失。

然后会返回一个HANDLE 只要不是NULL就是建立完成端口成功。

2创建Socket绑定侦听 不多说

SOCKET lo_sock = INVALID_SOCKET;
//创建失败
if (iocp == NULL){
goto failed;
}
//创建一个线程  把IOCP传到线程函数里
h_threadS = CreateThread(NULL, 0, ServerThread, (LPVOID)iocp, 0, 0);
// 防止内存泄露
CloseHandle(h_threadS);
//end
//创建socket
lo_sock = socket(AF_INET,SOCK_STREAM,0);
if (lo_sock == INVALID_SOCKET){
goto failed;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
int ret = bind(lo_sock, (const struct sockaddr*)&addr, sizeof(addr));
if (ret != 0){
printf("bind %s:%d error \n", "127.0.0.1", port);
goto failed;
}
printf("bind %s:%d success \n", "127.0.0.1", port);
printf("starting listener on %d\n", port);
// SOMAXCONN 通过listen指定最大队列长度
ret = listen(lo_sock, SOMAXCONN);
if (ret != 0){
printf("listening on port failed\n");
goto failed;
}
printf("listening on success\n");

3在主线程里面侦听accept

struct sockaddr_in c_addr;
int len = sizeof(c_addr);
//没有client接入进来,线程会挂起  也就是阻塞
int client_fd = accept(lo_sock, (struct sockaddr*)&c_addr, &len);
if (client_fd != INVALID_SOCKET){
        //这里就是有新的socket连接了  
	printf("new client %s:%d coming\n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
	    }
    else{
	continue;
}

//保存会话信息  
struct session* s = save_session(client_fd, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
将信息保存在一个存用户ip port  端口的结构体里面  这个结构体是这样的:
	/* 这个结构中定义
	struct  session{
	char c_ip[32]; //ip地址
	int c_port;  //端口
	int c_sock;  //socket句柄
	int removed;//删除标记
	struct  session * _next; //链表指针
};
*/

4然后把获得的客户端socket绑定到iocp

这段代码是在一个while(1)死循环里进行

先介绍下这个函数 和创建完成端口用的是一个API

HANDLE WINAPI CreateIoCompletionPort(
__in  HANDLE FileHandle,  //这里就是客户连入的socket
__in_opt HANDLE ExistingCompletionPort,//就是前面创建的完成端口,
__in ULONG_PRT CompletionKey,//这个参数可以传递一个结构体,自定义的结构体
                               //你只要把这个结构体传入,工作线程就可以取出来,
                               // 我使用的是上面我定义的 结构体                              
_in  DWORD DWORD NumberOfConcurrenThreads//上面说了,设置为0就行
);

//添加到这个完成端口
CreateIoCompletionPort((HANDLE)client_fd, iocp,(DWORD)s, 0);
client_fd 就是上面或得的客户端socket
然后iocp完成端口,  s就是带有客户端会话信息的结构体

5投递一个异步recv请求

(就是告诉完成端口,如果我这个客户端有包过,你要接收完成,然后告诉我)

在这之前就要定义一个结构体作为标志,因为启动的时候投递了很多的

I/O请求,要用一个标志来绑定每一个I/O操作,这样网络操作完成后,

在通过这个标志找到这组返回的数据:

一定要将WSAOVERLAPPED放第一个,其他的随意

//缓冲区大小
#define MAX_RECV_SIZE 8092
struct io_package{
WSAOVERLAPPED overlapped;  //重叠I/O网络操作都要用到这个 重叠结构
int opt;                    //标记请求的类型
int pkg_size;               //包的长度
WSABUF wsabuffer;           //存储数据的缓冲区,用来给重叠操作传递数据的
char pkg[MAX_RECV_SIZE];   //对应WSABUF里的缓冲区
};

//监听事件   用来标记请求的类型
enum{
	IOCP_ACCEPT = 0,
	IOCP_RECV,
	IOCP_WRITE,
};

WSARecv函数

int WSARecv(
SOCKET s,//当然是投递这个操作的套接字  
LPWSABUF lpBuffers,            // 接收缓冲区
DWORD dwBufferCount,           // 数组中WSABUF结构的数量,设置为1即可
LPDWORD lpNumberOfBytesRecvd,  // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
LPDWORD lpFlags,               // 设置为0  
LPWSAOVERLAPPED lpOverlapped,  // 这个Socket对应的重叠结构  
lpCompletionRoutine            //这个参数只有完成例程模式才会用到,  
)
WSA_IO_PENDING:最常见的返回值,说明WSARecv成功了, 但是I/O操作没完成

投递这个请求

struct io_package* io_data = malloc(sizeof(struct io_package));
//只需要清空一次,即可  就是为了 让重叠结构清空
memset(io_data, 0, sizeof(struct io_package));
io_data->wsabuffer.buf = io_data->pkg;
io_data->wsabuffer.len = MAX_RECV_SIZE - 1;
io_data->opt = IOCP_RECV; //标记请求类型   我们设置成接收
DWORD dwFlags = 0;
//............
WSARecv(client_fd, &io_data->wsabuffer, 1, NULL,&dwFlags, &io_data->overlapped, NULL);

5在工作线程里等待完成事件

GetQueuedCompletionStatus函数原型,是工作线程里要

用到的API,他一旦进入,工作线程就会被挂起,知道

完成端口上出现了完成的事件。或网络超时

那么这个线程会被立刻唤醒,执行后续代码

BOOL WINAPI GetQueuedCompletionStatus(
__in   HANDLE          CompletionPort,    // 这个就是我们建立的那个唯一的完成端口  
__out  LPDWORD         lpNumberOfBytes,   //这个是操作完成后返回的字节数
__out  PULONG_PTR      lpCompletionKey,   // 这个是建立完成端口的时候绑定的那个自定义结构体参
__out  LPOVERLAPPED    *lpOverlapped,     // 这个是在连入Socket的时候一起建立的那个重叠结构
 __in   DWORD           dwMilliseconds     // 等待完成端口的超时时间,WSA_INFINITE是等待有事件才返回

看下这个代码操作

//线程函数
static DWORD WINAPI ServerThread(LPVOID lParam)
{
	//获取完成端口
	HANDLE iocp = (HANDLE)lParam;
	//返回的字节数
	DWORD dwTrans;
	//带有socket句柄的结构体 因为之前是添加进去 这个函数可以取出
	struct session* s;
	//带有重叠结构的结构体
	struct io_package* io_data;
	//等待IOCP
	while (1){
	    	s = NULL;
		dwTrans = 0;
		io_data = NULL;

		//调用这个API 等待事件
		int ret = GetQueuedCompletionStatus(iocp, &dwTrans, (LPDWORD)&s, 
		(LPOVERLAPPED*)&io_data, WSA_INFINITE);
		if (ret == 0){
			printf("iocp error");//IOCP端口发生错误
			continue;
		}
		//来告诉所有用户socket的完成事件发生了
		printf("IOCP have event\n");
		//接收的字节==0 表示客户端断开连接
		if (dwTrans == 0){//socket关闭了
		        closesocket(s->c_sock);
		        //释放内存
			free(io_data); 
			continue;
		}

		//到这里意味着数据以及读取到
		//这里就是前面标记的事件类型
		switch (io_data->opt)
		{
		case IOCP_RECV:{ // 接收数据以及完成了
			io_data->pkg[dwTrans] = 0;
			printf("IOCP %d: recv %d,%s\n",s->c_port,dwTrans,io_data->pkg);

			//当读的请求完成后, 必须再投递一个读的请求
			DWORD dwFlags = 0;
			int ret = WSARecv(s->c_sock, &io_data->wsabuffer, 1, NULL, &dwFlags, &io_data->overlapped, NULL);

		}
			break;

		case IOCP_WRITE:{

		}
			break;

		case IOCP_ACCEPT:{

		}

			break;

		default:
			break;
		}

	}
	return 0;
}

到这里其实就完成了这个IOCP的使用,后面还会补充的。

原文地址:http://blog.51cto.com/12158490/2058302

时间: 2024-08-29 12:59:21

Windows完成端口 IOCP模型(二)的相关文章

Windows完成端口 IOCP模型(一)

1 Windows完成端口基本介绍 2他是只能在Windows下的基于SOCKET事件管理的模型 3与select不同,select需要多次重置管理句柄,IOCP只要一次 4有事件后select需要操作获取数据,而IOCP通知你的时候说明数据操作好了 5select管理句柄的数目有限,IOCP没有限制 6IOCP支持多线程同时等待. 我的设计思路一个线程用来侦听accept事件, 一个线程来侦听SOCKET的IO事件, 大部分框架都是这样, 其实可以只使用一个线程做异步SOCKET就完全足够了,

Winsock IOCP模型(二)

// IOCP2.cpp : Defines the entry point for the console application.// #include "stdafx.h"#include <WinSock2.h>#include <MSWSock.h>#include <Windows.h>#include <process.h>#pragma comment(lib, "WS2_32.lib") #defin

套接字I/O模型-完成端口IOCP

“完成端口”模型是迄今为止最为复杂的一种I/O模型.然而,假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!但不幸的是,该模型只适用于Windows NT和Windows 2000操作系统.因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型.要记住的一个基本准则是,假如要为Windows NT或Windows 2000开发高性能的

【windows核心编程】IO完成端口(IOCP)复制文件小例

1.演示内容 文件复制 2.提要 复制大文件时,使用FILE_FLAG_NO_BUFFERING标志 同时需要注意: 读写文件的偏移地址为 磁盘扇区 的整数倍 读写文件的字节数为 磁盘扇区 的整数倍 读文件到的缓冲区在进程地址空间中的地址为 磁盘扇区 的整数倍 3.JUST CODING #include "stdafx.h" #include <Windows.h> #include <process.h> #include <iostream>

【windows核心编程】IO完成端口(IOCP)复制文件小例前简单说明

1.关于IOCP IOCP即IO完成端口,是一种高伸缩高效率的异步IO方式,一个设备或文件与一个IO完成端口相关联,当文件或设备的异步IO操作完成的时候,去IO完成端口的[完成队列]取一项,根据完成键(Complete Key)来判断是哪个设备或文件的操作完成,然后再根据实际情况进行处理. 2.相关API 和 数据结构   将一个已完成的IO通知追加到IOCP的[完成队列]中 BOOL   PostQueuedCompletionStatus( HANDLE    hCompletionPort

IOCP模型与网络编

一.前言:        在老师分配任务(“尝试利用IOCP模型写出服务端和客户端的代码”)给我时,脑子一片空白,并不知道什么是IOCP模型,会不会是像软件设计模式里面的工厂模式,装饰模式之类的那些呢?嘿嘿,不过好像是一个挺好玩的东西,挺好奇是什么东西来的,又是一个新知识啦~于是,开始去寻找一大堆的资料,为这个了解做准备,只是呢,有时还是想去找一本书去系统地学习一下,毕竟网络的资料还是有点零散.话说,本人学习这个模型的基础是,写过一个简单的Socket服务器及客户端程序,外加一个简单的Socke

IOCP模型与网络编程

IOCP模型与网络编程 一.前言:        在老师分配任务("尝试利用IOCP模型写出服务端和客户端的代码")给我时,脑子一片空白,并不知道什么是IOCP模型,会不会是像软件设计模式里面的工厂模式,装饰模式之类的那些呢?嘿嘿,不过好像是一个挺好玩的东西,挺好奇是什么东西来的,又是一个新知识啦~于是,开始去寻找一大堆的资料,为这个了解做准备,只是呢,有时还是想去找一本书去系统地学习一下,毕竟网络的资料还是有点零散.话说,本人学习这个模型的基础是,写过一个简单的Socket服务器及客

Nginx源码分析 - Nginx启动以及IOCP模型

Nginx 源码分析 - Nginx启动以及IOCP模型 版本及平台信息 本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用linux更多,但是windows平台下的代码也基本相似 ,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看. Nginx启动 曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了 大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办.偶尔会去玉泉,但

IOCP模型总结(总结回顾)

IOCP旧代码重提,最近一直在玩其他方面的东东,时不时回顾一下,收益多多. IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型.它是应用程序使用线程池处理异步I/O请求的一种机制.在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求.这样就有很多的线程并行地运行在系统中.而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上.再加上创建新线程的开销比较大,所以造成了效率的