IOCP使用

      昨天刚刚用IOCP写了一个小例子。

     iocp在服务器和客户端一对多或者是多对多的情况下是很好用的。

     之前很通俗的做法就是使用socket()创建一个套接字,然后只能用于一个连接,所以这个socket和连接需要时刻记住,并且不同的连接还需要去找不同的socket。很是麻烦。

 现在使用IOCP就会变得简单很多。事实上我对他的理解是,他就是一个I/O队列,当然队列创建以后,他自己就可以处理一个队列的连接。

示例代码:

/这两个结构体也很重要,在IOCP中有一个叫重叠使用的概念。既然是重叠,我觉得可以理解为重复使用,反正就是多次使用。
CPKey叫完成键,一般在使用时包含是socket信息,CPOverlap就比较多了,但是里面必须含有的一个元素是OVERLAP这个参数,它是至关重要的。正是因为他才可以重叠使用。还有就是在阻塞操作时,这个是一个出参,里面可以带出任何你想要的链路信息,只需要把你想要的放在这个结构体中即可。

struct CPKey
{
	SOCKET _sock;
};

struct CPOverlap
{
	OVERLAPPED _overlap;

	int _type;
	sockaddr addr;
	int addr_len;
	WSABUF _wsa_buff;
	char buff[1024];

	CPOverlap()
	{
		memset(this, 0, sizeof(CPOverlap));
		_type = 0;
		addr_len = sizeof(addr);
		_wsa_buff.buf = buff;
		_wsa_buff.len = sizeof(buff);		
		
	}
};
</pre><p>主代码</p><p><pre name="code" class="cpp">HANDLE _iocp;
unsigned int __stdcall Thr(void*)
{
	while(true)
	{
		CPKey* cp_key = 0;
		CPOverlap* cp_overlap = 0;
		DWORD bufflen = 0;

		DWORD ret = GetQueuedCompletionStatus(_iocp, &bufflen, (PULONG_PTR)&cp_key, reinterpret_cast<LPOVERLAPPED *>(&cp_overlap), INFINITE);

		if (ret == 0)
		{
			DWORD err = GetLastError();
			cout << err;
		}

		switch(cp_overlap->_type)
		{
		case 0:
			{
				cout << cp_overlap->buff << endl;
				cout << bufflen << endl;
				//Sleep(1000 * 3);

				char buf[1024] ="reer";
				WSABUF wb;
				wb.buf = buf;
				wb.len = 4;

				DWORD ret_bytes = 0;
				DWORD ret_flag = 0;

				int ret = WSARecvFrom(cp_key->_sock, &cp_overlap->_wsa_buff, 1, &ret_bytes, &ret_flag, (sockaddr*)&cp_overlap->addr, &cp_overlap->addr_len, &cp_overlap->_overlap, 0);

				CPOverlap ol_send = *cp_overlap;
				ol_send._type = 1;
				int a = WSASendTo(cp_key->_sock,&wb,1,&ret_bytes,0,(sockaddr*)&ol_send.addr,ol_send.addr_len,
					&ol_send._overlap,0);
			}
			break;
		default:
			break;
		}
	}
	return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsadata;
	WSAStartup(0x0202, &wsadata);

	_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (NULL == _iocp)
	{
		return 0;
	}

	_beginthreadex(0, 0, Thr, 0, 0, 0);

	SOCKET sock = /*socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);*/WSASocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP,0,0,WSA_FLAG_OVERLAPPED);/*socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);*/
	if (SOCKET_ERROR == sock)
	{
		CloseHandle(_iocp);
		return 0;
	}
	DWORD err = GetLastError();
	cout << "socket() " << err << endl;

	sockaddr_in addr;
	addr.sin_addr.S_un.S_addr = INADDR_ANY;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6667);
	int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));
	if (0 != ret)
	{
		closesocket(sock);
		CloseHandle(_iocp);
		return 0;
	}
	timeval tv = {3, 0};
	//int set = setsockopt(sock, IPPROTO_UDP, SO_RCVTIMEO, (char*)&tv, sizeof(timeval)); 
	//err = GetLastError();

	CPKey key;
	key._sock = sock;
	CreateIoCompletionPort((HANDLE)sock, _iocp, (ULONG_PTR)&key, 0);

	CPOverlap overlap;
	DWORD ret_bytes = 0;
	DWORD flag = 0;
	ret = WSARecvFrom(sock, &overlap._wsa_buff, 1, &ret_bytes, &flag, &(overlap.addr), &overlap.addr_len, &overlap._overlap, LPWSAOVERLAPPED_COMPLETION_ROUTINE());
	err = GetLastError();
	cout << "RECVROM " << err << endl;
	//ret = WSARecvFrom(sock, &overlap._wsa_buff, 1, &ret_bytes, &flag, (sockaddr*)&overlap.addr, &overlap.addr_len, &overlap._overlap, 0);

	Sleep(100000000000);
	system("pause");
	return 0;
}


代码框架
1,首先创建一个IOCP句柄

2,启动线程,那么进入阻塞状态,不断的收消息,处理消息

3,创建socket,并将socket和IOCP句柄绑定。

4,投递接受操作,那么一旦收到消息,阻塞停止,进入下面的操作。


所使用到的函数
1,创建IOCP句柄

HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);创建IO完成端口。字面解释即为此。

2,启动线程

GetQueuedCompletionStatus();

一共有五个参数

   1.入参,创建好的IOCP句柄

   2,出参,DWORD指针,可以返回此次操作收到消息的长度

   3,出参,PULONG_PTR,同样也是一个指针的指针,一般定义一个空的CPKey指针,将其指针的指针传进去。

   4,出参,LPOVERLAPPED *,这里需要一个overlap的指针,所以上面说CPOverlap中定义一个OVERLA很重要,这里使用。

   5,入参,阻塞时间,一般设为INFINITE,直到等到响应为之。这是一个阻塞函数,一旦投递的请求收到响应,那么阻塞解除。

3,启动socket

这里使用的是WSASocket而不是socket(),如果使用socket是得不到效果的,而且在投递请求之后,错误码会返回为10057。使用WSASocket主要是因为这样建立的socket有重叠使用的功能,而socket()创建的socket没有这个功能。这个地方需要注意。

4,将iocp句柄和socket绑定

CreateIOCompletionPort((HNADLE)sock, iocp, (ULONG_PTR)&cpkey, 0);

5,投递请求

比如说我这里做的是接受消息的请求。使用的是WSARecvFrom()函数

   1,socekt,即为创建好的socket

   2,buffer,这个buffer类型是LPWSABUFF,所以在CPOverlap中buffer的类型为WSABUFF,

   3,BUFFER个数,

   4,收到消息的长度指针

   5,标志  

   6,对端地址指针

   7,对端地址长度指针

   8,是一个函数指针,可以带出错误信息或者是传输信息

在投递请求时会把这些信息都填写好,那么在收到请求之后,阻塞消除,那么在阻塞函数中填写的指针会将这些值带出来,所以说那边的出参和投递请求时的入参是对应的。只不多请求收到之后还会做一些处理,比如说我们在投递请求之前并没有将对端的地址信息写好,但是收到请求之后,CPOVerlap中可以直接解析出对端的地址信息。也是很方便的。
还有一个需要注意的是,投递请求实一直再做的,有一次请求,那么才会收到一次响应,如果没有投递,是收不到响应的。


 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值