widnows iocp 的理解

第一步:创建监听socket(wsasocket使用重叠结构)

第二步:创建iocp(createiocompleteport),与监听的socket的关联起来(createiocompleteport)【完成键表示这和监听socket的连接信息,可以自己定义结构和这个socket产生联系】

第三步:使用acceptex接受连接(预先取出一个socket来接受新来的客户端socket)【这里需要io数据结构 必须有重叠结构和socket,缓冲区】

第四步:工作线程通过getqueuedcompletestatus获取io的状态和操作【在接受连接完成后需要重新发送连接下一个的请求,投递一个接受请求,等待新的连接到来】


例子如下:(转载)

//  by SinCoder  Blog : hi.baidu.com/sincoder
/*
#include <WINSOCK2.H>
#include <windows.h>
#include <MSWSOCK.H>
#include <STDIO.H>
#pragma  comment(lib,"Mswsock.lib")
#pragma comment(lib,"ws2_32")


#define  DATA_LEN 1024


HANDLE hIocp           = INVALID_HANDLE_VALUE;
SOCKET m_Listen_Socket = INVALID_SOCKET;


enum
{
IO_READ =0,
IO_WRITE,
IO_ACCEPT,
IO_QUIT,
IO_UNDEFINE        
};


class Per_Handle_Data
{
public:
SOCKET s_Socket;
};


class Per_Io_Data
{
public:
OVERLAPPED O_Overlapped;
WSABUF     buff;
BYTE       Data[DATA_LEN];
int        IO_TYPE;
SOCKET     New_Socket;
};


DWORD WINAPI WorkerThread(LPVOID lparam)
{
//线程就不开了。。这只是个例子 ,
return 0;
}


void PostIocpRecv(Per_Handle_Data *Iocp_Handle_Data)
{
Per_Io_Data *p_Io_Data=new Per_Io_Data;
p_Io_Data->buff.buf=(char *)p_Io_Data->Data;
p_Io_Data->buff.len=DATA_LEN;
p_Io_Data->IO_TYPE=IO_READ;


ZeroMemory(&p_Io_Data->O_Overlapped,sizeof(OVERLAPPED));


DWORD Bytes;
DWORD Flags=0;
WSARecv(Iocp_Handle_Data->s_Socket,&p_Io_Data->buff,1,&Bytes,&Flags,&p_Io_Data->O_Overlapped,NULL);

}


void PostIocpSend(SOCKET m_Socket,BYTE *data,int datalen)
{
Per_Io_Data *p_Io_Data=new Per_Io_Data;
memcpy(&p_Io_Data->Data,data ,datalen);
p_Io_Data->buff.buf=(char *)p_Io_Data->Data;
p_Io_Data->buff.len=datalen;
p_Io_Data->IO_TYPE=IO_WRITE;


ZeroMemory(&p_Io_Data->O_Overlapped,sizeof(OVERLAPPED));


DWORD Bytes;
DWORD Flags=0;
WSASend(m_Socket,&p_Io_Data->buff,1,&Bytes,Flags,&p_Io_Data->O_Overlapped,NULL);
}


void PostIocpAccept()
{
SOCKET m_New_Socket= WSASocket(AF_INET,SOCK_STREAM,0,NULL,NULL,WSA_FLAG_OVERLAPPED);


Per_Handle_Data *p_Handle_Data=new Per_Handle_Data;
p_Handle_Data->s_Socket=m_New_Socket;


Per_Io_Data *p_Io_Data=new Per_Io_Data;
ZeroMemory(&p_Io_Data->O_Overlapped,sizeof(OVERLAPPED));
p_Io_Data->IO_TYPE = IO_ACCEPT;
p_Io_Data->New_Socket=m_New_Socket;
DWORD Bytes_Recv=0;


AcceptEx(m_Listen_Socket,
m_New_Socket,
p_Io_Data->Data,
DATA_LEN-2*(sizeof(sockaddr_in)+16),
sizeof(sockaddr_in)+16,
sizeof(sockaddr_in)+16,
&Bytes_Recv,
&p_Io_Data->O_Overlapped);
}


void PostIocpExit()
{

Per_Handle_Data *p_Handle_Data=new Per_Handle_Data;
p_Handle_Data->s_Socket=m_Listen_Socket;




Per_Io_Data *p_Io_Data=new Per_Io_Data;
ZeroMemory(&p_Io_Data->O_Overlapped,sizeof(OVERLAPPED));
p_Io_Data->IO_TYPE = IO_QUIT;
p_Io_Data->New_Socket=m_Listen_Socket;
p_Io_Data->buff.buf=(char *)p_Io_Data->Data;
p_Io_Data->buff.len=DATA_LEN;

PostQueuedCompletionStatus(hIocp, sizeof(Per_Handle_Data), (ULONG_PTR)p_Handle_Data, (OVERLAPPED*) (&p_Io_Data->O_Overlapped) );
}


int main()
{
#pragma region 初始化
WSAData wsa;
WSAStartup(MAKEWORD(2,2),&wsa);


hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,0);


m_Listen_Socket=WSASocket(AF_INET,SOCK_STREAM,0,NULL,NULL,WSA_FLAG_OVERLAPPED);


sockaddr_in m_addr;
m_addr.sin_family=AF_INET;
m_addr.sin_addr.S_un.S_addr=inet_addr("0.0.0.0");  //在所有的ip上监听,,
m_addr.sin_port=htons(4646);


bind(m_Listen_Socket,(sockaddr *)&m_addr,sizeof(sockaddr));


listen(m_Listen_Socket,SOMAXCONN);


SOCKET m_New_Socket= WSASocket(AF_INET,SOCK_STREAM,0,NULL,NULL,WSA_FLAG_OVERLAPPED);


Per_Handle_Data *p_Handle_Data=new Per_Handle_Data;
p_Handle_Data->s_Socket=m_Listen_Socket;


CreateIoCompletionPort((HANDLE)m_Listen_Socket,hIocp,(unsigned long)p_Handle_Data,0);

//XXXX {的确没必要把监听的socket也关联到 iocp 上 因为监听的socket上没有 IO 操作 。。}
//客户端来了 就有了。。。我晕。。

Per_Io_Data *p_Io_Data=new Per_Io_Data;
ZeroMemory(&p_Io_Data->O_Overlapped,sizeof(OVERLAPPED));
p_Io_Data->IO_TYPE = IO_ACCEPT;
p_Io_Data->New_Socket=m_New_Socket;
DWORD Bytes_Recv=0;


AcceptEx(m_Listen_Socket,
m_New_Socket,
p_Io_Data->Data,DATA_LEN-2*(sizeof(sockaddr_in)+16),
sizeof(sockaddr_in)+16,
sizeof(sockaddr_in)+16,
&Bytes_Recv,&p_Io_Data->O_Overlapped);
#pragma endregion 




Per_Handle_Data *Lp_Handle_Data;
Per_Io_Data     *Lp_Io_Data;
OVERLAPPED      *m_Overlapped;
DWORD Bytes=0;


while(TRUE)
{
GetQueuedCompletionStatus(hIocp,&Bytes,(PULONG_PTR)&Lp_Handle_Data,&m_Overlapped,WSA_INFINITE);
Lp_Io_Data = (Per_Io_Data *)CONTAINING_RECORD(m_Overlapped, Per_Io_Data, O_Overlapped);


if (Bytes<=0&&Lp_Io_Data->IO_TYPE!=IO_ACCEPT)
{
printf("Socket: %d exit !\r\n",Lp_Handle_Data->s_Socket);
delete Lp_Handle_Data;
delete Lp_Io_Data;
continue;
}


switch(Lp_Io_Data->IO_TYPE)
{
case IO_ACCEPT:
{
printf("\r\nIO_ACCEPT !\r\n");
Per_Handle_Data *per_Handle_Data=new Per_Handle_Data;
per_Handle_Data->s_Socket=Lp_Io_Data->New_Socket;
CreateIoCompletionPort((HANDLE)Lp_Io_Data->New_Socket,hIocp,(unsigned long)per_Handle_Data,0);
PostIocpAccept();
PostIocpRecv(per_Handle_Data);
}
break;
case IO_READ:
printf("\r\nIO_READ !\r\n");
Lp_Io_Data->Data[Bytes] = '\0';
printf("Socket: %d --> Data: %s\r\n",Lp_Handle_Data->s_Socket,Lp_Io_Data->Data);
PostIocpRecv(Lp_Handle_Data);
PostIocpSend(Lp_Handle_Data->s_Socket,(BYTE*)"123456",6);
delete Lp_Io_Data;
break;
case IO_WRITE:
printf("\r\nIO_WRITE \r\n");
delete Lp_Io_Data;
PostIocpExit();
break;
case IO_QUIT:
printf("Let me quit ! \r\n");
delete Lp_Io_Data;
delete Lp_Handle_Data;
goto END;
case  IO_UNDEFINE:
printf("\r\nIO_UNDEFINE \r\n");
delete Lp_Io_Data;
break;
default: break;
}
}
END:
//Do some thing when we exit !!
// .......
closesocket(m_Listen_Socket);
WSACleanup();
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近有项目要做一个高性能网络服务器,决定下功夫搞定完成端口(IOCP),最终花了一个星期终于把它弄清楚了,并用C++写了一个版本,效率很不错。 但,从项目的总体需求来考虑,最终决定上.net平台,因此又花了一天一夜弄出了一个C#版,在这与大家分享。 一些心得体会: 1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。 6、测试结果:(在我的笔记本上时行的,我的本本是T420 I7 8G内存) 100客户 100,000(十万次)不间断的发送接收数据(发送和接收之间没有Sleep,就一个一循环,不断的发送与接收) 耗时3004.6325 秒完成 总共 10,000,000 一千万次访问 平均每分完成 199,691.6 次发送与接收 平均每秒完成 3,328.2 次发送与接收 整个运行过程中,内存消耗在开始两三分种后就保持稳定不再增涨。 看了一下对每个客户端的延迟最多不超过2秒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值