[Day 17] IOCP 实作

前言

今天简单实践了最基本的 IOCP http server , 原则上是用我第二天写的 one2Many httpServer 改的。

中间会撷取跟 IOCP 有关的部分讲讲。

其实我不觉得大家有必要深入的去读原始码, 因为真的有点复杂, 会了也没太大作用, 我想, 只要稍微看过我写的概念, 心中大概有些认知, 之後读 node 的原始码时应该就够用了。

程序码

https://gist.github.com/leon123858/29ecce37aa3c43c59b8362cd336c75ef

以下会用 3 个步骤来解释 IOCP 在这个 http server 中的意义

  1. 建立 threadpool
  2. 注册客户端的讯息传入事件
  3. 讯息传入事件唤醒 thread 且回传资料

建立 threadpool ( httpserver 的 constructor )

httpServer() {
		// 上略, 仅创建 TCP 连线 socket
		// 创建 IOCP 公用 queue 
		eventQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
		if (eventQueue == NULL)
			errorHandle("IOCP create");
		// 将 TCP 连线事件注册进 IOCP, 等待客户端连线的事件发生
		if (CreateIoCompletionPort((HANDLE)listenSocket, eventQueue, (ULONG_PTR)0, 0) == NULL)
			errorHandle("IOCP listen");
		// 创建沉睡的 threadpool 等待讯息传入的事件发生後醒来处理事件
		for (int i = 0; i < THREADPOOL_SIZE; i++)
			workerThreads[i] = thread(&httpServer::workerThreadFunction, this, eventQueue);
}

注册 客户端的 讯息传入事件 ( main thread 利用无限 loop 运行此方法)

// 创建连线客户端 socket
struct sockaddr_in clientSocketSetting;
int clientSocketSettingLength;
clientSocketSettingLength = sizeof(clientSocketSetting);
SOCKET messageSocket = accept(listenSocket, (struct sockaddr*)&clientSocketSetting, &clientSocketSettingLength);
// 把连线客户端的 socket 注册进 IOCP , 所以等下 WSARecv 把客户端的讯息传入 queue 後, 也会触发事件。
ioInformation* ioInfo = new ioInformation(messageSocket);
if (CreateIoCompletionPort((HANDLE)messageSocket, eventQueue, (ULONG_PTR)ioInfo, 0) == NULL)
	errorHandle("IOCP listen");
// 把客户端的讯息传入 queue , 传进去後会触发事件唤醒 threadpool 中的 thread。
WSARecv(messageSocket, &(ioInfo->wsaBuf),1, &recvBytes, &flags, &(ioInfo->overlapped), NULL);

讯息传入事件 唤醒 thread 且回传资料 ( threadpool 中的 thread 运行的方法 )

while (true){
	// IOCP 提供的方法, 使 thread 沉睡, 当 queue 中有一个事件, 就唤醒一条 thread, 且取出资料
	result = GetQueuedCompletionStatus(eventQueue,&ipNumberOfBytes,(PULONG_PTR)&ipCompletionKey,&ipOverlap,INFINITE);
	// 确认事件类型	
	if (result == 0 || ipNumberOfBytes == 0)
		continue;
	// http response
	ioInformation* ioInfo = (ioInformation*)ipCompletionKey;
	request req = request(ioInfo->wsaBuf.buf, ioInfo->wsaBuf.len);
	if (req.requestType < 0) {
		delete ioInfo;
		continue;
	}
	cout << req.typeName[req.requestType] << " : " << req.filePath << endl;
	int sentResult = responseClient(req, ioInfo->socket);
	if (sentResult <= 0) {
		printf("send error\n");
		break;
	}
	delete ioInfo;
}

明天进度

明天会开始探讨一个关於 node 的小实验。

明天见


<<:  (Day17) this 介绍下 - 绑定 this 的 call & apply & bind 与严格模式

>>:  [从0到1] C#小乳牛 练成基础程序逻辑 Day 2 - Visual Studio 2022 开发环境建立 64位元

D23 Django-debug-tools 失败

我想要用这个工具来看SQL等等的debug讯息 但是我怎麽用他都不会出现那条展示列 如果有高手看到这...

使用 Python 实作网路爬虫 part 3

实际操作 了解 requests 与 BeautifulSoup 的功能後,我们来进行整合吧!接下来...

#26 初探 Electron

前两天我们做了一个网页服务器,接下来我们来帮它加上 GUI 吧! Electron Electron...

Day.26 Binary Search Tree IV

今天讲二元树的删除,特别拉一篇出来讲,是因为它满复杂,要处理的case很多。 树的删除这边会把它分成...

Azure CLI 本质 Restful API

为何 Azure CLI / SDK / Portal / PowerShell 的行为可以一致,背...