iT邦幫忙

2021 iThome 鐵人賽

DAY 17
0
自我挑戰組

從C到JS的同步非同步探索系列 第 17

[Day 17] IOCP 實作

  • 分享至 

  • xImage
  •  

前言

今天簡單實踐了最基本的 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 的小實驗。

明天見


上一篇
[Day 16] IOCP Input/Output Completion Port
下一篇
[Day 18] Node.js 的非同步小實驗
系列文
從C到JS的同步非同步探索30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言