對(duì)Windows TCP/IP協(xié)議棧的一種簡(jiǎn)化設(shè)計(jì)
2.2 設(shè)置多級(jí)優(yōu)先級(jí)隊(duì)列
在網(wǎng)絡(luò)數(shù)據(jù)傳輸中,由于有些緊急數(shù)據(jù)希望盡快發(fā)送出去提交給目的主機(jī),而曰前的Windows系統(tǒng)網(wǎng)絡(luò)傳輸機(jī)制并沒有提供這樣的功能??梢酝ㄟ^采用多級(jí)優(yōu)先級(jí)隊(duì)列的方式來達(dá)到一定的實(shí)時(shí)效果,比如對(duì)于緊急數(shù)據(jù),可以設(shè)置最高優(yōu)先級(jí)值,而一般數(shù)據(jù)就可以設(shè)置最低優(yōu)先級(jí)值。在用戶應(yīng)用程序中,對(duì)發(fā)送函數(shù)進(jìn)行封裝,新的發(fā)送函數(shù)有個(gè)優(yōu)先級(jí)參數(shù),通過指明優(yōu)先級(jí)參數(shù)值靈活處理數(shù)據(jù),當(dāng)提交給內(nèi)核時(shí),就按照優(yōu)先級(jí)值放到相對(duì)應(yīng)的優(yōu)先級(jí)隊(duì)列中。相應(yīng)的在內(nèi)核收包、發(fā)包緩沖區(qū)中,設(shè)置多級(jí)優(yōu)先級(jí)隊(duì)列,按照多級(jí)反饋隊(duì)列調(diào)度算法進(jìn)行處理,每個(gè)隊(duì)列的優(yōu)先級(jí)不同,并且每個(gè)隊(duì)列的被處理的時(shí)間不同,各個(gè)隊(duì)列的時(shí)間片是隨著優(yōu)先級(jí)的減少而增加的,優(yōu)先級(jí)越高的隊(duì)列中它的被線程處理的時(shí)間也就越短。比如緊急數(shù)據(jù)放到最高優(yōu)先級(jí)隊(duì)列中,遲緩的數(shù)據(jù)可以放到最低優(yōu)先級(jí)隊(duì)列中,在內(nèi)核的發(fā)包線程中,首先判斷最高優(yōu)先級(jí)隊(duì)列是否為空,不為空則優(yōu)先發(fā)送該隊(duì)列中的數(shù)據(jù)包,當(dāng)該隊(duì)列的時(shí)間用完,如果該隊(duì)列還有包沒有處理完,則把這些包鏈接到低一級(jí)的隊(duì)列尾部,然后判斷低一級(jí)優(yōu)先級(jí)隊(duì)列是否為空,重復(fù)以上的操作依次進(jìn)行下去,當(dāng)對(duì)最低優(yōu)先級(jí)隊(duì)列處理完后,再循環(huán)處理。如果線程在處理第i隊(duì)列的數(shù)據(jù)時(shí),這時(shí)候有新的用戶數(shù)據(jù)進(jìn)入到比i隊(duì)列優(yōu)先級(jí)高的j隊(duì)列中,則線程處理完該數(shù)據(jù)就立即去處理j隊(duì)列,這個(gè)可以用一個(gè)掩碼mask,每一位標(biāo)識(shí)一個(gè)隊(duì)列,當(dāng)隊(duì)列不為空,則該標(biāo)識(shí)位置為1,否則置為0。
2.3 封裝Socket層
創(chuàng)建Socket套接字,就是打開設(shè)備對(duì)象(第一次是創(chuàng)建,之后就是打開),而打開設(shè)備對(duì)象就會(huì)創(chuàng)建一個(gè)內(nèi)核文件對(duì)象,這個(gè)內(nèi)核文件對(duì)象其實(shí)就可以映射創(chuàng)建的Socket套接字。對(duì)于打開設(shè)備對(duì)象,就可以用CreateFile()函數(shù),并且把返回的句柄定義為Socekt句柄,之后的操作就可以直接用這個(gè)Socket句柄進(jìn)行操作,如send()函數(shù),可以用WriteFile()函數(shù)封裝實(shí)現(xiàn);Receive()函數(shù)可以用ReadFile()函數(shù)封裝實(shí)現(xiàn);bind()函數(shù)、setsockopt()函數(shù)、getsockopt()函數(shù)都可以通過DeviceIoControl()函數(shù)封裝實(shí)現(xiàn)。為了真正實(shí)現(xiàn)打開設(shè)備等操作,需要在協(xié)議驅(qū)動(dòng)程序中塒各個(gè)用戶應(yīng)用程序下達(dá)的IRP請(qǐng)求進(jìn)行響應(yīng),用派遣函數(shù)就可以實(shí)現(xiàn)。在圖2中,用戶程序可以通過新封裝好的Socket層,使用原來同樣的Socket編程語句,這樣使用戶使用起來感覺沒有差別,對(duì)用戶是透明的。
2.4 協(xié)議驅(qū)動(dòng)
在應(yīng)用程序中,對(duì)同一個(gè)線程環(huán)境下的文件句柄的讀,寫等,映射到內(nèi)核中的IRP I/O堆棧的內(nèi)核文件對(duì)象File()bject是同一個(gè)File()bject,這樣可以用內(nèi)核文件對(duì)象作為紐帶作用。在協(xié)議驅(qū)動(dòng)的設(shè)備擴(kuò)展NDISPROT_OPEN_CONTEXT結(jié)構(gòu)體內(nèi),建立一個(gè)File Port鏈表,如圖3所示。鏈表的每個(gè)節(jié)點(diǎn)包含有內(nèi)核文件對(duì)象、接收數(shù)據(jù)緩沖區(qū)、發(fā)送數(shù)據(jù)緩沖區(qū)、端口號(hào)、接收數(shù)據(jù)緩沖區(qū)大小、發(fā)送數(shù)據(jù)緩沖區(qū)大小等兒部分。內(nèi)核文件對(duì)象用來標(biāo)識(shí)是哪一個(gè)用戶Socket句柄;接收、發(fā)送數(shù)據(jù)緩沖區(qū)用來存放Socket的接收、發(fā)送的數(shù)據(jù);端口號(hào)的作用是讓網(wǎng)絡(luò)數(shù)據(jù)包可以知道提交到哪個(gè)內(nèi)核文件對(duì)象下的接收緩沖區(qū);接收、發(fā)送數(shù)據(jù)緩沖區(qū)大小指明接收、發(fā)送緩沖區(qū)最大的長(zhǎng)度。如果緩沖區(qū)隊(duì)列滿,而這時(shí)候又有數(shù)據(jù)過來,則該數(shù)據(jù)應(yīng)被丟棄。在協(xié)議驅(qū)動(dòng)程序里面,利用這個(gè)FilePort鏈表,可以實(shí)現(xiàn)收發(fā)數(shù)據(jù),設(shè)置接收、發(fā)送緩沖區(qū)的大小等操作。
需要注意的是在NDISPROT_OPEN_CONTEXT結(jié)構(gòu)體內(nèi),需創(chuàng)建一個(gè)NPROT_LOCK類型的鎖,用來對(duì)FilePort鏈表進(jìn)行互斥訪問。
2.4.1 端口號(hào)的綁定
在協(xié)議驅(qū)動(dòng)設(shè)備擴(kuò)展中需要建立一張表,里面存放已默認(rèn)分配的端口號(hào)以及用戶綁定的端口號(hào),端口號(hào)是從小到大按序排列,表的作用是當(dāng)用戶應(yīng)用程序綁定端口號(hào)操作時(shí),首先會(huì)通過二叉查找法查找這張表,看該端口號(hào)是否存放在該表中,如果找到,則要返回給應(yīng)用程序綁定失敗,如果沒有找到,則把該端口號(hào)插入到適當(dāng)位置,并返回給應(yīng)用程序綁定成功。用戶應(yīng)用程序通過調(diào)用bind()函數(shù)實(shí)現(xiàn)綁定Socket套接字,其含義就是用端口號(hào)來惟一標(biāo)識(shí)用戶線程下的Socket,讓網(wǎng)絡(luò)數(shù)據(jù)包提交給正確的Socket,在bind函數(shù)里面可以通過封裝DeviceIo Control函數(shù)調(diào)用來實(shí)現(xiàn)。
2.4.2 發(fā)送數(shù)據(jù)過程
用戶應(yīng)用程序發(fā)送的IRP寫請(qǐng)求(WriteFile()函數(shù)),傳遞到協(xié)議驅(qū)動(dòng)程序后,調(diào)用派遣函數(shù)NdisProtWrite,通過IRP I/O堆棧里面的內(nèi)核文件對(duì)象循環(huán)遍歷FilePort鏈表找到對(duì)應(yīng)的節(jié)點(diǎn),然后把用戶應(yīng)用程序的數(shù)據(jù)通過緩沖區(qū)讀寫設(shè)備的方式拷貝到NDISPROT_OPEN_CONTEXT結(jié)構(gòu)的相應(yīng)的Priority SendQueue優(yōu)先級(jí)隊(duì)列中。如圖3所示,發(fā)包線程的工作主要有,從Priority SendQueue優(yōu)先級(jí)隊(duì)列中提取數(shù)據(jù),如何提取按照多級(jí)反饋隊(duì)列調(diào)度算法處理,經(jīng)過簡(jiǎn)化的TCP/IP協(xié)議棧,然后再調(diào)用NdisSendPackets函數(shù)發(fā)送給網(wǎng)卡驅(qū)動(dòng)程序。在TCP/IP協(xié)議棧中,把該數(shù)據(jù)的優(yōu)先級(jí)值賦值給IP首部的服務(wù)類型(TOS)字段中,使收包的時(shí)候根據(jù)此字段的優(yōu)先級(jí)值把包放進(jìn)相應(yīng)的收包優(yōu)先級(jí)隊(duì)列中。
2.4.3 接收數(shù)據(jù)過程
協(xié)議驅(qū)動(dòng)從網(wǎng)卡驅(qū)動(dòng)程序接收網(wǎng)絡(luò)數(shù)據(jù)包,這些數(shù)據(jù)包是打包封裝好的,首先存放在NDISPROT_OPEN_CONTEXT結(jié)構(gòu)的收包優(yōu)先級(jí)隊(duì)列Pri ority RecvQueue中,這樣可以接收到高速上傳過來的底層數(shù)據(jù)。如圖3所示,需要建立一個(gè)收包處理線程,它的主要工作是,從收包優(yōu)先級(jí)隊(duì)列提取數(shù)據(jù),具體算法根據(jù)上面的多級(jí)反饋隊(duì)列調(diào)度算法,然后經(jīng)由TCP/IP協(xié)議棧的處理,如果是UDP,TCP的數(shù)據(jù)包則通過包的目的端口號(hào),遍歷FilePort鏈表找到對(duì)應(yīng)的節(jié)點(diǎn),然后把剩下的凈數(shù)據(jù)提交給節(jié)點(diǎn)(目標(biāo)Socket)的收包緩沖區(qū)中。值得注意的是,因?yàn)镹DIS封裝數(shù)據(jù)用的是NDIS_PACKET結(jié)構(gòu),NDIS_PACKET結(jié)構(gòu)里面包含一個(gè)NDIS_BUFFER結(jié)構(gòu)的鏈表,在每個(gè)NDIS_BUFFER里面才真正指向數(shù)據(jù)的首地址,這里說的提交,并沒有拷貝數(shù)據(jù),只是把凈數(shù)據(jù)的首地址再次鏈接到FilePort鏈表中。當(dāng)用戶應(yīng)用程序通過Receive()函數(shù)接收數(shù)據(jù)的時(shí)候,會(huì)調(diào)用ReadFile()函數(shù),發(fā)出讀IRP請(qǐng)求,IRP到達(dá)協(xié)議驅(qū)動(dòng)后,調(diào)用NdisProtRead()派遣函數(shù)處理,NdisProtRead()會(huì)通過IRP I/O堆棧里面的內(nèi)核文件對(duì)象,遍歷FilePort鏈表,找到相應(yīng)的節(jié)點(diǎn),再把節(jié)點(diǎn)接收緩沖區(qū)里面的數(shù)據(jù)拷貝到用戶緩沖區(qū)里面。
tcp/ip相關(guān)文章:tcp/ip是什么
評(píng)論