• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            kenlistian

            厚積薄發. 勤為槳,思為帆

               :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理 ::
              73 隨筆 :: 4 文章 :: 22 評論 :: 0 Trackbacks

            來自微軟的完成端口例子,就講解一下它的使用套路吧
            反正編程這個玩意,只要用過,自然就知道什么回事,一次不會再看一次,學習這個玩意,無他,勤奮而已。
            奢談效率等等,那只是孰能生巧上的功夫。
             

              這個例子是在console下的例子,算是一個echo服務器吧,
              跑起來后將在5150端口監聽,一旦有個端口連接上來,發個數據給服務端口,它就echo回數據給那個端口. 直到那個連接中斷.
             
             完成端口,其實理解成一個通道或管子就可以了,和管道也差不了多少,不過可以實現異步處理罷了,
             你這邊往管子里丟數據,通過GetQueuedCompletionStatus來查管子那頭出數據沒,出了就處理,這個管子就是通過一個自定義有點特殊的結構來寫入或讀出數據而已.
             那個完成端口,其實就相當是標識那個數據塊的句柄,

            //下面請看例子
            #include <winsock2.h>
            #include <windows.h>
            #include <stdio.h>

            #define PORT 5150
            #define DATA_BUFSIZE 8192

            #pragma comment(lib, "Ws2_32")

            typedef struct                        //這個玩意就是灌數據,取數據的一個自定義數據結構

                                                          //和那個wm_data差不了多少,不過就是老要塞一個OverLapped結構,
            {
               OVERLAPPED Overlapped;
               WSABUF DataBuf;
               CHAR Buffer[DATA_BUFSIZE];                    
               DWORD BytesSEND;                                 //發送字節數
               DWORD BytesRECV;                                
            } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


            typedef struct
            {
               SOCKET Socket;
            } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;


            DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);


            void main(void)
            {
               SOCKADDR_IN InternetAddr;
               SOCKET Listen;
               SOCKET Accept;
               HANDLE CompletionPort;
               SYSTEM_INFO SystemInfo;
               LPPER_HANDLE_DATA PerHandleData;
               LPPER_IO_OPERATION_DATA PerIoData;
               int i;
               DWORD RecvBytes;
               DWORD Flags;
               DWORD ThreadID;
               WSADATA wsaData;
               DWORD Ret;

               if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
               {
                  printf("WSAStartup failed with error %d\n", Ret);
                  return;
               }

               //
               //完成端口的建立得搞2次,這是第一次調用,至于為什么?我問問你
               //
               if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
               {
                  printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());
                  return;
               }
               //老套子api,不談也罷
               GetSystemInfo(&SystemInfo);
              
               //發現2個CPU,那就開個雙倍的線程跑吧
               for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
               {
                  HANDLE ThreadHandle;
                 
                  //
                  //完成端口掛到線程上面來了,就像管子把灌數據的和讀數據的兩頭都連上了,           
                 //
                  if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,
                     0, &ThreadID)) == NULL)
                  {
                     printf("CreateThread() failed with error %d\n", GetLastError());
                     return;
                  }     
                  CloseHandle(ThreadHandle);
               }

               //
               //啟動一個監聽socket ,以下都是長長的交代
               //
               if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
                  WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
               {
                  printf("WSASocket() failed with error %d\n", WSAGetLastError());
                  return;
               }

               InternetAddr.sin_family = AF_INET;
               InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
               InternetAddr.sin_port = htons(PORT);

               if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
               {
                  printf("bind() failed with error %d\n", WSAGetLastError());
                  return;
               }  

               if (listen(Listen, 5) == SOCKET_ERROR)
               {
                  printf("listen() failed with error %d\n", WSAGetLastError());
                  return;
               }

               //
               // 監聽端口打開,就開始在這里循環,一有socket連上,WSAAccept就創建一個socket,
               // 這個socket 又和完成端口聯上,
               //
               // 嘿嘿,完成端口第二次調用那個createxxx函數,為什么,留給人思考思考可能更深刻,
               // 反正這套路得來2次,
               // 完成端口completionport和accept socket掛起來了,
               //
               while(TRUE)
               {

                //主線程跑到這里就等啊等啊,但是線程卻開工了,
                  if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
                  {
                     printf("WSAAccept() failed with error %d\n", WSAGetLastError());
                     return;
                  }
                 
                  if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL)
                  {
                     printf("GlobalAlloc() failed with error %d\n", GetLastError());
                     return;
                  }     
                 
                  PerHandleData->Socket = Accept;
                 
                  //
                 //把這頭和完成端口completionPort連起來
                 //就像你把漏斗接到管子口上,開始要灌數據了
                 //
                  if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
                     0) == NULL)
                  {
                     printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
                     return;
                  }
                 
                  //
                  //清管子的數據結構,準備往里面灌數據
                  //
                  if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) == NULL)
                  {
                     printf("GlobalAlloc() failed with error %d\n", GetLastError());
                     return;
                  }

                  ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
                  PerIoData->BytesSEND = 0;
                  PerIoData->BytesRECV = 0;
                  PerIoData->DataBuf.len = DATA_BUFSIZE;
                  PerIoData->DataBuf.buf = PerIoData->Buffer;

                  Flags = 0;
                 
                  //
                  //  accept接到了數據,就放到PerIoData中,而perIoData又通過線程中的函數取出,
                 //
                  if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
                     &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                  {
                     if (WSAGetLastError() != ERROR_IO_PENDING)
                     {
                        printf("WSARecv() failed with error %d\n", WSAGetLastError());
                        return;
                     }
                  }
               }
            }

            //
            //線程一但調用,就老在里面循環,
            // 注意,傳入的可是完成端口啊,就是靠它去取出管子中的數據
            //
            DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
            {
               HANDLE CompletionPort = (HANDLE) CompletionPortID;
              
               DWORD BytesTransferred;
               LPOVERLAPPED Overlapped;
               LPPER_HANDLE_DATA PerHandleData;
               LPPER_IO_OPERATION_DATA PerIoData;        
               DWORD SendBytes, RecvBytes;
               DWORD Flags;
             
               while(TRUE)
               {
                  //
                  //在這里檢查完成端口部分的數據buf區,數據來了嗎?
                  // 這個函數參數要看說明,
                  // PerIoData 就是從管子流出來的數據,
                  //PerHandleData 也是從管子里取出的,是何時塞進來的,
                 //就是在建立第2次createIocompletionPort時
                // 

                  if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
                     (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
                  {
                     printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
                     return 0;
                  }

                  // 檢查數據傳送完了嗎
                  if (BytesTransferred == 0)
                  {
                     printf("Closing socket %d\n", PerHandleData->Socket);

                     if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
                     {
                        printf("closesocket() failed with error %d\n", WSAGetLastError());
                        return 0;
                     }

                     GlobalFree(PerHandleData);
                     GlobalFree(PerIoData);
                     continue;
                  }    
                 //
                //看看管子里面有數據來了嗎?=0,那是剛收到數據
                //
                  if (PerIoData->BytesRECV == 0)
                  {
                     PerIoData->BytesRECV = BytesTransferred;
                     PerIoData->BytesSEND = 0;
                  }
                  else   //來了,
                  {
                     PerIoData->BytesSEND += BytesTransferred;
                  }
              
                  //
                  // 數據沒發完?繼續send出去
                 //
                 if (PerIoData->BytesRECV > PerIoData->BytesSEND)
                  {

                     ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); //清0為發送準備
                     PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
                     PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

                   //1個字節一個字節發送發送數據出去
                     if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
                        &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                     {
                        if (WSAGetLastError() != ERROR_IO_PENDING)
                        {
                           printf("WSASend() failed with error %d\n", WSAGetLastError());
                           return 0;
                        }
                     }
                  }
                  else
                  {
                     PerIoData->BytesRECV = 0;

                     Flags = 0;
                     ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

                     PerIoData->DataBuf.len = DATA_BUFSIZE;
                     PerIoData->DataBuf.buf = PerIoData->Buffer;

                     if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
                        &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                     {
                        if (WSAGetLastError() != ERROR_IO_PENDING)
                        {
                           printf("WSARecv() failed with error %d\n", WSAGetLastError());
                           return 0;
                        }
                     }
                  }
               }
            }

            posted on 2006-05-25 22:27 kenlistian 閱讀(2460) 評論(0)  編輯 收藏 引用
            伊人色综合九久久天天蜜桃| 99久久人妻无码精品系列| 久久99国产精品成人欧美| 精品久久人人妻人人做精品| 精品久久久久久久国产潘金莲| 国产精品久久久久久久人人看| 国内精品久久人妻互换 | 久久久久久亚洲精品无码| 欧美久久一级内射wwwwww.| 久久综合亚洲欧美成人| 久久久久久国产精品免费免费 | 久久精品国产清自在天天线| 久久国产精品-久久精品| 久久天天躁夜夜躁狠狠躁2022| 丁香久久婷婷国产午夜视频| 亚洲精品白浆高清久久久久久| 久久久这里有精品中文字幕| 91精品国产高清91久久久久久| 久久午夜无码鲁丝片秋霞| 一本大道加勒比久久综合| 狠狠色噜噜色狠狠狠综合久久| 国产精品激情综合久久| 国产日产久久高清欧美一区| 久久久无码精品亚洲日韩按摩 | 久久亚洲精品无码播放| 99久久99久久久精品齐齐| 无码日韩人妻精品久久蜜桃| 香蕉久久永久视频| 久久久这里有精品中文字幕| 99久久精品费精品国产| 国产69精品久久久久9999| 国产美女久久久| A狠狠久久蜜臀婷色中文网| 久久久一本精品99久久精品66 | 国产福利电影一区二区三区,免费久久久久久久精 | 国产精品美女久久久m| 久久精品无码专区免费东京热 | 色婷婷久久综合中文久久蜜桃av| 无码人妻少妇久久中文字幕| 欧美性大战久久久久久| 午夜精品久久久久久影视riav|