IOCP编程详解:多线程同步与异步操作
"这篇文章主要介绍了IOCP编程流程,包括如何创建和使用IO完成端口,以及客户端和服务端的具体操作步骤。" 在Windows系统中,IO完成端口(IOCP,I/O Completion Port)是一种高效的多线程I/O模型,特别适用于高并发的网络服务器。下面我们将详细探讨IOCP的编程流程。 ### 1. 初始化阶段 #### 1.1 创建IO完成端口 在主线程中,首先需要创建一个IO完成端口。这通过调用`CreateIoCompletionPort`函数完成,传入`INVALID_HANDLE_VALUE`表示为新创建的句柄分配端口。 #### 1.2 创建服务线程 接着,主线程创建一个服务线程,用于处理I/O操作。服务线程的入口点通常是`ServerThread`函数,线程参数传递IO完成端口句柄。 #### 1.3 建立服务器套接字 在服务线程中,进行常规的TCP服务器初始化,包括`socket()`、`SOCKADDR_IN`结构体填充、`bind()`和`listen()`函数,使服务器能够接收客户端连接。 #### 1.4 绑定套接字到IO完成端口 当有新的客户端连接时,服务线程将新连接的套接字与IO完成端口关联,这样可以将完成的I/O操作放入端口队列。 #### 1.5 监听I/O事件 服务线程进入无限循环,不断调用`GetQueuedCompletionStatus`来获取端口队列中的I/O完成状态,处理已完成的I/O操作。 #### 1.6 处理客户端连接 当有新的客户端连接请求到达时,使用`accept()`函数接受连接,并获取新的套接字`sNew`,准备进行数据交换。 ### 2. 数据传输阶段 #### 2.1 分配句柄数据结构 在主线程或服务线程中,为每个客户端连接分配一个`PER_HANDLE_DATA`结构,用于存储每个连接的相关信息。 #### 2.2 将句柄绑定到IO完成端口 接着,将新分配的句柄数据结构的套接字与IO完成端口关联,确保I/O操作完成后能够通知服务线程。 #### 2.3 分配I/O数据结构 为每个I/O操作(如读写)分配一个`PER_IO_DATA`结构,其中包含缓冲区和操作类型等信息。 #### 2.4 发起异步接收 使用`WSARecv`发起异步接收操作,将接收缓冲区和接收长度等信息传给系统,等待数据到来。 #### 2.5 循环处理I/O操作 服务线程持续检查I/O队列,一旦有新的I/O完成,就进行相应的处理,如读取的数据解析、响应发送等。 ### 总结 IOCP编程模型通过将I/O操作与线程解耦,提高了服务器性能和可扩展性。主线程负责创建IO完成端口和服务线程,服务线程则处理所有I/O操作。这种模型允许系统根据需要动态调整工作线程数量,有效地处理大量并发连接。在实际应用中,IOCP常被用于构建高性能的网络服务器,例如HTTP服务器、FTP服务器或游戏服务器等。
1.主函数完成初始化工作:
1.1: (主线程)HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); 创建完成端口对象
1.2: (主线程)::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0); 创建线程用于接收等
1.3:(主线程)调用socket(),SOCKADDR_IN,bind(),listen(),初始化套接字
1.4: (线程函数)HANDLE hCompletion = (HANDLE)lpParam; 通过线程参数得到完成端口对象
1.5: (线程函数) while(TRUE) 定义循环 循环等待套接字上发生事件
1.6:(线程函数) ::GetQueuedCompletionStatus() 在关联到此完成端口的所有套节字上等待I/O完成
1.7: (主线程) SOCKADDR_IN saRemote; SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen); 为新连接建立结构并等待连接请求
2.有连接发生:
2.1: (主线程while循环中) PPER_HANDLE_DATA pPerHandle = (PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
创建PPER_HANDLE_DATA结构
2.2: (主线程while循环中) ::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
完成 完成端口 与 套接字关联
2.3: (主线程while循环中) 给pPerIO结构 添加类型
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType = OP_READ;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
2.4: (主线程while循环中) ::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL); 发送异步接收请求
2.5: (主线程while循环中) 回到WHILE循环accept()函数处 等待新连接
2.6: (线程函数while循环中)GetQueuedCompletionStatus() 此时I/O完成 开始处理消息
2.7: (线程函数while循环中)pPerIO->nOperationType 通过类型判断 消息类型
2.7:(线程函数while循环中)打印接收的消息 并在此投递 一个完成端口
pPerIO->buf[dwTrans] = '\0';
printf(pPerIO -> buf);
buf.buf = pPerIO->buf ;
buf.len = BUFFER_SIZE;
pPerIO->nOperationType = OP_READ;
DWORD nFlags = 0;
printf("12\n");
::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
2.8:(线程函数while循环中) 由于又有端口I/O完成 ::GetQueuedCompletionStatus()函数 继续判断并处理
2.9:(线程函数while循环中) 由于没有数据 GetQueuedCompletionStatus()返回值为错误 调用一下函数关闭
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
2.10:(线程函数while循环中) 在此回到while循环 调用::GetQueuedCompletionStatus()函数 继续等待
分析完成 完整代码如下:
#include "../common/initsock.h"
#include <stdio.h>
#include <windows.h>
// 初始化Winsock库
CInitSock theSock;
#define BUFFER_SIZE 1024
typedef struct _PER_HANDLE_DATA // per-handle数据
{
SOCKET s; // 对应的套节字句柄
sockaddr_in addr; // 客户方地址
} PER_HANDLE_DATA, *PPER_HANDLE_DATA;
剩余6页未读,继续阅读
- 粉丝: 1
- 资源: 1
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- IPQ4019 QSDK开源代码资源包发布
- 高频组电赛必备:掌握数字频率合成模块要点
- ThinkPHP开发的仿微博系统功能解析
- 掌握Objective-C并发编程:NSOperation与NSOperationQueue精讲
- Navicat160 Premium 安装教程与说明
- SpringBoot+Vue开发的休闲娱乐票务代理平台
- 数据库课程设计:实现与优化方法探讨
- 电赛高频模块攻略:掌握移相网络的关键技术
- PHP简易简历系统教程与源码分享
- Java聊天室程序设计:实现用户互动与服务器监控
- Bootstrap后台管理页面模板(纯前端实现)
- 校园订餐系统项目源码解析:深入Spring框架核心原理
- 探索Spring核心原理的JavaWeb校园管理系统源码
- ios苹果APP从开发到上架的完整流程指南
- 深入理解Spring核心原理与源码解析
- 掌握Python函数与模块使用技巧