IOCP完成端口簡單Demo

//////////////////////////////////////////////////////////////////////////
///author:Jeson Yang
///date:2014.09.28
//////////////////////////////////////////////////////////////////////////
#include <WinSock2.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

#define  TCP_PORT 10066
#define MAX_BUFF_LENGTH 1024
typedef struct PER_IO_OPERATION_DATA
{
 WSAOVERLAPPED overlap;

 WSABUF buf;
 char szMessage[MAX_BUFF_LENGTH];
 DWORD numberOfBytesRecved;
 DWORD flag;
}PER_IO_OPERATION_DATA,*LPPER_IO_OPERATION_DATA;

DWORD WINAPI WorkerThread(LPVOID CompletionPortId);
void main()
{
 WORD dwVersion = MAKEWORD(2, 2);
 WSAData wsaData;
 WSAStartup(dwVersion, &wsaData);
 
 //初始化完成端口
 HANDLE completionport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
 
 SYSTEM_INFO sysInfo;
 DWORD threadId;
 ::GetSystemInfo(&sysInfo);
 for (int i = 0; i < (int)sysInfo.dwNumberOfProcessors; i++)
 {
  CreateThread(NULL, 0, WorkerThread, completionport, 0, &threadId);
 }


 //創建套接字
 SOCKET sClient;
 SOCKET sScok = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

 SOCKADDR_IN localAddr, clientAddr;
 ::memset(&localAddr, 0, sizeof(SOCKADDR_IN));
 
 //綁定套接字
 localAddr.sin_family = AF_INET;
 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr("127.0.0.1");
 localAddr.sin_port = htons(TCP_PORT);
 if (::bind(sScok, (SOCKADDR*)&localAddr, sizeof(sockaddr)))
 {
  ::OutputDebugString(_T("bind error!"));
  return;
 }
 
 //開始監聽
 ::listen(sScok, 1);
 int iaddrSize = sizeof(SOCKADDR_IN);//此處應該先賦值,或者在accept出填NULL,如果此處爲負數,accept將不會死鎖在那裏。
 PER_IO_OPERATION_DATA *lpPerData = NULL;
 while (true)
 {
  //如果有客戶請求連接,則繼續,否則等待
  sClient = ::accept(sScok, (sockaddr*)&clientAddr, &iaddrSize);//NULL);

  //client中保存用戶信息
  printf("Accepted client: %s: %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
  
  //將這個最新到來的客戶套接字和完成端口綁定到一起
  //第三個參數表示傳遞的參數,這裏就傳遞的客戶套接字地址。最後一個參數爲0 表示有和CPU一樣的進程數。即1個CPU一個線程
  CreateIoCompletionPort((HANDLE)sClient, completionport, (DWORD)sClient, 0);

  lpPerData = (LPPER_IO_OPERATION_DATA)::HeapAlloc(::GetProcessHeap(),
   HEAP_ZERO_MEMORY,
   sizeof(PER_IO_OPERATION_DATA));
  lpPerData->buf.len = MAX_BUFF_LENGTH;
  lpPerData->buf.buf = lpPerData->szMessage;

  //異步接收消息,立刻返回。
  ::WSARecv(sClient, &lpPerData->buf, 1, &lpPerData->numberOfBytesRecved, &lpPerData->flag,&lpPerData->overlap, NULL);
 }
 ::PostQueuedCompletionStatus(completionport, 0xFFFFFFFF, 0, NULL);
 ::CloseHandle(completionport);
 closesocket(sScok);
 WSACleanup();
}

//工作者線程有一個參數,是指向完成端口的句柄
DWORD WINAPI WorkerThread(LPVOID CompletionPortId)
{
 HANDLE completionPort = (HANDLE)CompletionPortId;
 DWORD dwByteTransferred;
 SOCKET sClient;
 LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
 while(true)
 {
  ::GetQueuedCompletionStatus(completionPort,
   &dwByteTransferred,
   (PULONG_PTR)&sClient,
   (LPOVERLAPPED *)&lpPerIOData,
   INFINITE);
  if (dwByteTransferred == 0xFFFFFFFF)
  {
   return 0;
  }

  if (dwByteTransferred == 0)
  {
   ::closesocket(sClient);
   ::HeapFree(::GetProcessHeap(), HEAP_ZERO_MEMORY, lpPerIOData);//釋放結構體
  }
  else
  {
   lpPerIOData->szMessage[dwByteTransferred] = '\0';
   ::send(sClient, lpPerIOData->szMessage, dwByteTransferred, 0);//將接收到的消息返回

   memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
   lpPerIOData->buf.len = MAX_BUFF_LENGTH;
   lpPerIOData->buf.buf = lpPerIOData->szMessage;
   //異步接收消息,立刻返回。
   ::WSARecv(sClient, &lpPerIOData->buf, 1, &lpPerIOData->numberOfBytesRecved, &lpPerIOData->flag,&lpPerIOData->overlap, NULL);
  }
 }

 return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章