一個重疊io的例子 實現一個簡單的服務器

#include <stdlib.h>

#define STRICT
#define _WIN32_WINNT 0x0500 // Windows 2000 or later
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>

#include <Mswsock.h>

#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")

#define BUFFER_SIZE 1024
class CInitSock
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{ 
		// 初始化WS2_32.dll
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if(::WSAStartup(sockVersion, &wsaData) != 0)
		{ 
			exit(0); 
		}
	}

	~CInitSock()
	{ 
		::WSACleanup(); 
	}
};

CInitSock initsock;

typedef BOOL(PASCAL FAR * LPFN_ACCEPTEX)(
    IN SOCKET sListenSocket,
    IN SOCKET sAcceptSocket,
    IN PVOID lpOutputBuffer,
    IN DWORD dwReceiveDataLength,
    IN DWORD dwLocalAddressLength,
    IN DWORD dwRemoteAddressLength,
    OUT LPDWORD lpdwBytesReceived,
    IN LPOVERLAPPED lpOverlapped
    );

typedef struct _SOCKET_OBJ
{
	SOCKET s; // 套接字句柄
	int nOutstandingOps; // 記錄此套接字上的重疊I/O 數量
	LPFN_ACCEPTEX lpfnAcceptEx; // 擴展函數AcceptEx 的指針(僅對監聽套接字而言)
} SOCKET_OBJ, *PSOCKET_OBJ;

PSOCKET_OBJ GetSocketObj(SOCKET s)
{
	PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
	if(pSocket != NULL)
	{
		pSocket->s = s;
	}
	return pSocket;
}

void FreeSocketObj(PSOCKET_OBJ pSocket)
{
	if(pSocket->s != INVALID_SOCKET)
	{
		::closesocket(pSocket->s);
	}
	::GlobalFree(pSocket);
}

typedef struct _BUFFER_OBJ
{
	OVERLAPPED ol;			// 重疊結構
	char *buff;				// send/recv/AcceptEx 所使用的緩衝區
	int nLen;				// buff 的長度
	PSOCKET_OBJ pSocket;	// 此I/O 所屬的套接字對象
	int nOperation;			// 提交的操作類型
	#define OP_ACCEPT 1
	#define OP_READ 2
	#define OP_WRITE 3
	SOCKET sAccept;			// 用來保存AcceptEx 接受的客戶套接字(僅對監聽套接字而言)
	_BUFFER_OBJ *pNext;
} BUFFER_OBJ, *PBUFFER_OBJ;

HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS]; // I/O 事件句柄數組
int g_nBufferCount; // 上數組中有效句柄數量
PBUFFER_OBJ g_pBufferHead, g_pBufferTail; // 記錄緩衝區對象組成的表的地址

PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket, ULONG nLen)
{ 
	if(g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS -1)
	{
		return NULL;
	}
	PBUFFER_OBJ pBuffer = (PBUFFER_OBJ)::GlobalAlloc(GPTR, sizeof(BUFFER_OBJ));
	if(pBuffer != NULL)
	{ 
		pBuffer->buff = (char*)::GlobalAlloc(GPTR, nLen);
		pBuffer->ol.hEvent = ::WSACreateEvent();
		pBuffer->pSocket = pSocket;
		pBuffer->sAccept = INVALID_SOCKET;
		// 將新的BUFFER_OBJ 添加到列表中
		if(g_pBufferHead == NULL)
		{ 
			g_pBufferHead = g_pBufferTail = pBuffer; 
		}
		else
		{ 
			g_pBufferTail->pNext = pBuffer;
			g_pBufferTail = pBuffer;
		}
		g_events[++ g_nBufferCount] = pBuffer->ol.hEvent;
	}
	return pBuffer;
}

void FreeBufferObj(PBUFFER_OBJ pBuffer)
{ 
	// 從列表中移除BUFFER_OBJ 對象
	PBUFFER_OBJ pTest = g_pBufferHead;
	BOOL bFind = FALSE;
	if(pTest == pBuffer)
	{ 
		g_pBufferHead = g_pBufferTail = NULL;
		bFind = TRUE;
	}
	else
	{ 
		while(pTest != NULL && pTest->pNext != pBuffer)
		{
			pTest = pTest->pNext;
		}
		if(pTest != NULL)
		{ 
			pTest->pNext = pBuffer->pNext;
			if(pTest->pNext == NULL)
			{
				g_pBufferTail = pTest;
			}
			bFind = TRUE;
		}
	}
	// 釋放它佔用的內存空間
	if(bFind)
	{ 
		g_nBufferCount --;
		::CloseHandle(pBuffer->ol.hEvent);
		::GlobalFree(pBuffer->buff);
		::GlobalFree(pBuffer);
	}
}

PBUFFER_OBJ FindBufferObj(HANDLE hEvent)
{
	PBUFFER_OBJ pBuffer = g_pBufferHead;
	while(pBuffer != NULL)
	{
		if(pBuffer->ol.hEvent == hEvent)
		{
			break;
		}
		pBuffer = pBuffer->pNext;
	}
	return pBuffer;
}

void RebuildArray()
{
	PBUFFER_OBJ pBuffer = g_pBufferHead;
	int i = 1;
	while(pBuffer != NULL)
	{ 
		g_events[i++] = pBuffer->ol.hEvent;
		pBuffer = pBuffer->pNext;
	}
}

BOOL PostAccept(PBUFFER_OBJ pBuffer)
{
	PSOCKET_OBJ pSocket = pBuffer->pSocket;
	if(pSocket->lpfnAcceptEx != NULL)
	{ 
		// 設置I/O 類型,增加套接字上的重疊I/O 計數
		pBuffer->nOperation = OP_ACCEPT;
		pSocket->nOutstandingOps ++;
		// 投遞此重疊I/O
		DWORD dwBytes;
		pBuffer->sAccept = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
		BOOL b = pSocket->lpfnAcceptEx(pSocket->s,
		pBuffer->sAccept,
		pBuffer->buff,
		BUFFER_SIZE - ((sizeof(sockaddr_in) + 16) * 2),
		sizeof(sockaddr_in) + 16,
		sizeof(sockaddr_in) + 16,
		&dwBytes,
		&pBuffer->ol);
		if(!b)
		{
			if(::WSAGetLastError() != WSA_IO_PENDING)
			{
				return FALSE;
			}
		}
		return TRUE;
	}
	return FALSE;
}

BOOL PostRecv(PBUFFER_OBJ pBuffer)
{ 
	// 設置I/O 類型,增加套接字上的重疊I/O 計數
	pBuffer->nOperation = OP_READ;
	pBuffer->pSocket->nOutstandingOps ++;
	// 投遞此重疊I/O
	DWORD dwBytes;
	DWORD dwFlags = 0;
	WSABUF buf;
	buf.buf = pBuffer->buff;
	buf.len = pBuffer->nLen;
	printf("%s\n", buf.buf);
	if(::WSARecv(pBuffer->pSocket->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
	{
		if(::WSAGetLastError() != WSA_IO_PENDING)
		{
			return FALSE;
		}
	}
	return TRUE;
}

BOOL PostSend(PBUFFER_OBJ pBuffer)
{ 
	// 設置I/O 類型,增加套接字上的重疊I/O 計數
	pBuffer->nOperation = OP_WRITE;
	pBuffer->pSocket->nOutstandingOps ++;
	// 投遞此重疊I/O
	DWORD dwBytes;
	DWORD dwFlags = 0;
	WSABUF buf;
	buf.buf = pBuffer->buff;
	buf.len = pBuffer->nLen;
	buf.buf = "hello world";

	if(::WSASend(pBuffer->pSocket->s, &buf, 1, &dwBytes, dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
	{
		if(::WSAGetLastError() != WSA_IO_PENDING)
		{
			return FALSE;
		}
	}
	return TRUE;
}

BOOL HandleIO(PBUFFER_OBJ pBuffer)
{
	PSOCKET_OBJ pSocket = pBuffer->pSocket; // 從BUFFER_OBJ 對象中提取SOCKET_OBJ 對象指針,
	// 爲的是方便引用
	pSocket->nOutstandingOps --;
	// 獲取重疊操作結果
	DWORD dwTrans;
	DWORD dwFlags;
	BOOL bRet = ::WSAGetOverlappedResult(pSocket->s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags);
	if(!bRet)
	{ 
		// 在此套接字上有錯誤發生,因此,關閉套接字,移除此緩衝區對象。
		// 如果沒有其它拋出的I/O 請求了,釋放此緩衝區對象,否則,等待此套接字上的其它I/O 也完成
		if(pSocket->s != INVALID_SOCKET)
		{ 
			::closesocket(pSocket->s);
			pSocket->s = INVALID_SOCKET;
		}
		if(pSocket->nOutstandingOps == 0)
		{
			FreeSocketObj(pSocket);
		}
		FreeBufferObj(pBuffer);
		return FALSE;
	}
	// 沒有錯誤發生,處理已完成的I/O
	switch(pBuffer->nOperation)
	{
		case OP_ACCEPT: // 接收到一個新的連接,並接收到了對方發來的第一個封包
		{
			// 爲新客戶創建一個SOCKET_OBJ 對象
			PSOCKET_OBJ pClient = GetSocketObj(pBuffer->sAccept);

			// 爲發送數據創建一個BUFFER_OBJ 對象,這個對象會在套接字出錯或者關閉時釋放
			PBUFFER_OBJ pSend = GetBufferObj(pClient, BUFFER_SIZE);
			if(pSend == NULL)
			{ 
				printf(" Too much connections! \n");
				FreeSocketObj(pClient);
				return FALSE;
			}
			RebuildArray();

			// 將數據複製到發送緩衝區
			pSend->nLen = dwTrans;
			memcpy(pSend->buff, pBuffer->buff, dwTrans);
			// 投遞此發送I/O(將數據回顯給客戶)
			if(!PostSend(pSend))
			{ // 萬一出錯的話,釋放上面剛申請的兩個對象
				FreeSocketObj(pSocket);
				FreeBufferObj(pSend);
				return FALSE;
			}
			// 繼續投遞接受I/O
			PostAccept(pBuffer);
		}
		break;

		case OP_READ: // 接收數據完成
		{
			if(dwTrans > 0)
			{ 
				// 創建一個緩衝區,以發送數據。這裏就使用原來的緩衝區
				PBUFFER_OBJ pSend = pBuffer;
				pSend->nLen = dwTrans;
				// 投遞發送I/O(將數據回顯給客戶)
				PostSend(pSend);
			}
			else // 套接字關閉
			{ // 必須先關閉套接字,以便在此套接字上投遞的其它I/O 也返回
				if(pSocket->s != INVALID_SOCKET)
				{ 
					::closesocket(pSocket->s);
					pSocket->s = INVALID_SOCKET;
				}

				if(pSocket->nOutstandingOps == 0)
				{
					FreeSocketObj(pSocket);
				}
				FreeBufferObj(pBuffer);
				return FALSE;
			}
		}
		break;

		case OP_WRITE: // 發送數據完成
		{ 
			if(dwTrans > 0)
			{ 
				// 繼續使用這個緩衝區投遞接收數據的請求
				pBuffer->nLen = BUFFER_SIZE;
				PostRecv(pBuffer);
			}
			else // 套接字關閉
			{ 
				// 同樣,要先關閉套接字
				if(pSocket->s != INVALID_SOCKET)
				{ 
					::closesocket(pSocket->s);
					pSocket->s = INVALID_SOCKET;
				}
				if(pSocket->nOutstandingOps == 0)
				{
					FreeSocketObj(pSocket);
				}
				FreeBufferObj(pBuffer);
				return FALSE;
			}
		}
		break;
	}
	return TRUE;
}


int main(int argc, char* argv[])
{
	// 創建監聽套接字,綁定到本地端口,進入監聽模式
	int nPort =6000;
	SOCKET sListen =
	::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	SOCKADDR_IN si;
	si.sin_family = AF_INET;
	si.sin_port = ::ntohs(nPort);
	si.sin_addr.S_un.S_addr = INADDR_ANY;
	::bind(sListen, (sockaddr*)&si, sizeof(si));
	::listen(sListen, 200);
	// 爲監聽套接字創建一個SOCKET_OBJ 對象
	PSOCKET_OBJ pListen = GetSocketObj(sListen);
	// 加載擴展函數AcceptEx
	GUID GuidAcceptEx = WSAID_ACCEPTEX;
	DWORD dwBytes;
	WSAIoctl(pListen->s,
	SIO_GET_EXTENSION_FUNCTION_POINTER,
	&GuidAcceptEx,
	sizeof(GuidAcceptEx),
	&pListen->lpfnAcceptEx,
	sizeof(pListen->lpfnAcceptEx),
	&dwBytes,
	NULL,
	NULL);
	// 創建用來重新建立g_events 數組的事件對象
	g_events[0] = ::WSACreateEvent();
	// 在此可以投遞多個接受I/O 請求
	for(int i=0; i<5; i++)
	{ 
		PostAccept(GetBufferObj(pListen, BUFFER_SIZE)); 
	}
	while(TRUE)
	{
		int nIndex = ::WSAWaitForMultipleEvents(g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE);
		if(nIndex == WSA_WAIT_FAILED)
		{ 
			printf("WSAWaitForMultipleEvents() failed \n");
			break;
		}
		nIndex = nIndex - WSA_WAIT_EVENT_0;
		for(int i=0; i<=nIndex; i++)
		{
			int nRet = ::WSAWaitForMultipleEvents(1, &g_events[i], TRUE, 0, FALSE);
			if(nRet == WSA_WAIT_TIMEOUT)
			{
				continue;
			}
			else
			{ 
				::WSAResetEvent(g_events[i]);
				// 重新建立g_events 數組
				if(i == 0)
				{ 
					RebuildArray();
					continue;
				}
				// 處理這個I/O
				PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);
				if(pBuffer != NULL)
				{
					if(!HandleIO(pBuffer))
					RebuildArray();
				}
			}
		}
	}
	return 0;
}


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