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