Windows完成連接埠 IOCP模型(二)

來源:互聯網
上載者:User

標籤:err   未來   success   釋放記憶體   null   eof   wmi   一起   監聽事件   

1詳解完成連接埠基本使用

1建立完成連接埠

HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

參數其實就是-1,0,0,0. 最後一個參數代表的就是

NumberOfConcurrentThreads,就是允許應用同時執行的線程數量,

未來避免環境切換,就是說讓每個CPU只允許一個線程,設定為0

就是有多少處理器,就有多少背景工作執行緒。

原因就是如果一台機器有兩個CPU(兩核),如果讓系統同時啟動並執行

線程,多於本機CPU數量的話,就沒什麼意義,會浪費CPU寶貴周期,

降低效率,得不償失。

然後會返回一個HANDLE 只要不是NULL就是建立完成連接埠成功。



2建立Socket綁定偵聽 不多說

SOCKET lo_sock = INVALID_SOCKET;//建立失敗if (iocp == NULL){goto failed;}//建立一個線程  把IOCP傳到線程函數裡h_threadS = CreateThread(NULL, 0, ServerThread, (LPVOID)iocp, 0, 0);// 防止記憶體泄露CloseHandle(h_threadS);//end//建立socketlo_sock = socket(AF_INET,SOCK_STREAM,0);if (lo_sock == INVALID_SOCKET){goto failed;}struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(port);addr.sin_family = AF_INET;int ret = bind(lo_sock, (const struct sockaddr*)&addr, sizeof(addr));if (ret != 0){printf("bind %s:%d error \n", "127.0.0.1", port);goto failed;}printf("bind %s:%d success \n", "127.0.0.1", port);printf("starting listener on %d\n", port);// SOMAXCONN 通過listen指定最大隊列長度ret = listen(lo_sock, SOMAXCONN);if (ret != 0){printf("listening on port failed\n");goto failed;}printf("listening on success\n");




3在主線程裡面偵聽accept

struct sockaddr_in c_addr;int len = sizeof(c_addr);//沒有client接入進來,線程會掛起  也就是阻塞int client_fd = accept(lo_sock, (struct sockaddr*)&c_addr, &len);if (client_fd != INVALID_SOCKET){        //這裡就是有新的socket串連了  printf("new client %s:%d coming\n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));    }    else{continue;}//儲存會話資訊  struct session* s = save_session(client_fd, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));將資訊儲存在一個存使用者ip port  連接埠的結構體裡面  這個結構體是這樣的:/* 這個結構中定義struct  session{char c_ip[32]; //ip地址int c_port;  //連接埠int c_sock;  //socket控制代碼int removed;//刪除標記struct  session * _next; //鏈表指標};*/



4然後把獲得的用戶端socket綁定到iocp  

這段代碼是在一個while(1)死迴圈裡進行

先介紹下這個函數 和建立完成連接埠用的是一個API

HANDLE WINAPI CreateIoCompletionPort(__in  HANDLE FileHandle,  //這裡就是客戶連入的socket__in_opt HANDLE ExistingCompletionPort,//就是前面建立的完成連接埠,__in ULONG_PRT CompletionKey,//這個參數可以傳遞一個結構體,自訂的結構體                               //你只要把這個結構體傳入,背景工作執行緒就可以取出來,                               // 我使用的是上面我定義的 結構體                              _in  DWORD DWORD NumberOfConcurrenThreads//上面說了,設定為0就行);//添加到這個完成連接埠CreateIoCompletionPort((HANDLE)client_fd, iocp,(DWORD)s, 0);client_fd 就是上面或得的用戶端socket然後iocp完成連接埠,  s就是帶有用戶端工作階段資訊的結構體


5投遞一個非同步recv請求

(就是告訴完成連接埠,如果我這個用戶端有包過,你要接收完成,然後告訴我)

在這之前就要定義一個結構體作為標誌,因為啟動的時候投遞了很多的

I/O請求,要用一個標誌來綁定每一個I/O操作,這樣網路操作完成後,

在通過這個標誌找到這組返回的資料:

一定要將WSAOVERLAPPED放第一個,其他的隨意


//緩衝區大小#define MAX_RECV_SIZE 8092struct io_package{WSAOVERLAPPED overlapped;  //重疊I/O網路操作都要用到這個 重疊結構int opt;                    //標記請求的類型int pkg_size;               //包的長度WSABUF wsabuffer;           //儲存資料的緩衝區,用來給重疊操作傳遞資料的char pkg[MAX_RECV_SIZE];   //對應WSABUF裡的緩衝區};//監聽事件   用來標記請求的類型enum{IOCP_ACCEPT = 0,IOCP_RECV,IOCP_WRITE,};

WSARecv函數

int WSARecv(SOCKET s,//當然是投遞這個操作的通訊端  LPWSABUF lpBuffers,            // 接收緩衝區DWORD dwBufferCount,           // 數組中WSABUF結構的數量,設定為1即可LPDWORD lpNumberOfBytesRecvd,  // 如果接收操作立即完成,這裡會返回函數調用所接收到的位元組數LPDWORD lpFlags,               // 設定為0  LPWSAOVERLAPPED lpOverlapped,  // 這個Socket對應的重疊結構  lpCompletionRoutine            //這個參數只有完成常式模式才會用到,  )WSA_IO_PENDING:最常見的傳回值,說明WSARecv成功了, 但是I/O操作沒完成

投遞這個請求

struct io_package* io_data = malloc(sizeof(struct io_package));//只需要清空一次,即可  就是為了 讓重疊結構清空memset(io_data, 0, sizeof(struct io_package));io_data->wsabuffer.buf = io_data->pkg;io_data->wsabuffer.len = MAX_RECV_SIZE - 1;io_data->opt = IOCP_RECV; //標記請求類型   我們設定成接收DWORD dwFlags = 0;//............WSARecv(client_fd, &io_data->wsabuffer, 1, NULL,&dwFlags, &io_data->overlapped, NULL);


5在背景工作執行緒裡等待完成事件

GetQueuedCompletionStatus函數原型,是背景工作執行緒裡要

用到的API,他一旦進入,背景工作執行緒就會被掛起,知道

完成連接埠上出現了完成的事件。或網路逾時

那麼這個線程會被立刻喚醒,執行後續代碼

BOOL WINAPI GetQueuedCompletionStatus(__in   HANDLE          CompletionPort,    // 這個就是我們建立的那個唯一的完成連接埠  __out  LPDWORD         lpNumberOfBytes,   //這個是操作完成後返回的位元組數__out  PULONG_PTR      lpCompletionKey,   // 這個是建立完成連接埠的時候綁定的那個自訂結構體參__out  LPOVERLAPPED    *lpOverlapped,     // 這個是在連入Socket的時候一起建立的那個重疊結構 __in   DWORD           dwMilliseconds     // 等待完成連接埠的逾時時間,WSA_INFINITE是等待有事件才返回


看下這個代碼操作

//線程函數static DWORD WINAPI ServerThread(LPVOID lParam){//擷取完成連接埠HANDLE iocp = (HANDLE)lParam;//返回的位元組數DWORD dwTrans;//帶有socket控制代碼的結構體 因為之前是添加進去 這個函數可以取出struct session* s;//帶有重疊結構的結構體struct io_package* io_data;//等待IOCPwhile (1){    s = NULL;dwTrans = 0;io_data = NULL;//調用這個API 等待事件int ret = GetQueuedCompletionStatus(iocp, &dwTrans, (LPDWORD)&s, (LPOVERLAPPED*)&io_data, WSA_INFINITE);if (ret == 0){printf("iocp error");//IOCP連接埠發生錯誤continue;}//來告訴所有使用者socket的完成事件發生了printf("IOCP have event\n");//接收的位元組==0 表示用戶端中斷連線if (dwTrans == 0){//socket關閉了        closesocket(s->c_sock);        //釋放記憶體free(io_data); continue;}//到這裡意味著資料以及讀取到//這裡就是前面標記的事件類型switch (io_data->opt){case IOCP_RECV:{ // 接收資料以及完成了io_data->pkg[dwTrans] = 0;printf("IOCP %d: recv %d,%s\n",s->c_port,dwTrans,io_data->pkg);//當讀的請求完成後, 必須再投遞一個讀的請求DWORD dwFlags = 0;int ret = WSARecv(s->c_sock, &io_data->wsabuffer, 1, NULL, &dwFlags, &io_data->overlapped, NULL);}break;case IOCP_WRITE:{}break;case IOCP_ACCEPT:{}break;default:break;}}return 0;}

到這裡其實就完成了這個IOCP的使用,後面還會補充的。







Windows完成連接埠 IOCP模型(二)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.