TCP/IP 在 Windows 下的實現

來源:互聯網
上載者:User

標籤:key   實用   cpi   ndt   綁定   應用   架構   pac   裝置   

        Windows 實現TCP/IP 協議也是建立在上一篇部落格的OSI 基礎之上的。

使用者態是由ws2_32.dll 和一些其他服務提供者的 dll 共同實現,當中ws2_32.dll 是一個架構。能夠容納非常多的服務提供者,這些服務提供者事實上就是各種協議的實現者,如比較常見的有 TCP/IP 協議,IPX 協議。而 TCP/IP 協議的服務實現是由 msafd.dll 和 mswsock.dll 來完畢。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2,該注冊表下記錄了協議(服務)及其一些其他的資訊。

        就 TCP/IP 而言。我們普遍會使用 posix標準的 socket 介面來完畢我們應用程式的功能,這樣要想完畢跨平台的代碼就會比較方便。

        在上一篇文章中,我們知道,tcp/ip 協議的使用者態部分由msafd.dll 完畢,它與核心部分的 afd.sys 互動來實現 socket 介面的系統調用。然後 afd.sys 完畢 socket 的一些機制。而且和 tcpip.sys 驅動程式互動,總結一下例如以下。

        1.      Msafd.dll : socket 介面的使用者態部分。與afd.sys 通訊。

        2.      Afd.sys  : socket 介面的核心態部分。滿足 msafd.dll的調用,向下與 tcpip.sys 通訊。

        3.      Tcpip.sys : tcp/ip 協議的主要實現部分,滿足afd.sys 的調用,向下與小port網卡驅動通過 IRP通訊。

        4.      socket 的概念是在 msafd.dll和 afd.sys 中才有的,它們兩個實現了 socket 的使用者態和核心態部分。它們的下層是傳輸層(TDI)層,TDI 層完畢了 TCP, UDP, RawIp的機制。在 TDI 層中。僅僅有地址對象,連線物件,控制通道的概念。TDI 的下層是網路層(IP 層),在 IP 層中,僅僅有 Packet 的概念,收到資料時,通過 IP 包中的標識。知道要提交給 TCP 或 UDP 等處理。

TDI 層和 IP 層都由 tcpip.sys 來實現。

        知道上面的概念後。就有了比較清晰的結構,當然驅動和裝置的管理由 IO 管理器來管理,但tcpip 協議族卻沒實用常規裝置棧的方式來處理資料包,afd.sys 與 tcpip.sys 以及 tcpip.sys 與 miniport 驅動之間都是由發送 IRP 來實現。這也使中間過濾層驅動的實現稍微複雜,這裡且不談。

那麼我們把上面零散的概念串起來,看看從普通的 socket 介面到資料終於由網卡發出的整個過程。

        Socket :

        Ws2_32.dll 載入時會依據注冊表初始化服務提供者,服務者會告知自己支援的地址族,socket 類型,和協議類型。當我們調用socket(AF_INET, SOCK_DGRAM, IPPROT_UDP) 來建立一個 UDP 類型的通訊端的時候,依據傳入的參數,會定位到 msafd.dll 這個服務提供者,並會調用對應的 socket 建立介面,它會開啟裝置 \Device\Afd\EndPoint ,因為 afd.sys 建立了一個 \Device\Afd 裝置,所以一個 IRP_MJ_CREATE 的 IRP 便會發送到 afd.sys 驅動的建立函數,它會建立一個FAD_FCB 結構體來表示這個通訊端,而且記錄下 FileObject,並返回。

        Bind :

        要想接收資料包。我們會把 socket 綁定到本地的一個IP-Port 對,就是調用 Bind 介面,msafd.dll 會通過一個控制訊息。次功能號為 IOCTL_AFD_BIND。此時afd.sys 會接著依據上面 FCB 記錄的裝置名稱開啟對應的 \Device\UDP 裝置,並把輸入參數標識為是一個傳輸層的地址,那麼 tcpip.sys 會建立介面就會建立一個地址對象來表示這次綁定。當然還會分配對應的port資訊。

        Connect:

        假設是 TCP,還須要串連到對方的socket。與 Bind 類似。它也會依據 FCB 記錄的裝置名稱開啟對應的裝置,並把輸入參數標識為是一個連線物件,tcpip.sys 會建立一個連線物件來表示這次串連。

        事實上在 TDI 層,另一種叫做控制通道。當其他驅動想得到 TDI 層的一些資訊,如當前的 TCP或 UDP 串連有哪些,那麼它會直接開啟 \Device\TCP 等裝置,因為沒有傳入參數,那麼 tcpip.sys 則會建立一個控制通道。TDI 層這些對象的標識都會儲存在與之相應的 FileObject->FsContext2 裡,以便後來區分。

        當前面準備工作做好後。我們就來看資料的接收和發送。

        SendTo:

        由 msafd.dll 發送一個 IOCTL_AFD_SEND_DATAGRAM到 afd.sys 。afd.sys 建立一個主功能號為 IRP_MJ_INTERNAL_DEVICE_CONTROL ,次功能號為 TDI_SEND_DATAGRAM的 IRP 到 tcpip.sys,tcpip.sys 調用相就的 UDPSendDatagram。組裝一個 UDP 包,最後通過 IpSendDatagram 到協議層,然後由對應的小port驅動發送出去。

        RecvFrom:

        接收資料略微複雜一點,接收資料都是由afd.sys 驅動發送一個次功能號為 TDI_RECEIVE_DATAGRAM (afd.sys 與 tcpip.sys 的傳輸層都是以 IRP_MJ_INTERNAL_DEVICE_CONTROL 為主功能號)的 IRP 到 TDI 層。而 TDI 層都是以接收請求的形式來掛在地址對象的接收請求(DATAGRAM_RECEIVE_REQUEST)隊列中,在地址對象建立的時候會建立這個隊列。那麼什麼時候這個請求會被滿足呢,這要從網卡接到資料說起。當網卡接收到資料時。協議驅動也會收到這個資料,普通情況下僅僅有能處理這個協議的驅動才會去處理這個包。此時就會進行到 tcpip.sys 的協議部分,即 IP 協議,tcpip.sys 依據對應的標識,確定是 IP 包,由於 tcpip.sys 還完畢了 ARP 包的處理,最後會上交到 Ipv4 的處理流程。它會調用ProcessFragment ->IpDispatchProtocol ,IpDispatchProtocl 會區分出是什麼包,假設是 UDP 包,由會調用UDPReceive ,並進一步依據地址對象鏈表來找到匹配的地址對象。DGDeliverData 來交付資料。它會查看對址對象的接收請求隊列中是否有請求。假設沒有,則查看是否注冊了接收資料的處理過程,假設也沒有注冊。那麼就會丟掉這個包。這就是 UDP 不可靠的一個原因。

        那麼有人就會有疑問,我們假設調用完 Bind 之後,還沒來得及調用 RecvFrom ,那麼。接收到的包不就丟了麼。事實上,在調用 Bind 之後。就會立即發送一個接收請求到隊列中。也就避免了這樣的情況的發生。

這僅僅是整個過程的導火索。在 Bind 裡面它是通過調用TdiReceiveDatagram 來投遞一個接收請求的,它會建立一個TDI_RECEIVE_DATAGRAM 的 IRP。並為這個 IRP 設定一個完畢常式PacketSocketRecvComplete, tcpip.sys 會響應這個 IRP,並在對應的地址對象的接收請求隊列中插入一個請求,並設定這個請求的完畢函數為DGReceiveComplete。使用者完畢函數為DispDataRequestComplete。 當通過 DGDeliverData 交付資料時,假設隊列中有請求,那麼就去滿足這個請求,拷貝資料到與這個請求對應的緩衝中,當調用請求的完畢函數 DGReceiveComplete,它會調用使用者完畢函數 DispDataRequestComplete,DiapDataRequestComplete會完畢這個 IRP,那麼 IRP 的完畢常式PacketSocketRecvComplete 就會得到調用了。 在 PacketSocketRecvComplete中(該函數在 afd.sys 中)要做的工作先暫停一下。回到 RecvFrom 的調用。在 RecvFrom 向下直到 afd.sys 層,它並不會直接發送 IRP 到 tcpip.sys 中去請求接收資料,假設 FCB->DatagramList 中沒有資料。它會把 msafd.dll 下發的這個 IRP 放到 FCB->PendingIrpList 中,並掛起,所以到 tcpip.sys 的請求都是由在 Bind 最後發送的那個導火索引起。回到 PacketSocketRecvComplete 中。它會從FCB->PendingIrpList 中摘掉一個 IRP 並插入一個資料包到 FCB->DatagramList 中,最後完畢這個 IRP。那麼 RecvFrom 下發的這個 IRP 就完畢了。

最後它又調用TdiReceiveDatagram 來投遞一個接收請求,然後周而復始。

        一個 UDP Socket的大致過程就到此為止了。

TCP/IP 在 Windows 下的實現

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.