dlutyuanhongl(乱石铺街)的BLOG
VC知识库BLOG
首页
新随笔
联系
聚合
登录
11 Posts :: 1 Stories :: 73 Comments :: 2 Trackbacks
留言簿(8)
给我留言
查看公开留言
查看私人留言
随笔分类
与琳同行 (0)
Linux/C/C++ (0)
失败的研究生生涯 (0)
星座与易经 (1)
网络编程 (1)
我在DLUT的日子 (1)
附庸风雅 (3)
随笔档案
2005年5月 (1)
2005年4月 (4)
2005年3月 (6)
文章分类
附庸风雅 (0)
文章档案
2005年12月 (1)
相册
岁月无情 (1)
搜索
最新评论
1. re: 恍然大悟---拿出来自勉
http://skywalkerj.spaces.live.com/blog/cns
!49c467f1b758d6fc!146.entry
我是看了这个才明白的,呵呵..........
再牛的人也有未知的技术盲区,不用晕.
--hsc456
2. re: 轻轻松松搞定VC操作Excel之一
yuioftyu
--gui
3. re: 学习笔记之I/O完成端口(IOCP)
想问下下,在主线程的tmain函数里一直在接受客户端的连接,那如果有界面的程序,界面能刷新吗,是否还要另创建一个线程,来维护界面的更新
-------------------------------------------
可以投递ACCEPT 事件的呀,如果不想让IO完成端口接收ACCEPT 事件 可以开个线程 来accept!!
--hurryboylqs
4. re: 轻轻松松搞定VC操作Excel之一
楼主,怎样才能得到源码呢?
--yike0829
5. good for me
正找这些信息,有些用处。
--sjdev
6. re: 光驱,软驱,USB的禁用和启用
我想主要禁用的是USB设备,如果是存储设备就可手动禁用或启用。如果是输入设血,如键盘鼠标之类的,只是一被判断为非存储设备,则任何时候都启用该USB端口。
--USB
7. re: 学习笔记之I/O完成端口(IOCP)
想问下下,在主线程的tmain函数里一直在接受客户端的连接,那如果有界面的程序,界面能刷新吗,是否还要另创建一个线程,来维护界面的更新
--rong
8. re: 学习笔记之I/O完成端口(IOCP)
--kilobird
9. re: 学习笔记之I/O完成端口(IOCP)
--楠楠
10. re: 学习笔记之I/O完成端口(IOCP)
因为,这段代码是抄袭的,所以他不知道为什么
--waini12
阅读排行榜
1. 轻轻松松搞定VC操作Excel之一(10985)
2. 学习笔记之I/O完成端口(IOCP)(8888)
3. 恍然大悟---拿出来自勉(3652)
4. 学习笔记之一 : 延迟加载DLL———VC++6.0的出色特性(3489)
5. 君子终日乾乾,夕惕若,厉,无咎(3224)
6. 光驱,软驱,USB的禁用和启用(3171)
7. 告别VCKBASE论坛(2831)
8. 再看央视版《水浒》(2713)
9. 天下第一行书---dlutyuanhongl(2440)
10. 天下第二行书---dlutyuanhongl(2407)
评论排行榜
1. 轻轻松松搞定VC操作Excel之一(29)
2. 学习笔记之I/O完成端口(IOCP)(11)
3. 再看央视版《水浒》(8)
4. 恍然大悟---拿出来自勉(7)
5. 光驱,软驱,USB的禁用和启用(6)
6. 告别VCKBASE论坛(6)
7. 君子终日乾乾,夕惕若,厉,无咎(5)
8. 天下第二行书---dlutyuanhongl(2)
9. 学习笔记之一 : 延迟加载DLL———VC++6.0的出色特性(1)
10. 天下第一行书---dlutyuanhongl(0)
学习笔记之I/O完成端口(IOCP)
最近要做一个网络方面的小东东,基于C/S模式的。都说IOCP可以使系统达到最佳的性能,因此我就比划了两下,献丑了。抄书开始。
从本质上说,完成端口模型要求创建一个windows完成端口对象,该对象通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。
首先要创建一个I/O完成端口对象,用它面向任意数量的套接字句柄,管理多个I/O请求。调用以下函数创建完成端口对象:
HANDLE
CreateIoCompletionPort(
HANDLE
FileHandle
,// 同IOCP关联在一起的套接字句柄
HANDLE
ExistingCompletionPort
,// IOCP句柄
ULONG_PTR
CompletionKey
, // 完成健
DWORD
NumberOfConcurrentThreads
// 在IOCP上,同时允许执行的线程数量
);
该函数有两个作用:
(1)创建一个完成端口对象
(2)将一个句柄同完成端口关联到一起
然后就要创建一定数量的工作者线程,以便在套接字的I/O请求投递给完成端口后,为完成端口提供服务。写文字描述很烦,还是看代码吧:
//
NetServer3.cpp : Defines the entry point for the console application.
//
#include
"
stdafx.h
"
#include
"
NetServer3.h
"
#include
<
winsock2.h
>
#pragma comment(lib,
"
ws2_32.lib
"
)
#include
<
iostream
>
using
namespace
std;
/**/
////////////////////////////////////////////////////////////////////////
//
#ifdef _DEBUG
#define
new DEBUG_NEW
#undef
THIS_FILE
static
char
THIS_FILE[]
=
__FILE__;
#endif
/**/
////////////////////////////////////////////////////////////////////////
//
//
单句柄数据
typedef
struct
tagPER_HANDLE_DATA
{
SOCKET Socket;
SOCKADDR_STORAGE ClientAddr;
//
将和这个句柄关联的其他有用信息,尽管放在这里面吧
}
PER_HANDLE_DATA,
*
LPPER_HANDLE_DATA;
//
但I/O操作数据
typedef
struct
tagPER_IO_DATA
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
char
buffer[
1024
];
int
BufferLen;
int
OperationType; // 可以作为读写的标志,为简单,我忽略了
}
PER_IO_DATA,
*
LPPER_IO_DATA;
DWORD WINAPI ServerWorkerThread(LPVOID lpParam);
/**/
///////////////////////////////////////////////////////////////////////////
//
//
The one and only application object
CWinApp theApp;
using
namespace
std;
int
_tmain(
int
argc, TCHAR
*
argv[], TCHAR
*
envp[])
{
int
nRetCode
=
0
;
//
initialize MFC and print and error on failure
if
(
!
AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(),
0
))
{
//
TODO: change error code to suit your needs
cerr
<<
_T(
"
Fatal Error: MFC initialization failed
"
)
<<
endl;
nRetCode
=
1
;
}
else
{
//
TODO: code your application's behavior here.
CString strHello;
strHello.LoadString(IDS_HELLO);
cout
<<
(LPCTSTR)strHello
<<
endl;
}
/**/
////////////////////////////////////////////////////////////////////////
//
HANDLE CompletionPort;
WSADATA wsd;
SYSTEM_INFO SystemInfo;
SOCKADDR_IN InternetAddr;
SOCKET Listen;
//
加载WinSock2.2
WSAStartup(MAKEWORD(
2
,
2
),
&
wsd);
//
1.创建一个I/O完成端口
CompletionPort
=
CreateIoCompletionPort(INVALID_HANDLE_VALUE,
NULL,
0
,
0
);
//
2.确定系统中有多少个处理器
GetSystemInfo(
&
SystemInfo);
//
3.基于系统中可用的处理器数量创建工作器线程
for
(
int
i
=
0
; i
<
SystemInfo.dwNumberOfProcessors;
++
i)
{
HANDLE ThreadHandle;
//
创建一个服务器的工作器线程,并将完成端口传递到该线程
ThreadHandle
=
CreateThread(NULL,
0
,
ServerWorkerThread,
CompletionPort,
0
,
NULL);
CloseHandle(ThreadHandle);
}
//
4.创建一个监听套接字,以下的套路都是固定的。
Listen
=
WSASocket(AF_INET,
SOCK_STREAM,
0
,
NULL,
0
,
WSA_FLAG_OVERLAPPED);
InternetAddr.sin_family
=
PF_INET;
InternetAddr.sin_port
=
htons(
5000
);
InternetAddr.sin_addr.s_addr
=
htonl(INADDR_ANY);
bind(Listen, (SOCKADDR
*
)
&
InternetAddr,
sizeof
(InternetAddr));
listen(Listen,
5
);
BOOL b
=
TRUE;
while
(b)
{
PER_HANDLE_DATA
*
PerHandleData
=
NULL;
SOCKADDR_IN saRemote;
SOCKET Accept;
int
RemoteLen;
//
5.接收连接,并分配完成端口,这儿可以用AcceptEx来代替,以创
// 建可伸缩的Winsock应用程序。
RemoteLen
=
sizeof
(saRemote);
Accept
=
accept(Listen, (SOCKADDR
*
)
&
saRemote,
&
RemoteLen);
//
6.创建用来和套接字关联的单句柄数据信息结构
PerHandleData
=
(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,
sizeof
(PER_HANDLE_DATA));
cout
<<
"
Socket number
"
<<
Accept
<<
"
connected
"
<<
endl;
PerHandleData
->
Socket
=
Accept;
memcpy(
&
PerHandleData
->
ClientAddr,
&
saRemote, RemoteLen);
//
7.将接受套接字和完成端口关联起来
CreateIoCompletionPort((HANDLE)Accept,
CompletionPort,
(DWORD)PerHandleData,
0
);
//
开始在接受套接字上处理I/O
//
使用重叠I/O机制,在新建的套接字上投递一个或多个异步
// WSARecv 或 WSASend请求。这些I/O请求完成后,工作者线程
// 会为I/O请求提供服务,之后就可以坐享其成了
static
int
const
DATA_BUFSIZE
=
4096
; //
DWORD RecvBytes
=
0
;
DWORD Flags
=
0
;
//
单I/O操作数据
LPPER_IO_DATA PerIoData
=
NULL;
PerIoData
=
(LPPER_IO_DATA)GlobalAlloc(GPTR,
sizeof
(PER_IO_DATA));
ZeroMemory(
&
(PerIoData
->
Overlapped),
sizeof
(OVERLAPPED));
PerIoData
->
DataBuf.len
=
1024
;
PerIoData
->
DataBuf.buf
=
PerIoData
->
buffer;
PerIoData
->
OperationType
=
0
;
//
read
WSARecv(PerHandleData
->
Socket,
&
(PerIoData
->
DataBuf),
1
,
&
RecvBytes,
&
Flags,
&
(PerIoData
->
Overlapped),
NULL);
}
/**/
////////////////////////////////////////////////////////////////////////
//
return
nRetCode;
}
/**/
////////////////////////////////////////////////////////////////////////
//
DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{
HANDLE CompletionPort
=
(HANDLE)lpParam;
DWORD BytesTransferred;
LPOVERLAPPED lpOverlapped;
LPPER_HANDLE_DATA PerHandleData
=
NULL;
LPPER_IO_DATA PerIoData
=
NULL;
DWORD SendBytes;
DWORD RecvBytes;
DWORD Flags;
BOOL bRet
=
FALSE;
while
(TRUE)
{
bRet
=
GetQueuedCompletionStatus(CompletionPort,
&
BytesTransferred,
(PULONG_PTR)
&
PerHandleData,
(LPOVERLAPPED
*
)
&
lpOverlapped,
INFINITE);
//
检查成功的返回,这儿要注意使用这个宏
CONTAINING_RECORD
PerIoData
=
(LPPER_IO_DATA)CONTAINING_RECORD(lpOverlapped,
PER_IO_DATA,
Overlapped);
//
先检查一下,看看是否在套接字上已有错误发生
if
(
0
==
BytesTransferred)
{
closesocket(PerHandleData
->
Socket);
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue
;
}
//
数据处理
// 成功了!!!这儿就收到了来自客户端的数据
cout
<<
PerIoData
->
DataBuf.buf
<<
endl;
Flags
=
0
;
//
为下一个重叠调用建立单I/O操作数据
ZeroMemory(
&
(PerIoData
->
Overlapped),
sizeof
(OVERLAPPED));
PerIoData
->
DataBuf.len
=
1024
;
PerIoData
->
DataBuf.buf
=
PerIoData
->
buffer;
PerIoData
->
OperationType
=
0
;
//
read
WSARecv(PerHandleData
->
Socket,
&
(PerIoData
->
DataBuf),
1
,
&
RecvBytes,
&
Flags,
&
(PerIoData
->
Overlapped),
NULL);
}
return
0
;
}
/**/
////////////////////////////////////////////////////////////////////////
//
当然为了测试,各种异常处理都没有写,大家不要学我哦。
posted on 2005-04-19 13:26 乱石铺街 阅读(8888)
评论(11)
编辑
收藏
Feedback
#
re: 学习笔记之I/O完成端口(IOCP)
2005-04-27 13:54
飞天
这里的写的代码还可以折叠起来。这么酷
#
re: 学习笔记之I/O完成端口(IOCP)
2005-04-27 14:51
dlutyuanhongl
嘿嘿,这是BLOG提供的功能!
#
re: 学习笔记之I/O完成端口(IOCP)
2005-11-15 10:37
songs8467
写的不错,佩服!
#
re: 学习笔记之I/O完成端口(IOCP)
2006-01-23 15:29
杀手K
不错,简单明了
#
re: 学习笔记之I/O完成端口(IOCP)
2006-07-29 15:10
boli
谢谢啊~~ 正是有楼主这样的人,才能让我们 复制,粘贴~ 不用一块砖一块砖的码了~ :P
#
re: 学习笔记之I/O完成端口(IOCP)
2007-03-05 17:11
murongtianfeng
WSARecv(...)在主程序的循环里已经投递了 为什么还要在 ServerWorkerThread()里最后的位置进行 wsarecv(...)投递?? 本人感觉没有必要吧 只要在主程序的循环里投递就可以达到不断接收用户请求的目的了
#
re: 学习笔记之I/O完成端口(IOCP)
2007-04-27 11:17
waini12
因为,这段代码是抄袭的,所以他不知道为什么
#
re: 学习笔记之I/O完成端口(IOCP)
2007-05-16 10:10
楠楠
回复murongtianfeng:
因为WSASend/WSARecv是异步的 IO操作,具体的IO处理过程由WINDOWS系统完成. 主程序循环投递了I/O操作之后,就会立即返回. 但是这个动作(接收到数据如何得知呢?)
是由WINDOWS系统完成的, 在系统实际的IO处理后,把结果送到完成端口上.(操作系统已经完成了, 但我们程序如何得知呢?)
假如, 如果有多个IO都完成了,那么就会在完成端口那里排成一个队列。 我们需要从队列中取出数据, 所以在ServerWorkerThread线程中用 GetQueuedCompletionStatus 函数可以得到这些队列化的完成包.
得到数据. 但是不是完了呢. 我们又投递WSARecv. 让操作系统来替我们接收你的包. 然后再次通知我们.....
依次连接不断...............
有什么时间想和我讨论: QQ: 11718111
#
re: 学习笔记之I/O完成端口(IOCP)
2007-06-05 17:56
kilobird
回复murongtianfeng:
因为WSASend/WSARecv是异步的 IO操作,具体的IO处理过程由WINDOWS系统完成. 主程序循环投递了I/O操作之后,就会立即返回. 但是这个动作(接收到数据如何得知呢?)
是由WINDOWS系统完成的, 在系统实际的IO处理后,把结果送到完成端口上.(操作系统已经完成了, 但我们程序如何得知呢?)
假如, 如果有多个IO都完成了,那么就会在完成端口那里排成一个队列。 我们需要从队列中取出数据, 所以在ServerWorkerThread线程中用 GetQueuedCompletionStatus 函数可以得到这些队列化的完成包.
得到数据. 但是不是完了呢. 我们又投递WSARecv. 让操作系统来替我们接收你的包. 然后再次通知我们.....
依次连接不断...............
有什么时间想和我讨论: QQ: 11718111
我是这样理解的,希望对大家有些帮组,WSARecv主要用来socket 和 overlappet 捆绑。希望多交通行好友 QQ: 308806913
#
re: 学习笔记之I/O完成端口(IOCP)
2007-07-03 19:52
rong
想问下下,在主线程的tmain函数里一直在接受客户端的连接,那如果有界面的程序,界面能刷新吗,是否还要另创建一个线程,来维护界面的更新
#
re: 学习笔记之I/O完成端口(IOCP)
2008-03-29 22:01
hurryboylqs
想问下下,在主线程的tmain函数里一直在接受客户端的连接,那如果有界面的程序,界面能刷新吗,是否还要另创建一个线程,来维护界面的更新
-------------------------------------------
可以投递ACCEPT 事件的呀,如果不想让IO完成端口接收ACCEPT 事件 可以开个线程 来accept!!
标题
姓名
主页
验证码
*
内容
Remember Me?
登录
使用高级评论
Top
[使用Ctrl+Enter键可以直接提交]
Copyright © dlutyuanhongl(乱石铺街)