天天看點

一個對Winsock 完成端口模型封裝的類【2】

BOOL CompletionPortModel::ThreadLoop()

/*++

Fucntion Description:

   主線程循環,用WaitForSigleObject等待m_hEvent,已經發出的AcceptEx()調用耗盡,FD_ACCEPT

事件将被觸發,WaitForSigleObject成功傳回,然後調用PostAcceptEx()來新發出10個AcceptEx()調用。

WaitForSigleObject每次等待10秒,逾時傳回後,對系統中已經建立成功了的并且還沒有收發過資料的

SOCKET連接配接進行檢測,如果某個連接配接已經建立了30秒,并且還沒收發過資料,則強制關閉。

Arguments:

   無。

Return Value:

   函數調用成功傳回TRUE,調用失敗傳回FALSE;

--*/

{

int nOptval;

int nOptlen;

int nResult;

DWORD dwResult;

int nCounter = 0;

#ifdef _DEBUG

int nTimeOut = 0;

#endif

cout << "Server is running.........." << nCounter << " times" << endl;

while (TRUE)

dwResult = WaitForSingleObject(m_hEvent, 10000);

if (WAIT_FAILED == dwResult)

   PostQueuedCompletionStatus(m_hCOP, 0, NULL, NULL);

   cout << "WSAWaitForMultipleEvents() failed: " << WSAGetLastError() << endl;

   return FALSE;

}

if (WAIT_TIMEOUT == dwResult)

   nCounter++;

   cout << "Server is running.........." << nCounter << " times" << endl;

   nTimeOut++;

   cout << nTimeOut << "*******TIME_OUT********" << nTimeOut << endl;

   PPER_IO_CONTEXT lpCurentNode = m_lpConnectionListHead->pNext;

   PPER_IO_CONTEXT lpPreNode = m_lpConnectionListHead;

   PPER_IO_CONTEXT lpTempNode;

   while (NULL != lpCurentNode)

   {

    EnterCriticalSection(&m_ListCriSection);

    nOptlen = sizeof(nOptval);

    nResult = getsockopt(

     lpCurentNode->sClient,

     SOL_SOCKET,

     SO_CONNECT_TIME,

     (char*)&nOptval,

     &nOptlen

     );

    cout << "nOptval = " << nOptval << endl;

#endif _DEBUG

    if (SOCKET_ERROR == nResult)

    {

     cout << "SO_CONNECT_TIME failed: " << WSAGetLastError() << endl;

     lpPreNode = lpCurentNode;

     lpCurentNode = lpCurentNode->pNext;

     LeaveCriticalSection(&m_ListCriSection);

     continue;

    }

    if ((nOptval!=0xFFFFFFFF) && (nOptval>30))

     lpPreNode->pNext = lpCurentNode->pNext;

     lpTempNode = lpCurentNode;

     closesocket(lpTempNode->sClient);

     lpTempNode->pNext = NULL;

     InsertToLookaside(lpTempNode, NULL);

    else

    LeaveCriticalSection(&m_ListCriSection);

   }

else

   if (WAIT_TIMEOUT != dwResult)

    if (FALSE == PostAcceptEx())

     PostQueuedCompletionStatus(m_hCOP, 0, NULL, NULL);

     return FALSE;

return TRUE;

}//end of CheckConnectTime

void CompletionPortModel::ReleaseNode(PPER_IO_CONTEXT pNode)

     将參數中傳遞的結點從連結清單中解除,但不釋放結點。以便不讓ThreadLoop函數對其進行逾時檢測。

此函數在完成端口線程裡收發資料成功後調用。

要從連結清單中釋放的結點。

     無。

PPER_IO_CONTEXT pTempNode = m_lpConnectionListHead->pNext;

PPER_IO_CONTEXT pPreNode =m_lpConnectionListHead;

PPER_IO_CONTEXT pDeleteNode;

while (NULL != pTempNode)

if (pNode->unId == pTempNode->unId)

   pPreNode->pNext = pTempNode->pNext;

   pDeleteNode = pTempNode;

   pTempNode = pTempNode->pNext;

   return;

   pPreNode = pTempNode;

return;

}//end of RealeseNode

BOOL CompletionPortModel::HandleData(PPER_IO_CONTEXT lpPerIoContext, int nFlags)

      根據傳進來的nFlags參數對lpPerIoContext進行設定,并訓示下一步IO操作。

      lpPerIoContext - 調用GetQueueCompletionStatus函數得到的上一次IO操作的結果(擴充的

                      WSAOVERLAPPED結構)。

      nFlags         - 指明已經完成上一次IO的操作類型。

      函數調用成功傳回TRUE,失敗傳回FALSE。

//

//nFlags == IO_READ_COMPLETION表示完成的上一次IO操作是WSARecv。

if (IO_READ_COMPLETION == nFlags)

//完成了WSARecv,接下來需要調用WSASend把剛接收到的資料發送回去,把

//lpPerIoContext->ContinueAction = ContinueWrite;

lpPerIoContext->IoOperation = IoWrite;

ZeroMemory(&(lpPerIoContext->ol), sizeof(WSAOVERLAPPED));

//接收到的資料在lpPerIoContext->wsaBuffer.buf裡,可以調用

//自定義函數對資料自行處理,本例中,簡單的将資料再發送回去

strcpy(lpPerIoContext->szBuffer, lpPerIoContext->wsaBuffer.buf);

lpPerIoContext->wsaBuffer.buf = lpPerIoContext->szBuffer;

lpPerIoContext->wsaBuffer.len = BUFFER_SIZE;

if (IO_WRITE_COMPLETION == nFlags)

//上一次IO操作WSASend資料發送完成,将後續操作标志設定為關閉

//如果不需要關閉而是要繼續發送,将lpPerIoContext->IoOperation設定為

//IoWrite,如果要繼續接收,将lpPerIoContext->IoOperation設定為

//IoRead,并初始化好BUFFER,本例中,設定關閉

lpPerIoContext->IoOperation = IoEnd;

if (IO_ACCEPT_COMPLETION == nFlags)

//剛建立了一個連接配接,并且沒有收發資料,,,,

lpPerIoContext->IoOperation = IoRead;

ZeroMemory(lpPerIoContext->szBuffer, BUFFER_SIZE);

return FALSE;

}// end of HandleData()

BOOL CompletionPortModel::InitLinkListHead()

     初始化連結清單頭指針。

     函數調用成功傳回TRUE,失敗傳回FALSE。

m_lpConnectionListHead = (PPER_IO_CONTEXT)HeapAlloc(GetProcessHeap(), /

HEAP_ZERO_MEMORY, sizeof(PER_IO_CONTEXT));

if (NULL == m_lpConnectionListHead)

cout << "HeapAlloc() failed " << endl;

m_lpConnectionListHead->pNext = NULL;

}// end of InitLinkListHead()

BOOL CompletionPortModel::AllocEventMessage()

     将FD_ACCEPT事件注冊到m_hEvent,這樣當可用AcceptEx調用被耗盡的時候,就會觸發FD_ACCEPT

事件,然後ThreadLoop裡的WaitForSingleObject就會成功傳回,導緻PostAcceptEx被調用。

m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

if (NULL == m_hEvent)

PostQueuedCompletionStatus(m_hCOP, 0, NULL, NULL);

cout << "CreateEvent() failed: " << GetLastError() << endl;

int nResult = WSAEventSelect(m_ListenSocket, m_hEvent, FD_ACCEPT);

if (SOCKET_ERROR == nResult)

CloseHandle(m_hEvent);

cout << "WSAEventSeclet() failed: " << WSAGetLastError() << endl;

}//end of AllocEventMessage()

BOOL CompletionPortModel::DataAction(PPER_IO_CONTEXT lpPerIoContext, PPER_HANDLE_CONTEXT lpNewperHandleContext)

     根據參數lpPerIoContext的成員IoOperation的值來進行下一步IO操作。

lpPerIoContext        - 将随WSASend或者WSARecv一起投遞的擴充WSAOVERLAPPED結構。

lpNewperHandleContext - AcceptEx調用成功後給新套接字配置設定的“單句柄資料”。

DWORD dwIosize = 0;

DWORD dwFlags =0;

if (IoWrite == lpPerIoContext->IoOperation)

nResult = WSASend(lpPerIoContext->sClient,

     &(lpPerIoContext->wsaBuffer),

     1,

     &dwIosize,

     0,

     &(lpPerIoContext->ol),

     NULL

if((SOCKET_ERROR==nResult) && (ERROR_IO_PENDING != WSAGetLastError()))

   cout << "WSASend() failed: " << WSAGetLastError() << endl;

   closesocket(lpPerIoContext->sClient);

   lpPerIoContext->pNext = NULL;

   lpNewperHandleContext->pNext = NULL;

   InsertToLookaside(lpPerIoContext, NULL);

   InsertToLookaside(NULL, lpNewperHandleContext);

if (IoRead == lpPerIoContext->IoOperation)

nResult = WSARecv(lpPerIoContext->sClient,

     &dwFlags,

   cout << "WSARecv() failed: " << WSAGetLastError() << endl;

if (IoEnd == lpPerIoContext->IoOperation)

closesocket(lpPerIoContext->sClient);

lpNewperHandleContext->pNext = NULL;

InsertToLookaside(NULL, lpNewperHandleContext);

lpPerIoContext->pNext = NULL;

InsertToLookaside(lpPerIoContext, NULL);

}// end of DataAction()

void CompletionPortModel::GetAddressAndPort()

     由類構造函數調用的函數,用來輸入伺服器要綁定的本地IP位址和端口。

無。

無。  

cout << "/nPlease input a port: ";

cin >> uPort;

cout << "/nPlease input localaddress:";

cin >> szAddress;

system("cls");

}// end of GetAddressAdnPort

void CompletionPortModel::InsertToLookaside(PPER_IO_CONTEXT lpIoNode, PPER_HANDLE_CONTEXT lpHandleNode)

     給旁視清單的連結清單中插入一個空閑的結點。

lpIoNode     - 要插入的結點,類型為PPER_IO_CONTEXT。

lpHandleNode - 要插入的結點,類型為PPER_HANDLE_CONTEXT。

if (NULL != lpIoNode)

if (NULL == m_lpIoLookasideLists)

   m_lpIoLookasideLists = lpIoNode;

lpIoNode->pNext = m_lpIoLookasideLists->pNext;

m_lpIoLookasideLists->pNext = lpIoNode;

if (NULL != lpHandleNode)

if (NULL == m_lpHandleLOOKasideLists)

   m_lpHandleLOOKasideLists = lpHandleNode;

lpHandleNode->pNext = m_lpHandleLOOKasideLists->pNext;

m_lpHandleLOOKasideLists->pNext = lpHandleNode;

PPER_IO_CONTEXT CompletionPortModel::GetIoFromLookaside()

     從旁視清單中解除一個結點并将其傳回。

傳回一個PPER_IO_CONTEXT類型的結點。  

return NULL;

EnterCriticalSection(&m_IoCriSection);

PPER_IO_CONTEXT lpReturnNode = m_lpIoLookasideLists;

m_lpIoLookasideLists = m_lpIoLookasideLists->pNext;

LeaveCriticalSection(&m_IoCriSection);

return lpReturnNode;

PPER_HANDLE_CONTEXT CompletionPortModel::GetHandleFromLookaside()

傳回一個PPER_HANDLE_CONTEXT類型的結點。  

EnterCriticalSection(&m_HandleCriSection);

PPER_HANDLE_CONTEXT lpReturnNode = m_lpHandleLOOKasideLists;

m_lpHandleLOOKasideLists = m_lpHandleLOOKasideLists->pNext;

LeaveCriticalSection(&m_HandleCriSection);

================================================================================

#include "iomodel.h"

#include <winsock2.h>

#include <windows.h>

#include <iostream.h>

#pragma comment(lib, "ws2_32.lib")

void main()

CompletionPortModel p;

p.Init();

p.AllocEventMessage();

if (FALSE == p.PostAcceptEx())

p.ThreadLoop();

上一篇: ASP錯誤代碼

繼續閱讀