IO完成端口入门级详细注释代码

#include "StdAfx.h"
#include "MyServer.h"
#include <WinSock2.h>

#pragma comment(lib, "Ws2_32.lib")//windows socket库
typedef struct __ARG
{
	HANDLE hComplete;
	SOCKET server;
}__ARG;

typedef struct CompleteKey
{
	SOCKET socket;
}CompleteKey;

//overlap一定要放在第一位置
typedef struct
{
	OVERLAPPED      overlap;       //重叠io必须的结构体
	WSABUF          buf;         //系统缓冲区指针	
	char            data[1024];//数据缓存区
	DWORD           NumberOfBytesRecvd;//发送或者接收的数据字节数
	DWORD           Flags;
	int OperationType;//操作类型
}IO_DATA;    //定义一个结构体保存IO数据

void initIoData(IO_DATA* ioData)//初始化io数据包
{
	memset(ioData,0,sizeof(IO_DATA));	
	ioData->buf.buf=ioData->data;
	ioData->buf.len=100;
	ioData->Flags=0;
	
	ioData->NumberOfBytesRecvd=100;
}

//弹出对话框
void myBox(char* format,...)
{
	char buf[100]={0};
	va_list ap;
	va_start(ap, format);
	sprintf_s(buf,format, ap);
	va_end(ap);
	MessageBoxA(0,buf,"",MB_OK);
}

//测试用函数
void test(SOCKET socket)
{
	char buf[100]={0};
	recv(socket,buf,100,0);
	myBox(buf);
}

//创建一个IO完成端口
HANDLE CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads)
{
	return( CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, dwNumberOfConcurrentThreads));
}

//将设备与完成端口管理起来
BOOL AssociateDeviceWithCompletionPort(HANDLE hCompletionPort, HANDLE hDevice,  DWORD dwCompletionKey) 
{
	HANDLE h = CreateIoCompletionPort(hDevice,   hCompletionPort, dwCompletionKey,   0);
	return(h == hCompletionPort);
}

//初始化winsock环境
void initSocket()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		printf("WSAStartup failed with error: %d\n", err);
		return ;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return ;
	}
}

MyServer::MyServer(void)
{
	//跟客户端端一样 初始化windows socket环境
	initSocket();

	//创建socket句柄	
	socksrv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(socksrv==INVALID_SOCKET)
	{
		myBox("create socket server error!");
	}

	//地址设定
	addrsrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrsrv.sin_family = AF_INET;
	//端口设定
	addrsrv.sin_port = htons(9999);

	//绑定地址和端口
	bind(socksrv,(sockaddr*)&addrsrv,sizeof(sockaddr));

	//监听客户端请求
	listen(socksrv,5);
}

MyServer::~MyServer(void)
{

}

//服务器工作线程
DWORD WINAPI ServerWorkerThread(LPVOID arg) 
{
//	myBox("working\n");
	IO_DATA *data=0;	
	CompleteKey *key=0;
	DWORD recved;

	initSocket();
	while(1)
	{
		//异步接收的时候,传的是结构体中overlap的地址,这个地址和io_data结构体的地址是一致的 正是利用了这点,才巧妙的实现了异步获取数据
		//data返回的指针即为异步接收的时候传入的指针
		//key返回的指针即为绑定设备时候传入的指针
		GetQueuedCompletionStatus((HANDLE)arg,&recved,(LPDWORD)&key,(LPOVERLAPPED*)&data,WSA_INFINITE);
		
		//接收数据为0 说明客户端关闭连接
		if(recved==0)
		{
			closesocket(key->socket);
			delete key;
			GlobalFree(data);
			continue;
		}
		
		data->data[data->NumberOfBytesRecvd]=0;
		
		//讲数据不改动的发送给客户端
		send(key->socket,data->data,data->NumberOfBytesRecvd,0);

		initIoData(data);//初始化IO数据包

		//处理完数据后再次触发一次异步接收
		WSARecv(key->socket,&data->buf,1, &data->NumberOfBytesRecvd, &data->Flags,&data->overlap, NULL) ;
	}
	return 0;
}


//接收请求的线程
DWORD WINAPI MainThread(LPVOID arg) 
{
	__ARG *args=(__ARG*)arg;
	SOCKET client;
	initSocket();
	while(1)
	{
		SOCKADDR_IN saRemote;
		int nRemoteLen = sizeof(saRemote);

		//接受一个连接请求
		client= accept(args->server, (sockaddr*)&saRemote, &nRemoteLen);
		
		if(client==SOCKET_ERROR)
		{
			myBox("client error!\n");
			break;
		}

		//创建一个完成键
		CompleteKey* key=new CompleteKey();
		key->socket=client;
		
		//把接收到的客户socket当成设备管理到完成端口
		//key   标识这个设备的一些数据  		
		if(AssociateDeviceWithCompletionPort(args->hComplete,(HANDLE)client,(DWORD)key)==FALSE)//关联设备
			myBox("设备关联失败!");
		
		IO_DATA *ioData = (IO_DATA *)GlobalAlloc(GPTR,sizeof(IO_DATA));//初始化一个io完成数据
		
		initIoData(ioData);		
		
		//test(client);

		//触发一个异步接收请求  该函数调用后立马返回
		int ret=WSARecv(client,&ioData->buf,1, &ioData->NumberOfBytesRecvd, &ioData->Flags,&ioData->overlap, NULL) ;

		//注意多判断一个错误码~  如果错误码是WSA_IO_PENDING 其实也是正常状态,因为
		//该错误码表明异步操作成功,等待指示。(客户端调用connect后被阻塞了,就会出现这种情况)
		if(ret==SOCKET_ERROR && WSAGetLastError()!=WSA_IO_PENDING)			
		{
			myBox("wsaRecv error :%d \n",WSAGetLastError());	
		}			
	}
	return 0;
}




void MyServer::begin()
{
	int threads=2;
	HANDLE hCompletion = CreateNewCompletionPort(threads);//创建两个工作线程
	if(hCompletion==0)
	{
		myBox("端口创建失败!");
		return;
	}
	int i=0;
	DWORD threadId;
	for(i=0;i<threads;i++)
	{
		CreateThread(0,0,ServerWorkerThread,hCompletion,0,&threadId);
	}
	__ARG *arg=new __ARG();
	arg->hComplete=hCompletion;
	arg->server=socksrv;

	CreateThread(0,0,MainThread,arg,0,&threadId);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值