完成端口中的單句柄數據結構與單IO數據結構的理解與設計 完成端口模型,針對於WIN平臺的其它異步網絡模型而言,最大的好處,除了性能方面的卓越外,還在於完成端口在傳遞網絡事件的通知時,能夠一併傳遞與此事件相關的應用層數據。這個應用層數據,體如今兩個方面:一是單句柄數據,二是單IO數據。 GetQueuedCompletionStatus函數的原型以下: WINBASEAPI BOOL WINAPI GetQueuedCompletionStatus( IN HANDLE CompletionPort, OUT LPDWORD lpNumberOfBytesTransferred, OUT PULONG_PTR lpCompletionKey, OUT LPOVERLAPPED *lpOverlapped, IN DWORD dwMilliseconds ); 其中,咱們把第三個參數lpCompletionKey稱爲完成鍵,由它傳遞的數據稱爲單句柄數據。咱們把第四個參數lpOverlapped稱爲重疊結構體,由它傳遞的數據稱爲單IO數據。 以字面的意思來理解,lpCompletionKey內包容的東西應該是與各個socket一一對應的,而lpOverlapped是與每一次的wsarecv或wsasend操做一一對應的。 在網絡模型的常見設計中,當一個客戶端鏈接到服務器後,服務器會經過accept或AcceptEx建立一個socket,而應用層爲了保存與此socket相關的其它信息(好比:該socket所對應的sockaddr_in結構體數據,該結構體內含客戶端IP等信息,以及爲便於客戶端的邏輯包整理而準備的數據整理緩衝區等),每每須要建立一個與該socket一一對應的客戶端底層通訊對象,這個對象能夠負責保存僅在網絡層須要處理的數據成員和方法,而後咱們須要將此客戶端底層通訊對象放入一個相似於list或map的容器中,待到須要使用的時候,使用容器的查找算法根據socket值找到它所對應的對象而後進行咱們所須要的操做。 讓人很是高興的是,完成端口「體貼入微」,它已經幫咱們在每次的完成事件通知時,稍帶着把該socket所對應的底層通訊對象的指針送給了咱們,這個指針就是lpCompletionKey。也就是說,當咱們從GetQueuedCompletionStatus函數取得一個數據接收完成的通知,須要將這次收到的數據放到該socket所對應的通訊對象整理緩衝區內對數據進行整理時,咱們已經不須要去執行list或map等的查找算法,而是能夠直接定位這個對象了,當客戶端鏈接量很大時,頻繁查表仍是很影響效率的。哇哦,太帥了,不是嗎?呵呵。 基於以上的認識,咱們的lpCompletionKey對象能夠設計以下: typedef struct PER_HANDLE_DATA { SOCKET socket; //本結構體對應的socket值 sockaddr_in addr; //用於存放客戶端IP等信息 char DataBuf[ 2*MAX_BUFFER_SIZE ]; //整理緩衝區,用於存放每次整理時的數據 } PER_HANDLE_DATA與socket的綁定,經過CreateIOCompletionPort完成,將該結構體地址做爲該函數的第三個參數傳入便可。而PER_HANDLE_DATA結構體中addr成員,是在accept執行成功後進行賦值的。DataBuf則能夠在每次WSARecv操做完成,須要整理緩衝區數據時使用。 下面咱們再來看看完成端口的收、發操做中所使用到的重疊結構體OVERLAPPED。 關於重疊IO的知識,請自行GOOGLE相關資料。簡單地說,OVERLAPPED是應用層與核心層交互共享的數據單元,若是要執行一個重疊IO操做,必須帶有OVERLAPPED結構。在完成端口中,它容許應用層對OVERLAPPED結構進行擴展和自定義,容許應用層根據本身的須要在OVERLAPPED的基礎上造成新的擴展OVERLAPPED結構。通常地,擴展的OVERLAPPED結構中,要求放在第一個的數據成員是原OVERLAPPED結構。咱們能夠形如如下方式定義本身的擴展OVERLAPPED結構: typedef struct PER_IO_DATA { OVERLAPPED ovl; WSABUF buf; char RecvDataBuf[ MAX_BUFFER_SIZE ]; //接收緩衝區 char SendDataBuf[ MAX_BUFFER_SIZE ]; //發送緩衝區 OpType opType; //操做類型:發送、接收或關閉等 } 在執行WSASend和WSARecv操做時,應用層會將擴展OVERLAPPED結構的地址傳給核心,核心完成相應的操做後,仍然經過原有的這個結構傳遞操做結果,好比「接收」操做完成後,RecvDataBuf裏存放即是這次接收下來的數據。 根據各自應用的不一樣,不一樣的完成端口設計者可能會設計出不一樣的PER_HANDLE_DATA 和PER_IO_DATA,我這裏給出的設計也只是針對本身的應用場合的,不必定就適合你。但我想,最主要的仍是要搞明白PER_HANDLE_DATA和PER_IO_DATA兩種結構體的含義、用途,以及調用流程。