1. 程式人生 > >WinSocket模型的探討——Overlapped模型

WinSocket模型的探討——Overlapped模型

重疊模型是Windows裡一種重要的 I/O 模型,可以有效率的實現一些 I/O 操作,譬如檔案讀寫、Socket讀寫等,在這裡我們一起來研究一下重疊模型,看看它究竟為何方神聖。

這篇文章分為以下幾部分來說明:

  1. 重疊模型的概念
  2. 容易碰到的一些問題
  3. 重疊模型的基本程式設計方法(accept 和 AcceptEx )
  4. 突破64個事件的等待限制
  5. 例程

好了,下面就讓我們一起來學習一下重疊模型

1、概念

對於重疊模型的概念,大家都各有說法,以我自己的角度來說,我覺得重疊其實就是一種非同步處理的說法,一邊向socket 投遞操作,而在另一邊等待結果的完成,兩邊互不相干,我想這就是重疊的概念。其實這個概念也不用深究,我們還是來看看如何使用重疊

模型

2、容易碰到的一些問題

為什麼對Socket投遞第一個WSARecv()出錯並且返回錯誤為10045?

這個問題都是因為這個 WSARecv 的lpFlags引數引起的,這個引數一定要設定為0,而不是直接寫為0,
 如果直接寫為0,則會出現10014錯誤。要這樣寫:

DWORD dwFlags=0;
WSARecv(sClient, &as.m_wsaBuf, as.m_wsaBuf.len, &dwRecvBytes, &dwFlags, &as.m_Overlapped, NULL);

為什麼找不到AccepctEx的宣告和定義?

要使用 AcceptEx,必須加上下面兩句

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

AcceptEx 的第四個引數有什麼用?

  AcceptEx 的第四個引數,指定建立連線的時候,接收客戶端傳過來的多少位元組的資料,可以用來做驗證客戶端的字串,如果這個引數為0,則AcceptEx在接受客戶端連線後會立刻完成,不會接受任何客戶端的資料。如果不為0,則接收客戶端傳過來的指定位元組的資料。但這樣會出現惡意連線連進來的時候,不傳送任何資料,就導致了SERVER出錯。

為什麼程式只能收到連線,收不到從Socket傳過來的資料?

程式不停的收到連線,但傳過來的資料都不顯示,這個問題的解決方法是:把事件陣列的第一個設為和偵聽Socket (sListen) 對應的事件,就是說第一個事件是給偵聽Socket的,那在接受到連線後,要重新設定一下第一個事件:WSASetEvent(EventArray[0]); 以便工作執行緒可以繼續往下走去服務其他事件。這裡只知道要這樣做,但具體原因不太清楚,希望高人能指點一下!

程式接收到的資料在哪裡?

在使用WSARecv的時候,我們就已經傳了一個WSABUF型別的變數進去,那在後面呼叫WSAGetOverlappedResult 之後,之前的WSABUF中的buf就是取得資料的地方,這些更仔細的說明建議大家看看Piggy的《手把手教你寫 Overlapped 模型》,這篇文章對函式進行了比較詳細的介紹。

3、基本程式設計方法:使用重疊模型,就要注意以下的一些函式。

WSASocket、AcceptEx、WSACreateEvent、WSAWaitForMultipleEvents、WSAResetEvent、WSAGetOverlappedResult、WSARecv、WSASetEvent。具體使用方法各位可以看下面的例子或者查一下MSDN,裡面有介紹。

首先我們先看看重疊模型的使用步驟:

  • 建立一個帶Overlapped標誌的Socket控制代碼(其實也可以是檔案控制代碼);
  • 準備好一個與這個Socket控制代碼對應的Overlapped物件,並準備好事件控制代碼,以便讓後面的WaitForXXX函式使用
  • 使用支援Overlapped的函式對上面的控制代碼作操作(向這個控制代碼投遞請求),這些函式都有一個共同點,就是它們都有個引數是Overlapped型別的。例如AcceptEx、WriteFile、WSARecv等;
  • 在一個迴圈或者一個執行緒中使用WSAWaitForMultipleEvents來等待事件的發生;
  • 事件發生後,使用WSAGetOverlappedResult 來取得對應的結果並做處理。
  • 繼續向控制代碼投遞操作請求(向它發東西啊,收東西啊等等的!)。

到這裡,給大家一個建議,對著上面的步驟寫寫練習,要看程式碼網上一堆,真要理解,還是得親自動手才行,程式設計技術,動手是硬道理!

好,我們一步步來看看程式碼片斷,對上面的步驟有點理性的認識才行。

建立帶Overlapped標誌的Socket控制代碼:

 SOCKET sListen;
 if((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped)) == INVALID_SOCKET)
 {
  OutErr("WSASocket error!");  
  return;
 }

準備好一個Overlapped物件,還有hEvent事件:

 // 準備好一個給 listenSocket 使用的 Overlapped
 ZeroMemory(&ListenOverlapped, sizeof(Overlapped));
 ListenOverlapped.hEvent = WSACreateEvent();

對這個控制代碼投遞操作,這是一個偵聽控制代碼,當然就是給他投遞AcceptEx操作:

 AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, 0,
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
    &ListenOverlapped);

在一個迴圈或者執行緒中等待事件的完成,並取得結果做處理:

 while(TRUE)
 {
  // 等待偵聽事件的發生
  nIdx = WSAWaitForMultipleEvents(1, &ListenOverlapped.hEvent, FALSE, INFINITE, FALSE);

  // 取得這次Overlapped的結果
  WSAGetOverlappedResult(sListen, &ListenOverlapped, &dwTransferred, FALSE, &dwFlags);

  cout << sClient << " 連線上來了!" << endl;
  cout << "連線總數為:" << ++g_nClientCnt << endl;
  // 重置一下事件
  WSAResetEvent(ListenOverlapped.hEvent);

  // 再重新向ListenSocket投遞一個AcceptEx請求
  sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped);
  ZeroMemory(szAcceptBuffer, 2*(sizeof(SOCKADDR_IN) + 16) );
  AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, dwRecvData,
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
    &ListenOverlapped);
 }

上面幾步,基本上實現了AcceptEx的接受連線的整個過程,如果你都能看懂,那下面的例子程式也難不倒你了!

 4、突破64個事件的等待限制

雖說使用完成埠可以不受64個事件的限制,但我們這裡既然是抱著鑽研的態度,那總是要試一試的。

64個事件的等待限制是由Windows規定的,如果超過了64個,則WSAWaitForMultipleEvents函式會報錯,錯誤碼為87。要突破此限制的最基本的思路就是當執行緒等待的事件數超過了64,則新開執行緒來等待,把事件以64個為一個區,則0區、1區、2區。。。每區有64個事件,每個執行緒服務於一個區。這裡要注意的是每個執行緒中的WaitForMultipleEvents要小心事件的個數、事件的起始位。詳細的可以看下面的例子。

5、例程,下面的程式都是一個cpp檔案拷貝過去就可以直接編譯了。

 /*

  Server1: 使用 accept 來得到連線,並使用ProcessIO來處理結果,這裡使用64個事件為一區,一個執行緒服務一個區的方法來突破了
  64個事件的限制。

*/

#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

#include <iostream>
using namespace std;

#define PORT 5050
#define DATA_BUFSIZE 8192
#define DYL_MAXIMUM_WAIT_EVENTS 1024

#define OutErr(a) cout << (a) << endl
      << "出錯程式碼:" << WSAGetLastError() << endl
      << "出錯檔案:" << __FILE__ << endl  
      << "出錯行數:" << __LINE__ << endl

#define OutMsg(a) cout << (a) << endl;

void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 
}

SOCKET BindServer(int nPort)
{
 // 建立socket
 SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped);

 // 繫結埠
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
 {
  OutErr("bind Failed!");
  return NULL;
 }

 // 設定監聽佇列為200
 if(listen(sServer, WSA_MAXIMUM_WAIT_EVENTS) != 0)
 {
  OutErr("listen Failed!");
  return NULL;
 }
 return sServer;
}


DWORD EventTotal = 0;

WSAEVENT EventArray[DYL_MAXIMUM_WAIT_EVENTS];

typedef struct _SOCKET_INFORMATION {
   CHAR Buffer[DATA_BUFSIZE];
   WSABUF DataBuf;
   SOCKET Socket;
   WSAOverlapped Overlapped;
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

LPSOCKET_INFORMATION SocketInfoArray[DYL_MAXIMUM_WAIT_EVENTS];

void CreateSocketInfo(SOCKET sClient)
{
 //if(EventTotal%WSA_MAXIMUM_WAIT_EVENTS)
 // EventTotal++;
 SocketInfoArray[EventTotal] = new SOCKET_INFORMATION();
 SocketInfoArray[EventTotal]->Socket = sClient;
 ZeroMemory(&SocketInfoArray[EventTotal]->Overlapped, sizeof(WSAOverlapped));
 SocketInfoArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent();
 SocketInfoArray[EventTotal]->DataBuf.buf = SocketInfoArray[EventTotal]->Buffer;
 SocketInfoArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;

 memset(SocketInfoArray[EventTotal]->Buffer, 0, DATA_BUFSIZE);

 EventTotal++;
}

// 釋放,注意N從0開始
void DestroySocketInfo(int n)
{
 delete SocketInfoArray[n];
 if( (n+1) != DYL_MAXIMUM_WAIT_EVENTS )
 {
  for(int i = n; i < EventTotal; i++)
  {
   SocketInfoArray[i] = SocketInfoArray[i + 1];
  }
 }
 EventTotal--;
}

DWORD WINAPI ProcessIO(LPVOID lpParam)
{
 int nSec = *((int*)lpParam);
 int nIdx = 0;
 DWORD BytesTransferred;
 DWORD Flags;
 int nTriggerCnt;

 while(1)
 {
  // 計算好事件數
  int nEvtCnt;
  if( EventTotal % WSA_MAXIMUM_WAIT_EVENTS == 0)
   nEvtCnt = 64;
  else
   nEvtCnt = EventTotal % WSA_MAXIMUM_WAIT_EVENTS;

  // 計算好事件陣列的起始位置
  WSAEVENT *evtStart = EventArray + nSec*WSA_MAXIMUM_WAIT_EVENTS;
  if( (nIdx = WSAWaitForMultipleEvents(nEvtCnt, evtStart, FALSE, WSA_INFINITE, FALSE) ) == WSA_WAIT_FAILED )
  {
   OutErr("WSAWaitForMultipleEvents failed!");
   return 0;
  }

  nTriggerCnt = nIdx - WSA_WAIT_EVENT_0;

  //!! 如果是0,則說明有新的客戶要連線進來
  if ( nTriggerCnt == 0 )
  
  
  nTriggerCnt = nTriggerCnt + nSec*WSA_MAXIMUM_WAIT_EVENTS;
  LPSOCKET_INFORMATION SI = SocketInfoArray[nTriggerCnt];
  WSAResetEvent(EventArray[nTriggerCnt]);
  
  // 取得重疊操作的結果
  if(WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE || BytesTransferred == 0)
  {
   //OutErr("WSAGetOverlappedResult failed!");
   cout << "socket " << SI->Socket << " 斷開!" << endl;
   DestroySocketInfo(nTriggerCnt);

   continue;
  }

  cout << SI->Socket << " 傳送過來的資料:"  << SI->Buffer << endl;

  // 繼續等待接收資料
  DWORD dwRecv;
  DWORD dwFlags = 0;

  if(WSARecv(SI->Socket, &(SI->DataBuf), 1, &dwRecv, &dwFlags, &(SI->Overlapped), NULL)  == SOCKET_ERROR)
  {
   if (WSAGetLastError() != ERROR_IO_PENDING)
   {
      OutErr("WSARecv error!");
      return 0;
   }   
  }
 }

 cout << "ProcessIO end!" << endl;

 return 0;
}

int g_nThreadCnt = 1; ///< 標示當前開了多少個執行緒來服務於

void main()
{
 InitWinsock();

 SOCKET sListen = BindServer(PORT);

 char szBuf[200];
 memset(szBuf, 0, 200);

 SOCKET sClient;
 if((sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped)) == INVALID_SOCKET)
 {
  OutErr("WSASocket error!");  
  return;
 }

 if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
 {
  OutErr("WSACreateEvent error!");
  return;
 }

 EventTotal = 1;

 int nSec = 0;
 CreateThread(NULL, 0, ProcessIO, (LPVOID)&nSec, 0, NULL);

 while(1)
 {
  sClient = accept(sListen, NULL, NULL);
  if(sClient == INVALID_SOCKET)
  {
   OutErr("accept faild!");
   return;
  }

  // 先判斷是否能連線,是否超過了最大連線數

  if(EventTotal + 1 > DYL_MAXIMUM_WAIT_EVENTS)
  {
   cout << "Too many Client! closing socket" << sClient << endl;
   closesocket(sClient);
   continue;
  }

  cout << sClient << " 連線進來!" << endl;
  cout << "客戶端連線數:" << EventTotal + 1 << endl;

  /// 這裡要做身份驗證,不準其他客戶端連線上來

  // 判斷是否需要分割槽
  if( int( EventTotal/WSA_MAXIMUM_WAIT_EVENTS ) + 1 > g_nThreadCnt)
  {
   nSec = int((EventTotal + 1)/WSA_MAXIMUM_WAIT_EVENTS);
   EventArray[nSec * WSA_MAXIMUM_WAIT_EVENTS] = EventArray[0];
   EventTotal++;
   CreateThread(NULL, 0, ProcessIO, (LPVOID)&nSec, 0, NULL);
   g_nThreadCnt++;
  }
  
  CreateSocketInfo(sClient);

  DWORD dwRecv;
  DWORD dwFlags = 0;
  int nCurCnt = EventTotal - 1;

  if( WSARecv(sClient, &(SocketInfoArray[EventTotal-1]->DataBuf), 1, &dwRecv, &dwFlags,
     &(SocketInfoArray[EventTotal-1]->Overlapped), NULL) == SOCKET_ERROR )
  {
   if(WSAGetLastError() != WSA_IO_PENDING)
   {
    OutErr("WSARecv Error!");

    // 那就只能釋放相關資源了  
    DestroySocketInfo(nCurCnt);
   }
  }

  if(int(EventTotal/WSA_MAXIMUM_WAIT_EVENTS) == 0)
   WSASetEvent(EventArray[0]);
  else
  {
   //!! 這裡要設定一下0事件,讓工作執行緒去服務事件數組裡的其他事件
   for(int i = 0 ; i < int(EventTotal/WSA_MAXIMUM_WAIT_EVENTS); i++)
   {
    if (WSASetEvent(EventArray[i*WSA_MAXIMUM_WAIT_EVENTS]) == FALSE)
    {
     OutErr("WSASetEvent failed!");
     return;
    }
   }
  }
 }
 
}

/**
Server2.cpp: 使用了 AcceptEx 來處理連線
**/

/// 包含標頭檔案
#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

#include <windows.h>

#include <iostream>
using namespace std;


/// 巨集定義
#define PORT 5050
#define DATA_BUFSIZE 8192

#define OutErr(a) cout << (a) << endl
      << "出錯程式碼:" << WSAGetLastError() << endl
      << "出錯檔案:" << __FILE__ << endl  
      << "出錯行數:" << __LINE__ << endl

#define OutMsg(a) cout << (a) << endl;


/// 全域性變數定義
DWORD EventTotal = 0;

// #define WSA_MAXIMUM_WAIT_EVENTS 100

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

typedef struct _SOCKET_INFORMATION {
   CHAR Buffer[DATA_BUFSIZE];
   WSABUF DataBuf;
   SOCKET Socket;
   WSAOverlapped Overlapped;
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

LPSOCKET_INFORMATION SocketInfoArray[WSA_MAXIMUM_WAIT_EVENTS];

/// 全域性函式定義
void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 
}

SOCKET BindServer(int nPort)
{
 // 建立socket
 SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped);

 // 繫結埠
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
 {
  OutErr("bind Failed!");
  return NULL;
 }

 // 設定監聽佇列為200
 if(listen(sServer, WSA_MAXIMUM_WAIT_EVENTS) != 0)
 {
  OutErr("listen Failed!");
  return NULL;
 }
 return sServer;
}

void CreateSocketInfo(SOCKET sClient)
{
 SocketInfoArray[EventTotal] = new SOCKET_INFORMATION;
 SocketInfoArray[EventTotal]->Socket = sClient;
 ZeroMemory(&SocketInfoArray[EventTotal]->Overlapped, sizeof(WSAOverlapped));
 SocketInfoArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent();
 SocketInfoArray[EventTotal]->DataBuf.buf = SocketInfoArray[EventTotal]->Buffer;
 SocketInfoArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;

 memset(SocketInfoArray[EventTotal]->Buffer, 0, DATA_BUFSIZE);

 EventTotal++;
}

// 釋放,注意N從0開始
void DestroySocketInfo(int n)
{
 delete SocketInfoArray[n];
 if( (n+1) != WSA_MAXIMUM_WAIT_EVENTS )
 {
  for(int i = n; i < EventTotal; i++)
  {
   SocketInfoArray[i] = SocketInfoArray[i + 1];
  }
 }
 EventTotal--;
}


// 正文
void main()
{
 InitWinsock();

 SOCKET sListen, sClient;

 sListen = BindServer(PORT);

 // 準備好連線的SOCKET
 sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped);
 if(sClient == SOCKET_ERROR)
 {
  OutErr("WSASocket error!");
  return;
 }

 // 給這個Listen SOCKET分配一個Overlapped,可以把這個SOCKET 想象為其他的控制代碼,例如檔案控制代碼
 ZeroMemory(&ListenOverlapped, sizeof(Overlapped));
 if ((EventArray[EventTotal++] = ListenOverlapped.hEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
 {
  OutErr("WSACreateEvent failed");
  return;
 }

 // 投遞一個 AcceptEx 請求,注意這裡的AcceptBuffer引數是指:取得的第一塊從CLIENT發來的資料
 // 這塊資料包括:1、本地的地址 2、連線端的地址 3、這些地址是寫在這個緩衝區的後面一段的(具體可再參看MSDN)
 CHAR AcceptBuffer[2 * (sizeof(SOCKADDR_IN) + 16)];
 DWORD dwRecvBytes;
 if (AcceptEx(sListen, sClient, (PVOID) AcceptBuffer, 0,
  sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
  &ListenOverlapped) == FALSE)
 {
  if (WSAGetLastError() != ERROR_IO_PENDING)
  {
   OutErr("AcceptEx failed");
   return;
  }
 }

 // 真正的工作開始了,在下面可以非同步去處理Accept、WSARecv、WSASend等請求
 int nIdx;
 DWORD BytesTransferred;
 DWORD Flags;
 while(TRUE)
 {
  // 首先等待事件的發生
  if( (nIdx = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, INFINITE, FALSE)) == WSA_WAIT_FAILED )
  {
   OutErr("WSAWaitForMultipleEvents Error!");
   return;
  }

  // 處理連線
  if( (nIdx - WSA_WAIT_EVENT_0) == 0)
  {
   // 先看看什麼結果,反正 WSAWaitForMultipleEvents 呼叫完成,就肯定是有某種事情發生了
   if(WSAGetOverlappedResult(sListen, &ListenOverlapped, &BytesTransferred, FALSE, &Flags) == FALSE)
   {
    OutErr("nIdx 為 0,WSAGetOverlappedResult error!");
    return;
   }
  
   if (EventTotal >= WSA_MAXIMUM_WAIT_EVENTS)
   {
    cout << "Too many connections - closing socket." << endl;
    closesocket(sClient);

    goto ReAccept;
   }
   else
   {
    // cout << AcceptBuffer << endl;
    cout << "socket " << sClient << " 連線進來" << endl;
    cout << "連線客戶端總數 " << EventTotal << endl;

    // 建立相應的結構
    CreateSocketInfo(sClient);

    // 向剛進來的連線 投遞一個WSARecv操作
    Flags = 0;
    if ( WSARecv(sClient, &(SocketInfoArray[EventTotal-1]->DataBuf), 1, &dwRecvBytes, &Flags,
     &(SocketInfoArray[EventTotal-1]->Overlapped), NULL) == SOCKET_ERROR)
    {
     if (WSAGetLastError() != ERROR_IO_PENDING)
     {
      OutErr("WSARecv error!");
      return;
     }
    }
   }

ReAccept:
   // 再重新向ListenSocket投遞一個AcceptEx請求
   sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_Overlapped);
   if(sClient == INVALID_SOCKET )
   {
    OutErr("WSASocket Error!");
    return;
   }

   // 重置一下事件
   if(!WSAResetEvent(EventArray[0]))
   {
    OutErr("WSAResetEvent error!");
    return;
   }

   ZeroMemory(&ListenOverlapped, sizeof(Overlapped));
   ListenOverlapped.hEvent = EventArray[0];
   
   if (AcceptEx(sListen, sClient, (PVOID) AcceptBuffer, 0,
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
    &ListenOverlapped) == FALSE)
   {
    if (WSAGetLastError() != ERROR_IO_PENDING)
    {
     OutErr("AcceptEx Error!");
     return;
    }
   } 
   
   continue;
  }

  // 上面搞了一大通,終於可以開始處理真正的客戶端請求了
  LPSOCKET_INFORMATION SI;
  SI = SocketInfoArray[nIdx - WSA_WAIT_EVENT_0];
  WSAResetEvent(SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]);

  // 取得結果
  if (WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE
   || BytesTransferred == 0)
  {
   cout << SI->Socket << " 斷開" << endl;
   DestroySocketInfo(nIdx - WSA_WAIT_EVENT_0);
   continue;
  }   

  cout << SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]->Socket << " 發過來的資料:" << SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]->Buffer << endl;

    // 再投遞新的接收請求
  Flags = 0;
  if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &dwRecvBytes, &Flags,
   &(SI->Overlapped), NULL) == SOCKET_ERROR)
  {
   if (WSAGetLastError() != ERROR_IO_PENDING)
   {
    OutErr("WSARecv() failed");
    return;
   }
  }  
 } 
}


/**
測試客戶端

  Client1.cpp:
  1、伺服器斷開重連線
  2、測試單個客戶端連線對話

**/

#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <iostream>
using namespace std;

#define PORT 5050

#define OutErr(a) cout << (a) << endl
      << "出錯程式碼:" << WSAGetLastError() << endl
      << "出錯檔案:" << __FILE__ << endl  
      << "出錯行數:" << __LINE__ << endl

#define OutMsg(a) cout << (a) << endl;

void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 
}

SOCKET ConnectServer(char *lpszServerIP, int nPort, ULONG NonBlock)
{
 SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //ioctlsocket(sServer, FIONBIO, &NonBlock);
 
 struct hostent *pHost = NULL;
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = inet_addr(lpszServerIP);


 // 如果給的是主機的名字而不是IP地址
 if(servAddr.sin_addr.s_addr == INADDR_NONE)
 {
  pHost = gethostbyname( lpszServerIP );
  if(pHost == NULL)
  {
   OutErr("gethostbyname Failed!");
   return NULL;
  }
  memcpy(&servAddr.sin_addr, pHost->h_addr_list[0], pHost->h_length);
 }

 int nRet = 0;
 nRet = connect(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr));
 if( nRet == SOCKET_ERROR )
 {
  OutErr("connect failed!");
  return NULL;
 }
  
 return sServer;
}


void main()
{

 InitWinsock();

connect:
 SOCKET sServer = ConnectServer("127.0.0.1", PORT, 0);


reSay:
 char szBuf[1024];
 memset(szBuf, 0, 1024);
 cout << "你要說:";
 cin >> szBuf; 

 if(send(sServer, szBuf, strlen(szBuf), 0) == SOCKET_ERROR)
  goto connect;
 else
  goto reSay;
}

 /*
測試客戶端2
 Client2.cpp:測試多個客戶端連線
  */

#include <winsock2.h>
#pragma comment(lib, "WS2_32")


#include <iostream>
using namespace std;

#define PORT 5050

#define OutErr(a) cout << (a) << endl
      << "出錯程式碼:" << WSAGetLastError() << endl
      << "出錯檔案:" << __FILE__ << endl  
      << "出錯行數:" << __LINE__ << endl

#define OutMsg(a) cout << (a) << endl;


// 全域性函式定義
void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 
}

SOCKET ConnectServer(char *lpszServerIP, int nPort, ULONG NonBlock)
{
 SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //ioctlsocket(sServer, FIONBIO, &NonBlock);
 
 struct hostent *pHost = NULL;
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = inet_addr(lpszServerIP);


 // 如果給的是主機的名字而不是IP地址
 if(servAddr.sin_addr.s_addr == INADDR_NONE)
 {
  pHost = gethostbyname( lpszServerIP );
  if(pHost == NULL)
  {
   OutErr("gethostbyname Failed!");
   return NULL;
  }
  memcpy(&servAddr.sin_addr, pHost->h_addr_list[0], pHost->h_length);
 }

 int nRet = 0;
 nRet = connect(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr));
 if( nRet == SOCKET_ERROR )
 {
  OutErr("connect failed!");
  return NULL;
 }
  
 return sServer;
}


void main()
{
 InitWinsock();

 SOCKET sServer[64];

 for(int i = 0; i < 63; i++)
 {
  sServer[i] = ConnectServer("127.0.0.1", PORT, 0);
  
  if(sServer[i] != NULL)
  {
   send(sServer[i], "hello", 6, 0);
  }
  Sleep(100);  
 }

 while(1)
 
}

 把上面的幾個CPP檔案都拷貝到自己的工程裡就可以編譯通過。

相關推薦

WinSocket模型探討——Overlapped模型

重疊模型是Windows裡一種重要的 I/O 模型,可以有效率的實現一些 I/O 操作,譬如檔案讀寫、Socket讀寫等,在這裡我們一起來研究一下重疊模型,看看它究竟為何方神聖。 這篇文章分為以下幾部分來說明: 重疊模型的概念 容易碰到的一些問題 重疊模型的基本程式設計方法(accept 和 Accept

判別模型和生成模型

使用 sin cow ria regress gauss 生成 == 給定 【摘要】 - 生成模型:無窮樣本==》概率密度模型 = 產生模型==》預測 - 判別模型:有限樣本==》判別函數 = 預測模型==》預測 【簡介】 簡單的說,假設o是觀察值,

生成模型和判別模型

特征 數據 學習方法 最大 mem 針對 表示 不能 問題 1、定義: 生成模型(或稱產生式模型)和判別模型(或稱判別式模型)的本質區別在於模型中觀測序列x和狀態序列y的決定關系。前者假設y決定x,後者假設x決定y。 2、生成模型特點 2.1、生成模型以“狀態序列y按照一定

CSS布局模型 之 浮動模型(浮動的工作原理和清除浮動技巧?)

浮動 浮動模型 工作原理 浮動的工作原理浮動是讓某元素脫離文檔流,在浮動框之前和之後的非定位元素會當它不存在一樣,可能沿著它的另一側垂直流動,但都為其騰出空間,塊級元素也不例外(被浮動元素占據了部分行空間的塊級元素,仍然被看作是占據了一整行,只不過是被浮動元素占據的那部分空間無法利用罷了)。浮動的

布局模型 之 層模型(position的relative、absolute與fixed區別?)

absolute fixed relative css的布局模型分為流動模型(Flow)、浮動模型(Float)、層模型(Layer)。 浮動模型(Float)和層模型(Layer)有什麽顯著區別? 浮動模型(Float):浮動是讓某元素脫離文檔流的限制,在浮動框之前和之後的非定位元素

RUP模型與XP模型

心得體會 將在 對象 制作 splay 部署 思想 初始化 進行   上一篇文章中大致介紹了軟件開發過程模型和一些傳統的過程模型,接下來分享一下學習新型軟件開發過程模型RUP的心得體會。 RUP模型   RUP(Rational Unified Process),統一軟件開

CSS布局模型(流動模型、浮動模型、層模型

同時 可能 內聯元素 堆疊 bsp nbsp 模型 fault posit 在網頁中,元素有三種布局模型: 1、流動模型(Flow) default,就是塊級元素都自上而下的分布,寬度都為100%。內聯元素都從左到右水平分布。 2、浮動模型 (Float) div、p、t

PowerDesigner概念模型與物理模型相互轉換及導出數據字典

microsoft 字典 ros 去掉 相互轉換 右擊 soft designer design 最近公司項目竣工,驗收完成後,把整體平臺的所有文檔都寫清楚,找包發給甲方,由於本人是維護數據庫工作,依上面要求,必須編寫《數據庫設計說明書》裏面格式包含三個部分:概念模型

MVC模型與MTV模型

png 如何 emp 請求 pytho 後端 com 不同 界面 MVC模型: MVC(Model View Controller 模型-視圖-控制器)是一種Web架構的模式,它把業務邏輯、模型數據、用戶界面分離開來,讓開發者將數據與表現解耦,前端工程師可以只改頁面效果部分

CAP理論下對比ACID模型與BASE模型

關系 實現 數據庫 需要 ava base 庫存 sof 一個 CAP介紹 Consistency(一致性), 數據一致更新,所有數據變動都是同步的。比如網購,庫存減少的同時資金增多。Availability(可用性), 好的響應性能。比如支付操作10ms內響應用戶。Par

IO阻塞模型 非阻塞模型

client __main__ pip -a 密鑰 paramiko authorize upper bcd IO阻塞模型(blocking IO) 在linux中,默認情況下所有的socket都是blocking,一個典型的讀操作流程大概是這樣: 所以,bloc

QT筆記:數據庫總結(三)之SQL模型類-QSqlTableModel模型

word -c error sele 讀寫 ren isp 添加 完全 QSqlTableModel類繼承至QSqlQueryModel類,該類提供了一個可讀寫單張SQL表的可編輯數據模型,功能:修改,插入,刪除,查詢,和排序 常用函數 QVariant headerDat

模型——標準盒模型與怪異盒模型

inter 所有 ges 讓我 圖片 生活 -s idt div2 盒模型是CSS中一種重要的思維模型,理解了盒模型才能進行更好的頁面布局。顧名思義,我們把頁面上所有的元素都看做是一個生活中常見的盒子,它具備內容(content),內邊距(padding),邊框(borde

網絡基礎之osi模型與TCP模型

接口 上層 system sum 選擇 prior 處理 報文 pro ISO/OSI(pen System Internetwork) 根據網絡功能劃分層次: 物理層:

dedecms自定義模型之獨立模型在首頁、列表頁、內容調用內容

兩個 定義 blog typeid 註意 lists lis curl lds dedecms關於自定義模型(獨立模型)的首頁、列表頁、內容怎麽調用?在後臺自定義模型(獨立模型)的建立及自定義字段的添加比較簡單,需要註意兩點: (1)如果某個字段需要在前臺列表頁顯示,則在前

css模型之層模型

如何 兄弟元素 生效 -m 方法 同時 頁面 意義 border css層模型說的是position這個屬性,它有四個常用值,分別是static、relative、absolute、fixed。 1.static:這個是元素的默認值,特點是頁面會依照自左向右、自上向下的方向

對星型模型和雪花模型的簡單理解

alt 存在 body 所有 維度 info 多層 分享 post 星形模型 雪花模型 星型模型是所有維度表都是連接在一個事實表上面,雪花模型是將維度表拆分地更加詳細,是多層次的。 在星型模型的維度表裏面,一張維度表儲存了眾多存在冗余的信息,為什麽冗余,在哪裏冗余,我

並發編程 - IO模型 - 1.io模型/2.阻塞io/3.非阻塞io/4.多路復用io

post app decode pos win 循環 效率 網絡io als 1.io模型提交任務得方式: 同步:提交完任務,等結果,執行下一個任務 異步:提交完,接著執行,異步 + 回調 異步不等結果,提交完任務,任務執行完後,會自動觸發回調函數同步不等於阻

模型評估與模型選擇

訓練 合並 曲線 問題 出現 技術 mil 評估 alt 一、訓練誤差與測試誤差 統計學習的目的就是利用已經學到的模型對已知數據和未知數據進行預測,因此在損失函數確定的情況下,基於損失函數的訓練誤差和測試誤差就成了我們對模型進行評價的一個標準。 註意:在統計學習中使用的損失

powerdesigner使用之——從“概念模型”到“物理模型

design ati .cn style 強制 直接 AI pre 分享圖片 原文地址:https://www.cnblogs.com/brolanda/p/4265806.html 現實問題在計算機上的解決,需要我們從現實問題中抽象出實體模型,然後再將實體模型對應到數據庫