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();