1. 程式人生 > >轉發:IOCP模型示例程式碼

轉發:IOCP模型示例程式碼

#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>

using namespace std;

#pragma comment(lib, "ws2_32.lib") //socket動態連結庫
#pragma comment(lib, "kernel32.lib") //IOCP動態連結庫


//重疊IO用到的結構體,記錄IO資料

const int DataBufferSize = 2* 1024;
typedef struct s1
{
	OVERLAPPED overlapped;
	WSABUF databuff;
	char cBuffer[DataBufferSize];
	int BufferLen;
	int OperationType;

}PER_IO_DATA, *LPPER_IO_DATA;

//記錄每個連線的socket資訊
typedef struct
{
	SOCKET socket;
	SOCKADDR_STORAGE clientAddr;
}PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

//定義全域性變數
const int nDefaultPort = 6000;
vector<PER_HANDLE_DATA *> g_vClientGroup;//記錄客戶端的容器

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
DWORD WINAPI ServerSendThread(LPVOID lpParam);

int main()
{
	WORD wVersionRequested = MAKEWORD(2,2);//請求版本為2.2的WINSOCKET庫
	WSADATA wsaData; //接收windows socket的結構資訊
	DWORD err = WSAStartup(wVersionRequested, &wsaData);
	if(0 != err)
	{
		cout<<"Request Windows Socket Library Error!"<<endl;
		return -1;
	}
	if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		cout<<"Request Windows Socket Version 2.2 Error!"<<endl;
		return -1;
	}
	//建立IOCP的核心物件
	HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if(NULL == completionPort)
	{
		cout<<"CreateIoCompletionPort failed Error!"<<GetLastError()<<endl;
		return -1;
	}

	//建立處理器的核心數量
	SYSTEM_INFO mySysInfo;
	GetSystemInfo(&mySysInfo);

	//建立工作執行緒
	for(DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); i++)
	{
		HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);
		if(NULL == ThreadHandle)
		{
			cout<<"Create Thread Handle failed Error:"<<GetLastError()<<endl;
			return -1;
		}
		CloseHandle(ThreadHandle);
	}

	//建立伺服器套接字
	SOCKET srvSocket = socket(AF_INET, SOCK_STREAM, 0);
	//繫結SOCKET到本機
	SOCKADDR_IN srvAddr;
	srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);   
	srvAddr.sin_family = AF_INET;
	srvAddr.sin_port = htons(nDefaultPort);

	int nBindResult = bind(srvSocket, (SOCKADDR *)&srvAddr, sizeof(SOCKADDR));
	if(SOCKET_ERROR == nBindResult)
	{
		cout<<"Bind failed Error:"<<GetLastError()<<endl;
		return -1;
	}

	//將socket設定為監聽模式
	int nListenResult = listen(srvSocket, SOMAXCONN);
	if(SOCKET_ERROR == nListenResult)
	{
		cout<<"Listen failed Error:"<<GetLastError()<<endl;
		return -1;
	}
	cout<<"伺服器已啟動,等待客戶端連線..."<<endl;

	//建立用於傳送資料的執行緒
	HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);

	while(true)
	{
		PER_HANDLE_DATA * lpPer_Handle_Data = NULL;
		SOCKADDR_IN sockaddr_client;
		int nsocketaddr_client;
		SOCKET acceptSocket;

		nsocketaddr_client = sizeof(sockaddr_client);
		acceptSocket = accept(srvSocket, (sockaddr *)&sockaddr_client, &nsocketaddr_client);
		if(SOCKET_ERROR == acceptSocket)
		{
			cout<<"Accept Socket Error:"<<GetLastError()<<endl;
			return -1;
		}

		//建立用來和套接字關聯的單控制代碼資料資訊結構
		lpPer_Handle_Data = new PER_HANDLE_DATA;
		lpPer_Handle_Data->socket = acceptSocket;
		memcpy(&lpPer_Handle_Data->clientAddr, &sockaddr_client, nsocketaddr_client);

		g_vClientGroup.push_back(lpPer_Handle_Data);

		//將接收套接字和完成埠關聯
		CreateIoCompletionPort((HANDLE)(lpPer_Handle_Data->socket), completionPort, (DWORD)lpPer_Handle_Data, 0);

		LPPER_IO_DATA perIoData = NULL;

		memset(perIoData, 0, sizeof(struct s1));
		//perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
		//perIoData = new PER_IO_DATA;
		//ZeroMemory(&(perIoData->overlapped), sizeof(OVERLAPPED));
		perIoData->databuff.len = 1024;
		perIoData->databuff.buf = perIoData->cBuffer;
		perIoData->OperationType = 0; //read

		DWORD RecvBytes;
		DWORD Flags = 0;
		WSARecv(lpPer_Handle_Data->socket, &(perIoData->databuff), 1, &RecvBytes, &Flags, &(perIoData->overlapped), NULL);


	}
	return 0;
}

//開始伺服器工作執行緒函式
DWORD WINAPI ServerWorkThread(LPVOID IpParam)
{
	HANDLE CompletionPort = (HANDLE)IpParam;
	DWORD BytesTransferred;
	LPOVERLAPPED IpOverlapped;
	LPPER_HANDLE_DATA PerHandleData = NULL;
	LPPER_IO_DATA lpPerIoData = NULL;
	DWORD RecvBytes;
	DWORD Flags = 0;
	BOOL bRet = false;

	while(true)
	{
		bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED *)&IpOverlapped, INFINITE);
		if(bRet == 0)
		{
			cout<<"GetQueuedCompletionStatus Error:"<<GetLastError()<<endl;
			return -1;
		}

		lpPerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);
		if(0 == BytesTransferred)
		{
			closesocket(PerHandleData->socket);
			GlobalFree(PerHandleData);
			GlobalFree(lpPerIoData);
			continue;
		}

		//開始資料處理,接收來至客戶端的資料
		WaitForSingleObject(hMutex, INFINITE);
	
		cout<<"A Client says:"<<lpPerIoData->databuff.buf<<endl;
		ReleaseMutex(hMutex);
		//為下一個重疊呼叫建立單I/O操作資料
		ZeroMemory(&(lpPerIoData->overlapped), sizeof(OVERLAPPED)); //清空記憶體
		lpPerIoData->databuff.len = 1024;
		lpPerIoData->databuff.buf = lpPerIoData->cBuffer;
		lpPerIoData->OperationType = 0;
		WSARecv(PerHandleData->socket, &(lpPerIoData->databuff), 1, &RecvBytes, &Flags, &(lpPerIoData->overlapped), NULL);
	}
	return 0;
}

DWORD WINAPI ServerSendThread(LPVOID IpParam)
{
	while(true)
	{
		char talk[200]={0x00};
		gets(talk);
		int len;
		for(len=0; talk[len] != '\0'; ++len)
		{
		}
		talk[len]='\n';
		talk[++len] = '\0';
		cout<<"I Say :";
		cout<<talk<<endl;
		WaitForSingleObject(hMutex, INFINITE);
		for(int i=0; i<g_vClientGroup.size(); i++)
		{
			send(g_vClientGroup[i]->socket, talk, 200, 0);
		}
		ReleaseMutex(hMutex);
	}
	return 0;
}