青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

第 1 樓:細說WinSock IOCP


1.Socket Modes",什么是"Socket I/O Models"?
Socket Modes : Determines how winsock functions behave when called with a socket.
Socket I/O Models : Describes how an application manages and processes I/O on a socket.

2.那么Winsock提供了哪幾種"Socket Modes"給程序員用呢?
  a.Blocking Mode(阻塞式的)
  b.Noblocking Mode(非阻塞式的)

3.詳細解釋一下阻塞和非阻塞.
我們那最常用的send和recv兩個函數來說吧...
比如你調用send函數發送一定的Byte,在系統內部send做的工作其實只是把數據傳輸(Copy)到TCP/IP協議棧的輸出緩沖區,它執行成功并不代表數據已經成功的發送出去了,如果TCP/IP協議棧沒有足夠的可用緩沖區來保存你Copy過來的數據的話...這時候就體現出阻塞和非阻塞的不同之處了:對于阻塞模式的socket send函數將不返回直到系統緩沖區有足夠的空間把你要發送的數據Copy過去以后才返回,而對于阻塞的socket來說send會立即返回WSAEWOULDDBLOCK告訴調用者說:"發送操作被阻塞了!!!你想辦法處理吧..."
對于recv函數,同樣道理,該函數的內部工作機制其實是在等待TCP/IP協議棧的接收緩沖區通知它說:嗨,你的數據來了.對于阻塞模式的socket來說如果TCP/IP協議棧的接收緩沖區沒有通知一個結果給它它就一直不返回:耗費著系統資源....對于阻塞模式的socket該函數會馬上返回,然后告訴你:WSAEWOULDDBLOCK.

3.對于阻塞模式(Blocking Mode)我采取開線程的方式不就搞定了嗎,而且還不用我去判斷基本的收/發什么時候完成,多省心?
  沒錯,這位大蝦說的開線程的方式具體的做法如下:我們將應用劃分成一個讀線程(Reading Thread)一個工作線程(Working Thread),兩個線程*同步對象(Synchronization Object,可以是Event,Mutex...)來共享一個數據緩沖區,讀線程(Reading Thread)負責從網
絡讀取數據并將其放到共享緩沖區中,在適當的時機(比如說讀線程將工作線程需要的數據接收下來之后)觸發一個事件,通知/喚醒工作線程(Working Thread),下面是上述文字的簡單代碼實現:

CRITICAL_SECTION g_data;
HANDLE          g_hEvent;
TCHAR           g_szBuffer[MAX_BUFFER_SIZE];
int               g_nBytes;

void ReadThread(void)
{
    int nTotle = nRead = nLeft = nBytes = 0;

       while(!done)
      {
        nTotal = 0;
         nLeft = NUM_BYTES_REQUIRED; //要讀多少的Bytes

          while( nTotal != NUM_BYTE_REQUIRED)
          {
                // 因為涉及線程和全局變量所以說需要用到關鍵代碼段(Critical Section)
                EnterCriticalSection(&g_data);

               nRead = recv(sock,&(g_szBuffer[MAX_BUFFER_SIZE] - nBytes),nLeft);

                 if( -1 == nread )
                  {
                      printf("socket %d error.\n",WSAGetLasError());
                      ExitThread();
                    }

                    nTotal += nRead;
                      nLeft -= nRead;

                     nBytes += nRead;

                       LeaveCriticalSelection(&g_data);
             }

            // 從網絡接收完指定的(NUM_BYTES_REQUIRED)Bytes之后通知WorkThread啟動
             SetEvent(g_hEvent);
     }
}


void WorkThread(void)
{
   WaitForSingleObject(g_hEvent);

     EnterCriticalSection(&g_data);


    // 主要的處理工作...
    DoSomeWorkOnData(g_szBuffer);

      g_nBytes -= NUM_BYTE_REQUIRED;

     LeaveCriticalSelection(&g_data);
}

  這種方式的一個缺點是:應用程序很難同時通過多個建立好連接的socket通信.當然話說回來我們可以為每一個連接好的socket都開一個Reading Thread and WorkThread,但是呢這會帶來很大的開銷,而且擴展性很差,試想:如果用這樣的模型建立一個服務器端來一個連接開至少兩個線程...天..要是業務負擔很大/連接的socket很多的話怎么辦??所以說別多想了...
   這樣的方式是很不適合做服務器端的.(注意:我說的服務器端是指C/S中的S端邏輯).

   從編寫代碼的角度解讀一下IO中最復雜的IOCP模型

   下面的一段代碼是Microsoft Win32 SDK里面的例子,這是一個Echo服務器程序:

  //
  // 這就不說了,為了使用Winsock2必須包含的頭文件...
  //
#include
#include
#include

#define PORT 5150 // 這個服務器選擇5150端口
#define DATA_BUFSIZE 8192 // 把Buffer定義為8K是一個比較被Microsoft推薦的大小

  //
  // 這個結構體很重要,請一定看清楚,到后面用到的時候別糊涂了
  // 特別注意:把Overlapped放最開頭是有原因的,你應該明確的是
  // 如果有:
  // PER_IO_OPERATION_DATA PerIoData;
  // &PerIoData->Overlapped 和 &PerIoData是一樣的/等價的/
  // 指向同一塊內存區域的.
  //
  typedef struct
  {
   OVERLAPPED Overlapped;
   WSABUF DataBuf;
   CHAR Buffer[DATA_BUFSIZE];
   DWORD BytesSEND; // 下面兩個參數是用來記錄每個connected socket上面的收發的Bytes的
   DWORD BytesRECV;
  } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;

  // 別把這個結構體和上面的結構體混淆了,名字很像的...其實它在這個程序中就相當于是那個和Client端連接的socket
  typedef struct
  {
   SOCKET Socket;
  } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;


  DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);

  void main(void)
  {
   SOCKADDR_IN InternetAddr;
   //
   // 一個listen和一個Accept socket是服務器程序的老套路了...
   //
   SOCKET Listen;
   SOCKET Accept;

   //
   // IO完成端口映射到程序就是一個HANDLE
   //
   HANDLE CompletionPort;
   SYSTEM_INFO SystemInfo;

   //
   // 看清楚,這是指針...不怕你笑話我們處還真有分不清和指針相關的一切概念的人,比如下面的代碼:
   // 我寫了一個DLL,里面有一個這樣的函數:
   // BOOL GetPlayingPosition(DWORD * dwPos);
   // 結果大蝦調用的時候是這樣的....
   // DWORD * p;
   // GetPlayingPostion(p);
   // 它是調用爽了...我呢...:-(
   // 當然了,也不好定義說它就是分不清指針,也許人家就不會寫程序呢..對吧..
   //
   LPPER_HANDLE_DATA PerHandleData;
   LPPER_IO_OPERATION_DATA PerIoData;

   int i;
   DWORD RecvBytes;
   DWORD Flags;
   DWORD ThreadID;
   WSADATA wsaData;
   DWORD Ret;

   // 就像CoInitialize()一樣的把Winsock 2.2服務啟動起來...
   if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
   {
    printf("WSAStartup failed with error %d\n", Ret);
    return;
   }

   // CreateIoCompletionPort有兩個不同的用途:1.創建完成端口;2.把已經創建好的完成端口和一個Socket關聯/綁定起來.
   // 下面這句話是它的第一個用途:創建完成端口
   if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
   {
    printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());
    return;
   }

   //
   // 下面這一些代碼說得是:根據該服務器跑的機器上有幾個CPU建立2*CPU數量的Thread.
   // 這涉及到完成端口工作線程應該創建多少個的問題,在早期(MSDN 2000)Microsoft說
   // 是WorkThreadNums = 2 * CPU + 2,現在的MSDN 2003說是WorkThreadNums = 2 * CPU;
   //
   GetSystemInfo(&SystemInfo);

   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;
    }

    // 這個風格我喜歡,創建完Thread就把人家的Handle給release,省得要是線程異常退出搞得資源泄漏之類的麻煩事情.
    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);

   // 把監聽socket和綁定TCP/IP協議棧做一個綁定...
   if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
   {
    printf("bind() failed with error %d\n", WSAGetLastError());
    return;
   }

   // 設置一次能接受5個連接
   if (listen(Listen, 5) == SOCKET_ERROR)
   {
    printf("listen() failed with error %d\n", WSAGetLastError());
    return;
   }

   //
   // 下面是服務器程序標準的循環了...
   //
   while(TRUE)
   {
    if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
    {
     printf("WSAAccept() failed with error %d\n", WSAGetLastError());
     return;
    }

    //
    // 接收到一個連接就new一個新的socket并承攬下連接進來的socket的接下來的接待/處理工作
    // 為什么不用new呢?我想是因為用Windows提供的內存管理函數,這樣的話在業務量大的情況下
    // 出現內存匱乏的情況下不至于直接就異常了,怎么著也就是該次的內存分配失敗...
    //
    if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL)
    {
     printf("GlobalAlloc() failed with error %d\n", GetLastError());
     return;
    }

    // Associate the accepted socket with the original completion port.

    printf("Socket number %d connected\n", Accept);
    PerHandleData->Socket = Accept;

    //
    // 把accept socket和IO完成端口關聯/綁定起來,并把accept socket傳給IOCP機制這樣的話
    // 當調用GetQueuedCompletionStatus的時候就可以得到該值.
    //
    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 socket上面發起IO請求:WSARecv()以此激發IOCP工作機制..
    // 注意WSARecv的第五個參數,也就是說我這時候告訴WSARecv的不僅僅是
    // PerIoData->Overlapped的地址同時也是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)
   {

    //
    // 這個函數解釋清楚了整個機制也就明白了...
    // 首先我們看CompletionPort應該沒有問題,是從創建線程的主線程傳過來的.
    // BytesTransferred指明了這次IO傳輸了多少Bytes...
    // PerHandleData是CreateIoCompletionPort的時候穿過來的,MSDN里面叫CompletionKey.
    // 請注意PerIoData這個參數,本來按照MSDN的解釋如下:
    // "Pointer to a variable that receives the address of the OVERLAPPED structure that was
    // specified when the completed I/O operation was started."
    // 其實在這這個lpOverlapped傳過來的就是上面WSARecv傳入的overlapped的地址...
    // 這下明白了吧:主線程借著overlapped的名聲傳了一個結構體指針過來..:-)
    //
    if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
     (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
    {
     printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
     return 0;
    }


    //
    // First check to see if an error has occured on the socket and if so
    // then close the socket and cleanup the SOCKET_INFORMATION structure
    // associated with the socket.
    //

    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;
    }

    //
    // Check to see if the BytesRECV field equals zero. If this is so, then
    // this means a WSARecv call just completed so update the BytesRECV field
    // with the BytesTransferred value from the completed WSARecv() call.
    //

    if (PerIoData->BytesRECV == 0)
    {
     PerIoData->BytesRECV = BytesTransferred;
     PerIoData->BytesSEND = 0;
    }
    else
    {
     PerIoData->BytesSEND += BytesTransferred;
    }

    if (PerIoData->BytesRECV > PerIoData->BytesSEND)
    {

     //
     // Post another WSASend() request.
     // Since WSASend() is not gauranteed to send all of the bytes requested,
     // continue posting WSASend() calls until all received bytes are sent.
     //
     ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

     PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
     PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

     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;

     // Now that there are no more bytes to send post another WSARecv() request.
     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 2007-10-09 11:57 聶文龍 閱讀(1738) 評論(0)  編輯 收藏 引用 所屬分類: net work
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产精品高精视频免费| 久久亚洲综合色| 国产麻豆综合| 国产精品久久亚洲7777| 国产精品五月天| 国产一区在线播放| 激情久久久久久久| 最近看过的日韩成人| 国产精品99久久久久久宅男| 亚洲视频中文| 欧美一区二区成人| 久久色在线观看| 欧美国内亚洲| 一区二区三区蜜桃网| 欧美伊人精品成人久久综合97| 久久精品国产清高在天天线 | 久久综合九九| 欧美日韩精品二区第二页| 国产精品一区二区黑丝| 激情欧美一区二区| 日韩网站在线看片你懂的| 午夜精品福利电影| 欧美电影免费观看网站| 亚洲夜间福利| 欧美a级一区| 久久这里有精品视频| 欧美三级午夜理伦三级中文幕| 国产欧美一区二区三区在线老狼| 国产一区二区丝袜高跟鞋图片| 亚洲精品一区二区三区在线观看| 欧美在线一区二区| 91久久极品少妇xxxxⅹ软件| 亚洲视频综合| 欧美激情一区二区三区在线| 国产专区欧美专区| 亚洲男人第一av网站| 欧美激情精品久久久久久| 午夜亚洲影视| 国产精品高精视频免费| 亚洲精品在线观| 久久婷婷成人综合色| 亚洲在线1234| 欧美三级日本三级少妇99| 亚洲国产精品123| 久久久久久久欧美精品| 亚洲永久精品国产| 欧美三级电影大全| 99re国产精品| 亚洲国产精品久久人人爱蜜臀| 欧美一区激情视频在线观看| 欧美揉bbbbb揉bbbbb| 99精品久久免费看蜜臀剧情介绍| 欧美粗暴jizz性欧美20| 久久精品毛片| 极品尤物av久久免费看| 久久精品99国产精品酒店日本| 亚洲深夜福利网站| 国产精品久久久对白| 亚洲一区二区在线免费观看视频| 亚洲激情六月丁香| 欧美精品国产精品| 日韩网站免费观看| 亚洲日本国产| 欧美日韩综合在线免费观看| 99xxxx成人网| 夜夜嗨av一区二区三区免费区| 欧美日本簧片| 一区二区精品国产| 一区二区国产精品| 国产欧美日韩视频一区二区三区 | 国产又爽又黄的激情精品视频| 亚洲欧美日韩国产综合精品二区| 一本色道久久加勒比精品| 欧美性色综合| 久久精品国产一区二区三| 欧美在线影院| 亚洲人成精品久久久久| 日韩视频在线免费观看| 国产精品视频观看| 久久久精品网| 美女国产一区| 亚洲一区二区动漫| 欧美有码在线视频| 欧美wwwwww| 欧美成人性网| 亚洲在线一区二区| 午夜欧美不卡精品aaaaa| 雨宫琴音一区二区在线| 亚洲福利在线看| 欧美午夜影院| 老**午夜毛片一区二区三区| 欧美理论片在线观看| 久久国产精彩视频| 欧美不卡视频一区发布| 亚洲欧美激情精品一区二区| 欧美一区二区久久久| 亚洲精品久久久久久久久久久| 一本色道久久综合亚洲精品小说| 国内外成人免费激情在线视频| 亚洲激情婷婷| 国产日韩精品一区二区三区在线| 亚洲第一成人在线| 国产一区二区三区黄| 日韩图片一区| 亚洲国产精品福利| 欧美亚洲尤物久久| 在线一区亚洲| 另类亚洲自拍| 久久精品国产综合精品| 欧美日韩www| 欧美成人国产| 国产小视频国产精品| 中文在线资源观看网站视频免费不卡 | 亚洲精品乱码久久久久久久久| 国产精品午夜久久| 亚洲日本成人在线观看| 亚洲成人在线网| 欧美一级网站| 亚洲欧美亚洲| 欧美日韩精品久久久| 欧美激情片在线观看| 韩国欧美一区| 欧美在线在线| 久久精品二区亚洲w码| 国产精品萝li| 日韩午夜在线电影| 99精品国产在热久久| 欧美另类极品videosbest最新版本 | 久久精品国产v日韩v亚洲| 欧美日韩专区| 99亚洲一区二区| 一区二区三区|亚洲午夜| 欧美韩日一区二区| 亚洲国产美女| 亚洲美女福利视频网站| 欧美ed2k| 亚洲日本电影在线| 亚洲少妇诱惑| 国产精品h在线观看| 一区二区三区高清在线| 亚洲免费视频一区二区| 国产精品一卡| 欧美一区亚洲| 欧美成熟视频| 亚洲人在线视频| 亚洲日本视频| 欧美国产日韩在线观看| 亚洲国产一区二区精品专区| 亚洲人成毛片在线播放女女| 欧美va亚洲va香蕉在线| 91久久香蕉国产日韩欧美9色| 亚洲人体一区| 欧美手机在线视频| 亚洲欧美清纯在线制服| 久久国产精品第一页| 韩国v欧美v日本v亚洲v| 美女脱光内衣内裤视频久久影院| 欧美激情第一页xxx| 99精品欧美| 国产日韩精品一区二区浪潮av| 欧美中文在线视频| 亚洲国产精品精华液网站| 亚洲淫片在线视频| 国产主播一区二区三区| 欧美极品欧美精品欧美视频| 亚洲先锋成人| 免费永久网站黄欧美| 夜色激情一区二区| 国产欧美日韩综合一区在线播放| 久久夜色精品国产欧美乱| 亚洲精品免费在线| 久久精品一区二区三区四区 | 亚洲精品小视频| 国产精品人人做人人爽| 久久综合亚洲社区| 亚洲一区图片| 亚洲国产一区二区三区青草影视| 午夜在线电影亚洲一区| 亚洲国产视频直播| 国产乱码精品一区二区三区五月婷| 裸体丰满少妇做受久久99精品| 亚洲视频第一页| 亚洲国产精品久久人人爱蜜臀 | 亚洲国产一区二区视频| 国产精品毛片a∨一区二区三区| 久久综合久久久久88| 亚洲综合日韩在线| 亚洲美女中文字幕| 欧美承认网站| 久久久久久久久久码影片| 亚洲小说春色综合另类电影| 在线日韩欧美视频| 国产视频观看一区| 国产精品成人免费精品自在线观看| 老司机精品导航| 久久gogo国模啪啪人体图| 亚洲永久字幕| 亚洲伊人一本大道中文字幕| 亚洲日本国产| 最新中文字幕一区二区三区|