您的位置:首页 > 其它

用完成例程(Completion Routine)实现的重叠I/O模型

2014-08-03 17:12 267 查看
/// 用完成例程(Completion Routine)实现的重叠I/O模型

/// 异步IO模型

///
用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中,主线程只用不停的接受连接

///
即可;辅助线程判断有没有新的客户端连接被建立,如果有,就为那个客户端套接字激活一个

///
异步的WSARecv操作,然后调用SleepEx使线程处于一种可警告的等待状态,以使得I/O完成后

///
CompletionROUTINE可以被内核调用。如果辅助线程不调用SleepEx,则内核在完成一次I/O操作后,

///
无法调用完成例程(因为完成例程的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内)。

///
完成例程内的实现代码比较简单,它取出接收到的数据,然后将数据原封不动的发送给客户端,

/// 最后重新激活另一个WSARecv异步操作。

///
注意,在这里用到了“尾随数据”。

///
我们在调用WSARecv的时候,参数lpOverlapped实际上指向一个比它大得多的结构PER_IO_OPERATION_DATA,

///
这个结构除了WSAOVERLAPPED以外,还被我们附加了缓冲区的结构信息,另外还包括客户端套接字等重要的信息。

///
这样,在完成例程中通过参数lpOverlapped拿到的不仅仅是WSAOVERLAPPED结构,

///
还有后边尾随的包含客户端套接字和接收数据缓冲区等重要信息。这样的C语言技巧在我后面介绍完成端口的时候还会使用到。

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

///
打开信封----掏出信纸----阅读信件----回复信件==>完成例程

///
老陈接收到新的信件后,一般的程序是:打开信封----掏出信纸----阅读信件----回复信件

///
......为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的

///
操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信/阅读/回复了!老陈

/// 终于过上了小资生活!

//////////////////////////////////////////////////////////////////////////

#
pragma
once

#
include
<
WINSOCK2.
H>

#
include
<
stdio.
h>

#
define
PORT 5150

#
define
MSGSIZE 1024

#
pragma
comment(
lib,
"ws2_32.lib"
)

typedef
struct

{

WSAOVERLAPPED
overlap;

WSABUF Buffer;

char
szMessage[
MSGSIZE]
;

DWORD NumberOfBytesRecvd;

DWORD Flags;

SOCKET
sClient;

}
PER_IO_OPERATION_DATA,
*
LPPER_IO_OPERATION_DATA;

DWORD WINAPI WorkerThread(
LPVOID)
;

void
CALLBACK CompletionROUTINE(
DWORD,
DWORD,

LPWSAOVERLAPPED,
DWORD)
;

//相应的完成例程函数

SOCKET
g_sNewClientConnection;

BOOL
g_bNewConnectionArrived =
FALSE
;

int
main(
)

{

WSADATA wsaData;

SOCKET
sListen;

SOCKADDR_IN
local,
client;

DWORD dwThreadId;

int

iaddrSize =
sizeof
(
SOCKADDR_IN
)
;

// Initialize Windows Socket library

WSAStartup(
0x0202,
&
wsaData)
;

// Create listening socket

sListen =
socket
(
AF_INET
,
SOCK_STREAM
,
IPPROTO_TCP
)
;

// Bind

local.
sin_addr.
S_un.
S_addr
=
htonl
(
INADDR_ANY
)
;

local.
sin_family =
AF_INET
;

local.
sin_port =
htons
(
PORT)
;

bind
(
sListen,
(
struct
sockaddr
*
)
&
local,
sizeof
(
SOCKADDR_IN
)
)
;

// Listen

listen
(
sListen,
3)
;

// Create worker thread

CreateThread(
NULL
,
0,
WorkerThread,
NULL
,
0,
&
dwThreadId)
;

while
(
TRUE
)

{

// Accept a connection

// 这里的写法肯定是有问题的

g_sNewClientConnection
=
accept
(
sListen,
(
struct
sockaddr
*
)
&
client,
&
iaddrSize)
;

g_bNewConnectionArrived =
TRUE
;

printf
(
"Accepted client:%s:%d/n"
,
inet_ntoa(
client.
sin_addr)
,
ntohs
(
client.
sin_port)
)
;

}

}

DWORD WINAPI WorkerThread(
LPVOID lpParam)

{

LPPER_IO_OPERATION_DATA lpPerIOData =
NULL
;

while
(
TRUE
)

{

if
(
g_bNewConnectionArrived)

{

// Launch an asynchronous operation for new arrived
connection

lpPerIOData =
(
LPPER_IO_OPERATION_DATA)
HeapAlloc(

GetProcessHeap(
)
,

HEAP_ZERO_MEMORY,

sizeof
(
PER_IO_OPERATION_DATA)
)
;

lpPerIOData-
>
Buffer.
len
=
MSGSIZE;

lpPerIOData-
>
Buffer.
buf
=
lpPerIOData-
>
szMessage;

lpPerIOData-
>
sClient =

g_sNewClientConnection;

WSARecv(
lpPerIOData-
>
sClient,

&
lpPerIOData-
>
Buffer,

1,

&
lpPerIOData-
>
NumberOfBytesRecvd,

&
lpPerIOData-
>
Flags,

&
lpPerIOData-
>
overlap,

CompletionROUTINE)
;

//注意这里向系统登记了一个回调函数,将会在接收数据完成的时候进行相应的调用

g_bNewConnectionArrived
=
FALSE
;

}

SleepEx(
1000,
TRUE
)
;

}

return
0;

}

void
CALLBACK CompletionROUTINE(
DWORD dwError,

DWORD cbTransferred,

LPWSAOVERLAPPED lpOverlapped,

DWORD dwFlags)

{

//注意这一句话,将LPWSAOVERLAPPED类型的lpOverlapped转化成了
LPPER_IO_OPERATION_DATA

LPPER_IO_OPERATION_DATA
lpPerIOData =
(
LPPER_IO_OPERATION_DATA)
lpOverlapped;

if
(
dwError !
=
0 |
|
cbTransferred =
=
0)

{

// Connection was closed by client

closesocket(
lpPerIOData-
>
sClient)
;

HeapFree(
GetProcessHeap(
)
,
0,
lpPerIOData)
;

}

else

{

lpPerIOData-
>
szMessage[
cbTransferred]
=
'/0'
;

send
(
lpPerIOData-
>
sClient,

lpPerIOData-
>
szMessage,
cbTransferred,
0)
;

// Launch another asynchronous
operation

memset
(
&
lpPerIOData-
>
overlap,
0,
sizeof
(
WSAOVERLAPPED)
)
;

lpPerIOData-
>
Buffer.
len
=
MSGSIZE;

lpPerIOData-
>
Buffer.
buf
=
lpPerIOData-
>
szMessage;

WSARecv(
lpPerIOData-
>
sClient,

&
lpPerIOData-
>
Buffer,

1,

&
lpPerIOData-
>
NumberOfBytesRecvd,

&
lpPerIOData-
>
Flags,

&
lpPerIOData-
>
overlap,

CompletionROUTINE)
;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: