完成端口(2) [转]

本文探讨了I/O完成端口(IOCP)机制及其在Windows NT中的应用,包括其设计原理、创建方法及如何利用API函数实现网络服务器的并发模型优化。
相信只要写过网络的朋友,应该对这样的结构在熟悉不过了。accept后线程被挂起,等待一个客户发出请求,而后创建新线程来处理请求。当新线程处理客户请求时,起初的线程循环回去等待另一个客户请求。处理客户请求的线程处理完毕后终结。

  在上述的并发模型中,对每个客户请求都创建了一个线程。其优点在于等待请求的线程只需做很少的工作。大多数时间中,该线程在休眠[因为recv处于堵塞状态]。

  但是当并发模型应用在服务器端[基于Windows NT],Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作。

  大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。

  我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题,Microsoft又怎会没有考虑到呢?!

  这个问题的解决方法就是一个称为I/O完成端口的内核对象,他首次在Windows NT3.5中被引入。

  其实我们上面的构想应该就差不多是IOCP的设计机理。其实说穿了IOCP不就是一个消息队列嘛!你说这和[端口]这两字有何联系。我的理解就是IOCP最多是应用程序和操作系统沟通的一个接口罢了。

  至于IOCP的具体设计那我也很难说得上来,毕竟我没看过实现的代码,但你完全可以进行模拟,只不过性能可能…,如果想深入理解IOCP, Jeffrey Ritchter的Advanced Windows 3rd其中第13章和第14张有很多宝贵的内容,你可以拿来窥视一下系统是如何完成这一切的。


实现方法

Microsoft为IOCP提供了相应的API函数,主要的就两个,我们逐一的来看一下:
HANDLE CreateIoCompletionPort (
    
HANDLE FileHandle,              // handle to file
    
HANDLE ExistingCompletionPort,  // handle to I/O completion port
    ULONG_PTR CompletionKey,        
// completion key
    
DWORD NumberOfConcurrentThreads // number of threads to execute concurrently
);


在讨论各参数之前,首先要注意该函数实际用于两个截然不同的目的:
1.用于创建一个完成端口对象
2.将一个句柄[HANDLE]和完成端口关联到一起

  在创建一个完成一个端口的时候,我们只需要填写一下NumberOfConcurrentThreads这个参数就可以了。它告诉系统一个完成端口上同时允许运行的线程最大数。在默认情况下,所开线程数和CPU数量相同,但经验给我们一个公式:
  线程数 = CPU数 * 2 + 2
要使完成端口有用,你必须把它同一个或多个设备相关联。这也是调用CreateIoCompletionPort完成的。你要向该函数传递一个已有的完成端口的句柄,我们既然要处理网络事件,那也就是将客户的socket作为HANDLE传进去。和一个完成键[对你有意义的一个32位值,也就是一个指针,操作系统并不关心你传什么]。每当你向端口关联一个设备时,系统向该完成端口的设备列表中加入一条信息纪录。

另一个API就是
BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort,        
// handle to completion port
    LPDWORD lpNumberOfBytes,      
// bytes transferred
    
PULONG_PTR lpCompletionKey,   // file completion key
    
LPOVERLAPPED *lpOverlapped,   // buffer
    DWORD dwMilliseconds         
// optional timeout value
);


第一个参数指出了线程要监视哪一个完成端口。很多服务应用程序只是使用一个I/O完成端口,所有的I/O请求完成以后的通知都将发给该端口。简单的说,GetQueuedCompletionStatus使调用线程挂起,直到指定的端口的I/O完成队列中出现了一项或直到超时。同I/O完成端口相关联的第3个数据结构是使线程得到完成I/O项中的信息:传输的字节数,完成键和OVERLAPPED结构的地址。该信息是通过传递给GetQueuedCompletionSatatus的lpdwNumberOfBytesTransferred,lpdwCompletionKey和lpOverlapped参数返回给线程的。

根据到目前为止已经讲到的东西,首先来构建一个frame。下面为您说明了如何使用完成端口来开发一个echo服务器。大致如下:
  1.初始化Winsock
  2.创建一个完成端口
  3.根据服务器线程数创建一定量的线程数
  4.准备好一个socket进行bind然后listen
  5.进入循环accept等待客户请求
  6.创建一个数据结构容纳socket和其他相关信息
  7.将连进来的socket同完成端口相关联
  8.投递一个准备接受的请求
以后就不断的重复5至8的过程
那好,我们用具体的代码来展示一下细节的操作。
至此文章也该告一段落了,我带着您做了一趟旋风般的旅游,游览了所谓的完成端口。
const unsigned char systemsettingpage[] = { "<html>\r\n" "<head>\r\n" "<meta charset=\"gb2312\">\r\n" // 20210421LWD "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\" />\r\n" // 20210421LWD "<title>智能蓄电池监控系统</title>" //"<Meat http-equiv=Content-Type content=text/html charset=gb2312>"//2021-03-19 "<script type=\"text/javascript\"> \r\n" "function validate()\r\n" "{\r\n" "var CT=document.getElementById(\"CT\").value\r\n" "var MT=document.getElementById(\"MT\").value\r\n" "var RT=document.getElementById(\"RT\").value\r\n" "var AD=document.getElementById(\"AD\").value\r\n" "var DA=document.getElementById(\"DA\").value\r\n" "var TM=document.getElementById(\"TM\").value\r\n" "var ED=document.getElementById(\"ED\").value\r\n" "var testtm=\"(([0-1][0-9])|(2[0-3])):[0-5][0-9]:[0-5][0-9]\"\r\n" "var testda=\"[2][0][0-9][0-9]-(([0][0-9])|([1][0-2]))-(([0-2][0-9])|([3][0-1]))\"\r\n" "submitOK=\"true\"\r\n" "if (isNaN(ED) || ED<0.0 || ED>0.2)\r\n" "{\r\n" "alert(\"均衡差异输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(CT) || CT<1 || CT>65535)\r\n" "{\r\n" "alert(\"充电记录周期输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(MT) || MT<29 || MT>65535)\r\n" "{\r\n" "alert(\"模块关机时间输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(AD) || AD<0 || AD>255)\r\n" "{\r\n" "alert(\"RS485地址超出范围!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(RT) || RT<0.1 || RT>65535.0)\r\n" "{\r\n" "alert(\"内阻测试周期输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (DA.match(testda)==null)\r\n" "{\r\n" "alert(\"日期输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (TM.match(testtm)==null)\r\n" "{\r\n" "alert(\"时间输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if(submitOK==\"false\")\r\n" "{\r\n" "return false\r\n" "}\r\n" "document.getElementById(\"RT\").value *=10\r\n" "document.getElementById(\"ED\").value *=100\r\n" "}\r\n" "function validate1()\r\n" "{\r\n" "var MA=document.getElementById(\"MA\").value\r\n" "var IP=document.getElementById(\"IP\").value\r\n" "var NG=document.getElementById(\"NG\").value\r\n" "var SM=document.getElementById(\"SM\").value\r\n" "var PN=document.getElementById(\"PN\").value\r\n" "var PR=document.getElementById(\"PR\").value\r\n" "var testma=\"[F][4].[0][E].[1][1].([0-9]|[A-F])([0-9]|[A-F]).([0-9]|[A-F])([0-9]|[A-F]).([0-9]|[A-F])([0-9]|[A-F])\"\r\n" "var testip=\"(([0-1][0-9][0-9])|([0-2][0-5][0-5])).(([0-1][0-9][0-9])|([0-2][0-5][0-5])).(([0-1][0-9][0-9])|([0-2][0-5][0-5])).(([0-1][0-9][0-9])|([0-2][0-5][0-5]))\"\r\n" "submitOK=\"true\"\r\n" "if (MA.match(testma)==null)\r\n" "{\r\n" "alert(\"MAC地址输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (IP.match(testip)==null)\r\n" "{\r\n" "alert(\"IP地址输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (NG.match(testip)==null)\r\n" "{\r\n" "alert(\"网关地址输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (SM.match(testip)==null)\r\n" "{\r\n" "alert(\"子网掩码输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(PN) || PN<0 || PN>65535)\r\n" "{\r\n" "alert(\"SOCKET1端口输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if (isNaN(PR) || PR<0 || PR>65535)\r\n" "{\r\n" "alert(\"SOCKET2端口输入错误!\")\r\n" "submitOK=\"false\"\r\n" "}\r\n" "if(submitOK==\"false\")\r\n" "{\r\n" "return false\r\n" "}\r\n" "}\r\n" "</script>\r\n" "</head>\r\n" "<body style=\"background-color:#f4f4f4\">\r\n" // "<h1>\r\n" // "<span style=\"color:#FFFFFF;background-color:#E53333;\"><strong><span style=\"font-size:34px;line-height:1;\">深圳市海德森科技股份有限公司</span></strong></span><br />\r\n" // "</h1>\r\n" "<span style=\"color:#000FFF;font-size:24px\">智能蓄电池监控系统</span>\r\n" "<p align=\"center\">\r\n" "<b>[&nbsp</b>" "<a href=\"/9.html\">主页</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/4.html\">实时数据</a>" //---------------------------------------------------20210421LWD "<b>&nbsp|&nbsp</b>" "<a href=\"/C.html\">初始内阻</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/7.html\">报警记录</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/1.html\">电池设置</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/2.html\" style=\"color:#ff0000\">系统设置</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/3.html\">报警设置</a>" "<b>&nbsp|&nbsp</b>" "<a href=\"/G.html\">安装调试</a>" "<b>&nbsp]</b>\r\n" "</p>\r\n" "<hr size=\"1\">\r\n" "<br />" "<form action=\"/2.html\" name=\"fm\" onsubmit=\"return validate()\">" "<p align=\"center\" style=\"color:#000FFF\">系统参数设置</p>" "<p align=\"center\">" "<table width=\"800\" bgcolor=\"#f0ffff\" border=\"0\" cellpadding=\"10\" cellspacing=\"10\">" "<tr>" "<hr size=\"1\" width=\"800\">" "<hr size=\"1\" width=\"800\">" "<td>" "<br />" "RS485设备地址:&nbsp&nbsp&nbsp&nbsp<input type=\"text\" value=%Number name=\"AD\" id=\"AD\" size=\"10\">" "</td>" "<td>" "<br />" "RS485设备速率:&nbsp&nbsp&nbsp&nbsp<select name=\"BD\">" "<option value=\"0\" %Number__>&nbsp&nbsp4800&nbsp&nbsp</option>" "<option value=\"1\" %Number__>&nbsp&nbsp9600</option>" "<option value=\"2\" %Number__>&nbsp&nbsp19200</option>" "</select>" "</td>" "</tr>" "<tr>" "<td>" "充电记录间隔(Min):<input type=\"text\" value=%Number name=\"CT\" id=\"CT\" size=\"10\">" "</td>" "<td>" "内阻测试周期(H):&nbsp&nbsp<input type=\"text\" value=%Number name=\"RT\" id=\"RT\" size=\"10\">" "</td>" "</tr>" "<tr>" "<td>" "FLASH数据清空:&nbsp&nbsp&nbsp&nbsp<select name=\"FR\">" "<option value=\"0\">&nbsp&nbsp禁止&nbsp&nbsp</option>" "<option value=\"1\">&nbsp&nbsp使能</option>" "</select>" "</td>" "<td>" "恢复出厂设置:&nbsp&nbsp&nbsp&nbsp&nbsp<select name=\"RS\">" "<option value=\"0\">&nbsp&nbsp禁止&nbsp&nbsp</option>" "<option value=\"1\">&nbsp&nbsp使能</option>" "</select>" "</td>" "</tr>" "<tr>" "<td>" "主机日期设置:&nbsp&nbsp&nbsp&nbsp&nbsp<input type=\"text\" value=%Number____ name=\"DA\" id=\"DA\" size=\"10\">" "</td>" "<td>" "主机时间设置:&nbsp&nbsp&nbsp&nbsp&nbsp<input type=\"text\" value=%Number__ name=\"TM\" id=\"TM\" size=\"10\">" "</td>" "</tr>" "<tr>" "<td >" "电池均衡:&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp<select name=\"EQ\">" "<option value=\"0\"%Number__>&nbsp&nbsp禁止&nbsp&nbsp</option>" "<option value=\"1\"%Number__>&nbsp&nbsp开启</option" "</select>" "</td>" "<td>" "均衡差异(V):&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp<input type=\"text\"value=%Number name=\"ED\" id=\"ED\" size=\"10\">" "</td>" "</tr>" "<tr>" "<td >" "模块通信速率:&nbsp&nbsp&nbsp&nbsp&nbsp<select name=\"MB\">" //--------------------------------------------------------20210421LWD "<option value=\"0\"%Number__>&nbsp&nbsp4800&nbsp&nbsp</option>" "<option value=\"1\"%Number__>&nbsp&nbsp9600&nbsp&nbsp</option>" "<option value=\"2\"%Number__>&nbsp&nbsp19200&nbsp&nbsp</option>" "<option value=\"3\"%Number__>&nbsp&nbsp38400</option" "</select>" "</td>" "<td>" "模块关机时间(Min):<input type=\"text\" value=%Number name=\"MT\" id=\"MT\" size=\"10\">" "</td>" "</tr>" "<tr>" "<td >" "内阻测试模式:&nbsp&nbsp&nbsp&nbsp&nbsp<select name=\"RM\">" //--------------------------------------------------------20210421LWD "<option value=\"0\"%Number__>&nbsp&nbsp自动快测&nbsp&nbsp</option>" "<option value=\"1\"%Number__>&nbsp&nbsp手动快测&nbsp&nbsp</option>" "<option value=\"2\"%Number__>&nbsp&nbsp自动慢测&nbsp&nbsp</option>" "<option value=\"3\"%Number__>&nbsp&nbsp手动慢测</option" "</select>" "</td>" "<td>" "组压模块:&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp<select name=\"GV\">" "<option value=\"0\"%Number__>&nbsp&nbsp禁止&nbsp&nbsp</option>" "<option value=\"1\"%Number__>&nbsp&nbsp开启</option" "</select>" "</td>" "</tr>" "<tr>" "<td>" "</td>" "<td align=\"right\">" "<input type=\"submit\" value=\"保存\">&nbsp" "<input type=\"reset\" value=\"取消\">" "<br />" "</td>" "</tr>" "</table>" "<hr size=\"1\" width=\"800\">" "<hr size=\"1\" width=\"800\">" "</p>" "</form>" "<form action=\"/2.html\" name=\"fm1\" onsubmit=\"return validate1()\">" "<p align=\"center\" style=\"color:#000FFF\">TCP/IP网络参数设置</p>" "</tr>" "<tr>" "<p align=\"center\">" "<table width=\"800\" bgcolor=\"#f0ffff\" border=\"0\" cellpadding=\"10\" cellspacing=\"10\">" "<tr>" "<hr size=\"1\" width=\"800\">" "<hr size=\"1\" width=\"800\">" "<td>" "<br />" "本机MAC地址:&nbsp<input type=\"text\" value=%Number___________ name=\"MA\" id=\"MA\" size=\"20\">" //----------------------------------20210421LWD "</td>" "<td>" "<br />" "本机IP地址:<input type=\"text\" value=%Number_________ name=\"IP\" id=\"IP\" size=\"18\">" "</td>" "</tr>" "<tr>" "</tr>" "<tr>" "<td>" "本地网关:&nbsp&nbsp&nbsp&nbsp<input type=\"text\" value=%Number_________ name=\"NG\" id=\"NG\" size=\"18\">" "</td>" "<td>" "子网掩码:&nbsp&nbsp<input type=\"text\" value=%Number_________ name=\"SM\" id=\"SM\" size=\"18\">" "</td>" "</tr>" "<tr>" "<td>" "Socket1端口号:<input type=\"text\" value=%Number name=\"PN\" id=\"PN\" size=\"10\">" "</td>" "<td>" "Socket2端口号:<input type=\"text\" value=%Number name=\"PR\" id=\"PR\" size=\"10\">" "</td>" "</tr>" "<tr>" "<td align=\"right\">" "<input type=\"submit\" value=\"保存\">&nbsp" "<input type=\"reset\" value=\"取消\">" "<br />" "</td>" "</tr>" "</table>" "<hr size=\"1\" width=\"800\">" "<hr size=\"1\" width=\"800\">" "</p>" "</form>" // "<script language=\"JavaScript\">\r\n" // "function reloadpg()\r\n" // "{\r\n" "<td align=\"right\">" "<input type=\"submit\" value=\"保存\">&nbsp" "<input type=\"reset\" value=\"取消\">" "<br />" "</td>" // // "setTimeout(\"reloadpg()\",5000)\r\n" // "document.location.href=\"/2.html\"\r\n" // // "}\r\n" // "setTimeout(\"reloadpg()\",5000)\r\n" // // "</script>\r\n" "</body>" "</html>" };化为完整的16进制数组
最新发布
09-19
ID VARCHAR(500) PRIMARY KEY, -- 主键(建议搭配序列实现自增) SOURCE_NAME VARCHAR2(100) NOT NULL, -- 网站名称(非空约束) DETAIL_LINK VARCHAR2(500) NOT NULL, -- 详情链接(长度适配长URL) DETAIL_TITLE VARCHAR2(200) NOT NULL, -- 详情标题 DETAIL_CONTENT CLOB NOT NULL, -- 正文HTML(大文本存储) PAGE_TIME DATE, -- 列表时间(精确到日) CREATE_TIME DATE DEFAULT SYSDATE, -- 创建时间(自动填充) LIST_TITLE VARCHAR2(200), -- 列表标题 CREATE_BY VARCHAR2(50) -- 创建者名称这个表传入Id = "http://ebid.scpcdc.com.cn:80/fhxrgg/26950.jhtml" webName = "四川港投集团交易管理服务平台" detailUrl = "http://ebid.scpcdc.com.cn:80/fhxrgg/26950.jhtml" title = "岷江犍为航电枢纽工程生产设备备品备件及需用工器具采购(第四次)候选人公示" date = "2025-02-12" detailResult = "<div class="WordSection1" style="layout-grid:15.6pt"> \n <p class="MsoNormal" align="center" style="text-align:center"><b><span style="font-size:18.0pt;font-family:宋体">四川嘉陵江凤仪航电开发有限公司</span></b></p> \n <p class="MsoNormal" align="center" style="text-align:center"><b><span style="font-size:18.0pt;font-family:宋体">凤仪航电枢纽发电机出口断路器采购项目</span></b></p> \n <p class="MsoNormal" align="center" style="text-align:center"><b><span style="font-size:18.0pt;font-family:宋体">中选候选人公示</span></b></p> \n <p class="MsoNormal" align="center" style="text-align:center"><b><span lang="EN-US" style="font-size:22.0pt;font-family:仿宋_GB2312">&nbsp;</span></b></p> \n <p class="MsoNormal" style="line-height:150%"><span style="line-height:150%;\nfont-family:宋体">本项目经评审委员会评审,推荐中选候选人为:</span></p> \n <p class="MsoNormal" style="margin-left:10.5pt;text-indent:-10.5pt;line-height:\n150%"><span style="line-height:150%;font-family:宋体">第一名:南京锐翔电气科技有限公司<span lang="EN-US">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>复询报价:<span " sqlDate = {Date@1895} "2025-03-06"这个数据会出错吗,
03-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值