您的位置:首页 > 其它

IOCP 浅析与实例

2013-01-12 06:28 856 查看
转载:http://www.cppfans.org/1267.html



这一年半来一直在做游戏项目逻辑层,学会了不少东西,觉得自己应该看看服务器底层的东西了,主要的东西就是网络模块,网络模块是沿用以前项目的,在 我们项目中被我们头改动过几次,现在还是比较稳定的。因为是Windows平台,所以用的依然是被大多数人神话了的IOCP,不过的确IOCP 表现的非常不错。

什么是IOCP?

众所周知,为了绝对同步,所以很多模式都采用的是同步模式,而不是异步,这样就会产生很大情况下在等待,CPU在切换时间片,从而导致效率比较低。自从MS在winsocket2中引入了IOCP这个模型之后,他才开始被大家所认知。

IOCP (I/O Completion Port),中文译作IO完成端口,他是一个异步I/O操作的API,他可以高效的将I/O事件通知给我们的应用程序,那游戏项目来说,就是客户端或者服务器。

他与Socket基础API select()或其他异步方法不同的是,他需要讲一个Socket和一个完成端口绑定在一起,然后就可以进行网路通信了。

什么是同步/异步?

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

逻辑上通俗来讲是完成一件事再去做另外一件事情就是同步,而一起做两件或者两件以上的事情就是异步了。类似于Win32API中的SendMessage()和PostMessage(),你可以将他理解成单线程和多线程的区别。

拿游戏服务器与客户端通信来说:

如果是同步:

ClientA发送一条Msg1Req消息给Server,这个时候ClientA就会等待Server处理Msg1Req。这段时间内ClientA只有等待,因为Server还没有给ClientA回复Msg1Ack消息,所以ClientA只能痴痴的等,等到回复之后,才能处理第二条Msg2Req消息,这样无疑就会大大的降低性能,产生非常差的用户体验。

如果是异步:

ClientA发送一条Msg1Req消息给Server,ClientA有发送第二条Msg2Req消息给Server,Server会将他们都存入队列,一条一条处理,处理完之后回复给ClientA,这样用户就可以不必等待,效率就会非常高。

什么是阻塞/非阻塞?

阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。可能阻塞和同步有点类似,但是同步调用的时候线程还是激活的,而阻塞时线程会被挂起。

非阻塞调用和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

对象的阻塞模式和阻塞函数调用

对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方 式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数 select就是这样的一个例子。

对IOCP的评价如何?

I/O完成端口可能是Win32提供的最复杂的内核对象。 Jeffrey Richter

这是实现高容量网络服务器的最佳方法。Microsoft Corporation

完成端口模型提供了最好的伸缩性。这个模型非常适用来处理数百乃至上千个套接字。Anthony Jones & Jim Ohlund

I/O completion ports特别显得重要,因为它们是唯一适用于高负载服务器[必须同时维护许多连接线路]的一个技术。Completion ports利用一些线程,帮助平衡由I/O请求所引起的负载。这样的架构特别适合用在SMP系统中产生的”scalable”服务器。 Jim Beveridge & Robert Wiener

IOCP中的完成是指什么意思?

网络通信说白了就是将一堆数据发过来发过去,到底还是数据的操作。不过大家都知道I/O操作是非常慢的,包括打印机、调制解调器、硬盘等,至少相对于CPU来说是非常慢的。坐等I/O是很浪费时间的事情,可能你只需要读取100KB的数据,假设读了0.1秒,假设CPU是3.0G Hz,那么CPU已经运行了0.3G次了,所以CPU这个时候就不满意了,哥这么NB,为什么要等你?

所以我们用另外一个线程来处理I/O操作,使用重叠IO(Overlapped I/O)技术,应用程序可以要求OS为其传输数据,在完成的时候通知应用程序,然后在进行相应操作,这也就是为什么叫完成的原因。这可以使得应用程序在I/O传输期间可以做其他事情,这也可以最大限度的利用线程,而让最NB的CPU不至于痴痴等待。

下来会将IOCP和网络有什么关系,以及IOCP的简单应用。



上一篇《IOCP浅析》中翻翻的谈了一下IOCP的简单含义,这篇稍微深入讨论下IOCP到底有什么好的,让大家将他推向神坛,同时简单的讨论下基本函数。

IOCP出现的意义?

写过网络程序的朋友应该很清楚网络程序的原型代码,startup一个WSADATA,然后建立一个监听socket对象,绑定一个服务器地址,然后开始监听,无限循环的accept来自客户端的消息,建立一个线程来处理消息,accept之后线程就被挂起了,知道收到来自客户端的消息。

这样的模型中服务器对每个客户端都会创建一个线程,优点在于等待请求的线程只做很少的事情,大部分时间该线程都在休息,因为recv函数是阻塞的。

所以这样的效率并不是很高,NT小组意识到这样CPU的大部分时间都耗费在线程的上下文切换上,线程并没有抢到cpu时间来处理自己的工作。

NT小组想到了一个解决办法,实现开好N个线程,将用户的消息都投递到一个消息队列中去,然后事先开好的N个线程逐一从消息队列中取出消息并加以处理,就可以避免为每一个客户端的请求单独开线程,既减少了线程的资源,也提高了线程的利用率。所以I/O完成端口的内核对象在NT3.5中首次被引入,MS还是比较伟大的。

这里你也看到了,IOCP其实称作是一种消息处理的机制差不多,而叫完成端口估计也是有历史原因,亦或者是因为他提供了用户与操作系统的一种接口吧。

ICOP的基本函数接口

创建完成端口

C++

HANDLE WINAPI CreateIoCompletionPort(
__in HANDLE FileHandle, // An open file handle or INVALID_HANDLE_VALUE
__in_opt HANDLE ExistingCompletionPort, // A handle to an existing I/O completion port or NULL
__in ULONG_PTR CompletionKey, // Completion key
__in DWORD NumberOfConcurrentThreads // Number of threads to execute concurrently
);

1
2
3
4
5
6

HANDLE
WINAPI CreateIoCompletionPort(
__in HANDLEFileHandle, //
An open file handle or INVALID_HANDLE_VALUE
__in_opt HANDLEExistingCompletionPort, //
A handle to an existing I/O completion port or NULL
__in ULONG_PTRCompletionKey, //
Completion key
__in DWORDNumberOfConcurrentThreads // Number of threads to execute
concurrently
);

第一个参数是指一个已经打开的文件句柄或者空句柄值,一般为客户端的socket 注意:第一个参数HANDLE在创建时需要在CreateFile()中制定FILE_FLAG_OVERLAPPED标志。

第二个参数是指一个已经存在的IOCP句柄或者NULL

第三个参数是指完成Key,是一个unsigned long的指针,可以为NULL

第四个参数才是我们比较关心的,是指已经创建好的线程数,一般我们会用一个公式来计算,预设的线程数 = CPU核心数 * 2 + 2,有人也说是 + 1,我是没明白为什么要这样计算,希望大神指教。

对于第三个参数的意思,MSDN上解释如下 Use the CompletionKey parameter to help your application track which I/O operations have completed.(用来检测那些IO操作已经完成)

该函数用于两个不同的目的

1.创建一个完成端口的句柄对象

HANDLE h = CreateIoCompletionPort((HANDLE) socket, hCompletionPort, dwCompletionKey, m_nIOWorkers);

2.将一个句柄和完成端口关联在一起

在绑定每一个CLIENT到IOCP时,需要传递一个DWORD CompletionKey, 该参数为CLIENT信息的一个指针。

IO的异步调用

C++

BOOL WINAPI PostQueuedCompletionStatus(
__in HANDLE CompletionPort,
__in DWORD dwNumberOfBytesTransferred,
__in ULONG_PTR dwCompletionKey,
__in_opt LPOVERLAPPED lpOverlapped
);

1
2
3
4
5
6

BOOLWINAPI
PostQueuedCompletionStatus(
__in HANDLECompletionPort,
__in DWORDdwNumberOfBytesTransferred,
__in ULONG_PTRdwCompletionKey,
__in_opt LPOVERLAPPEDlpOverlapped
);

第一个参数为创建的完成端口句柄

第二个参数传输了多少字节

第三个参数同样为完成键指针

第四个参数为重叠I/O buffer,其结构如下

C

typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;

1
2
3
4
5
6
7
8
9
10
11
12

typedefstruct
_OVERLAPPED{
ULONG_PTRInternal;
ULONG_PTRInternalHigh;
union{
struct{
DWORDOffset;
DWORDOffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
}OVERLAPPED,*LPOVERLAPPED;

线程的同步

C++

BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED *lpOverlapped,
__in DWORD dwMilliseconds
);

1
2
3
4
5
6
7

BOOLWINAPI
GetQueuedCompletionStatus(
__in HANDLE
CompletionPort,
__out LPDWORDlpNumberOfBytes,
__out PULONG_PTRlpCompletionKey,
__out LPOVERLAPPED*lpOverlapped,
__in DWORD
dwMilliseconds
);

第一个参数为创建的完成端口句柄

第二个参数同样为传输了多少字节

第三个参数同样为完成键指针

第四个参数为重叠I/O buffer

第五个参数的解释如下

The number of milliseconds that the caller is willing to wait for a completion packet to appear at the completion port. If a completion packet does not appear within the specified time, the function times out, returnsFALSE, and sets *lpOverlapped
to NULL.(等待完成端口上的完成packet出现的毫秒数。如果一个完成在特殊时间内没有出现,则认为超时,返回false,同时将重叠buffer置为NULL)

大体上函数就有这么几个,大家有兴趣可以去看看MSDN上关于完成端口上的英文介绍,比较准确,中文翻译上难免出现歧义,同时还可以锻炼英文阅读。

好了,I/O完成端口的API就介绍到这里,下一篇会尝试着写出一个简单的完成端口模型来通讯,这篇可能会比较晚出来,因为自己也是摸索阶段,大家互勉。

本文是我在学习IOCP的时候,第一次写一个完整的例子出来,当然了,参考了CSDN上一些朋友的博客,大部分都是按照他们的思路写的,毕竟我是初学者,参考现成的学起来比较快。当然了,真正用到项目中的IOCP肯定不止这么简单的,还有内存池,环形缓冲区,socket连接池等高端内容,后面我会参考一些例子,写出一个完整的给大家看。

C++

/************************************************************************

FileName:iocp.h
Author :eliteYang
http://www.cppfans.org
************************************************************************/
#ifndef __IOCP_H__
#define __IOCP_H__

#include
#include

#define DefaultPort 20000
#define DataBuffSize 8 * 1024

typedef struct
{
OVERLAPPED overlapped;
WSABUF databuff;
CHAR buffer[ DataBuffSize ];
DWORD bytesSend;
DWORD bytesRecv;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA;

typedef struct
{
SOCKET socket;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

#endif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

/************************************************************************

FileName:iocp.h
Author :eliteYang
http://www.cppfans.org
************************************************************************/
#ifndef __IOCP_H__
#define __IOCP_H__

#include
#include

#define DefaultPort 20000
#define DataBuffSize 8 * 1024

typedefstruct
{
OVERLAPPEDoverlapped;
WSABUFdatabuff;
CHARbuffer[
DataBuffSize ];
DWORDbytesSend;
DWORDbytesRecv;
}PER_IO_OPERATEION_DATA,*LPPER_IO_OPERATION_DATA;

typedefstruct
{
SOCKETsocket;
}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;

#endif

前面讲过IOCP里面一个很重要的东西就是IO重叠了,所以结构体里有一个OVERLAPPED结构。

C++

/************************************************************************

FileName:iocp.cpp
Author :eliteYang
http://www.cppfans.org
************************************************************************/

#include "iocp.h"
#include

using namespace std;

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

DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID );

void main()
{

SOCKET acceptSocket;
HANDLE completionPort;
LPPER_HANDLE_DATA pHandleData;
LPPER_IO_OPERATION_DATA pIoData;
DWORD recvBytes;
DWORD flags;

WSADATA wsaData;
DWORD ret;
if ( ret = WSAStartup( 0x0202, &wsaData ) != 0 )
{
std::cout << "WSAStartup failed. Error:" << ret << std::endl;
return;
}

completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
if ( completionPort == NULL )
{
std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
return;
}

SYSTEM_INFO mySysInfo;
GetSystemInfo( &mySysInfo );

// 创建 2 * CPU核数 + 1 个线程
DWORD threadID;
for ( DWORD i = 0; i < ( mySysInfo.dwNumberOfProcessors * 2 + 1 ); ++i )
{
HANDLE threadHandle;
threadHandle = CreateThread( NULL, 0, ServerWorkThread, completionPort, 0, &threadID );
if ( threadHandle == NULL )
{
std::cout << "CreateThread failed. Error:" << GetLastError() << std::endl;
return;
}

CloseHandle( threadHandle );
}

// 启动一个监听socket
SOCKET listenSocket = WSASocket( AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED );
if ( listenSocket == INVALID_SOCKET )
{
std::cout << " WSASocket( listenSocket ) failed. Error:" << GetLastError() << std::endl;
return;
}

SOCKADDR_IN internetAddr;
internetAddr.sin_family = AF_INET;
internetAddr.sin_addr.s_addr = htonl( INADDR_ANY );
internetAddr.sin_port = htons( DefaultPort );

// 绑定监听端口
if ( bind( listenSocket, (PSOCKADDR)&internetAddr, sizeof( internetAddr ) ) == SOCKET_ERROR )
{
std::cout << "Bind failed. Error:" << GetLastError() << std::endl;
return;
}

if ( listen( listenSocket, 5 ) == SOCKET_ERROR )
{
std::cout << "listen failed. Error:" << GetLastError() << std::endl;
return;
}

// 开始死循环,处理数据
while ( 1 )
{
acceptSocket = WSAAccept( listenSocket, NULL, NULL, NULL, 0 );
if ( acceptSocket == SOCKET_ERROR )
{
std::cout << "WSAAccept failed. Error:" << GetLastError() << std::endl;
return;
}

pHandleData = (LPPER_HANDLE_DATA)GlobalAlloc( GPTR, sizeof( PER_HANDLE_DATA ) );
if ( pHandleData = NULL )
{
std::cout << "GlobalAlloc( HandleData ) failed. Error:" << GetLastError() << std::endl;
return;
}

pHandleData->socket = acceptSocket;
if ( CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, (ULONG_PTR)pHandleData, 0 ) == NULL )
{
std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
return;
}

pIoData = ( LPPER_IO_OPERATION_DATA )GlobalAlloc( GPTR, sizeof( PER_IO_OPERATEION_DATA ) );
if ( pIoData == NULL )
{
std::cout << "GlobalAlloc( IoData ) failed. Error:" << GetLastError() << std::endl;
return;
}

ZeroMemory( &( pIoData->overlapped ), sizeof( pIoData->overlapped ) );
pIoData->bytesSend = 0;
pIoData->bytesRecv = 0;
pIoData->databuff.len = DataBuffSize;
pIoData->databuff.buf = pIoData->buffer;

flags = 0;
if ( WSARecv( acceptSocket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
{
if ( WSAGetLastError() != ERROR_IO_PENDING )
{
std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
return;
}
else
{
std::cout << "WSARecv() io pending" << std::endl;
return;
}
}
}
}

DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID )
{
HANDLE complationPort = (HANDLE)CompletionPortID;
DWORD bytesTransferred;
LPPER_HANDLE_DATA pHandleData = NULL;
LPPER_IO_OPERATION_DATA pIoData = NULL;
DWORD sendBytes = 0;
DWORD recvBytes = 0;
DWORD flags;

while ( 1 )
{
if ( GetQueuedCompletionStatus( complationPort, &bytesTransferred, (PULONG_PTR)&pHandleData, (LPOVERLAPPED *)&pIoData, INFINITE ) == 0 )
{
std::cout << "GetQueuedCompletionStatus failed. Error:" << GetLastError() << std::endl;
return 0;
}

// 检查数据是否已经传输完了
if ( bytesTransferred == 0 )
{
std::cout << " Start closing socket..." << std::endl;
if ( CloseHandle( (HANDLE)pHandleData->socket ) == SOCKET_ERROR )
{
std::cout << "Close socket failed. Error:" << GetLastError() << std::endl;
return 0;
}

GlobalFree( pHandleData );
GlobalFree( pIoData );
continue;
}

// 检查管道里是否有数据
if ( pIoData->bytesRecv == 0 )
{
pIoData->bytesRecv = bytesTransferred;
pIoData->bytesSend = 0;
}
else
{
pIoData->bytesSend += bytesTransferred;
}

// 数据没有发完,继续发送
if ( pIoData->bytesRecv > pIoData->bytesSend )
{
ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) );
pIoData->databuff.buf = pIoData->buffer + pIoData->bytesSend;
pIoData->databuff.len = pIoData->bytesRecv - pIoData->bytesSend;

// 发送数据出去
if ( WSASend( pHandleData->socket, &(pIoData->databuff), 1, &sendBytes, 0, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
{
if ( WSAGetLastError() != ERROR_IO_PENDING )
{
std::cout << "WSASend() failed. Error:" << GetLastError() << std::endl;
return 0;
}
else
{
std::cout << "WSASend() failed. io pending. Error:" << GetLastError() << std::endl;
return 0;
}
}

std::cout << "Send " << pIoData->buffer << std::endl;
}
else
{
pIoData->bytesRecv = 0;
flags = 0;

ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) );
pIoData->databuff.len = DataBuffSize;
pIoData->databuff.buf = pIoData->buffer;

if ( WSARecv( pHandleData->socket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR )
{
if ( WSAGetLastError() != ERROR_IO_PENDING )
{
std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
return 0;
}
else
{
std::cout << "WSARecv() io pending" << std::endl;
return 0;
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

/************************************************************************

FileName:iocp.cpp
Author :eliteYang
http://www.cppfans.org
************************************************************************/

#include "iocp.h"
#include

usingnamespace
std;

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

DWORDWINAPI
ServerWorkThread(LPVOID
CompletionPortID);

voidmain()
{

SOCKETacceptSocket;
HANDLEcompletionPort;
LPPER_HANDLE_DATApHandleData;
LPPER_IO_OPERATION_DATApIoData;
DWORDrecvBytes;
DWORDflags;

WSADATAwsaData;
DWORDret;
if(
ret=
WSAStartup(0x0202,
&wsaData )
!= 0)
{
std::cout<<
"WSAStartup failed. Error:"<<
ret<<
std::endl;
return;
}

completionPort=
CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,
0,
0);
if(
completionPort==
NULL)
{
std::cout<<
"CreateIoCompletionPort failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

SYSTEM_INFOmySysInfo;
GetSystemInfo(&mySysInfo
);

// 创建 2 * CPU核数 + 1 个线程
DWORDthreadID;
for(
DWORDi
=0;
i <(
mySysInfo.dwNumberOfProcessors*
2+
1);
++i )
{
HANDLEthreadHandle;
threadHandle=
CreateThread(NULL,
0,
ServerWorkThread,completionPort,0,
&threadID );
if(
threadHandle==
NULL)
{
std::cout<<
"CreateThread failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

CloseHandle(threadHandle
);
}

// 启动一个监听socket
SOCKETlistenSocket
=WSASocket(
AF_INET,
SOCK_STREAM,0,
NULL,
0,WSA_FLAG_OVERLAPPED
);
if(
listenSocket==
INVALID_SOCKET)
{
std::cout<<
" WSASocket( listenSocket ) failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

SOCKADDR_INinternetAddr;
internetAddr.sin_family=
AF_INET;
internetAddr.sin_addr.s_addr=
htonl(INADDR_ANY
);
internetAddr.sin_port=
htons(DefaultPort
);

// 绑定监听端口
if(
bind(listenSocket,(PSOCKADDR)&internetAddr,sizeof(
internetAddr ))
==SOCKET_ERROR
)
{
std::cout<<
"Bind failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

if(
listen(listenSocket,5
)== SOCKET_ERROR)
{
std::cout<<
"listen failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

// 开始死循环,处理数据
while(
1)
{
acceptSocket=
WSAAccept(listenSocket,NULL,
NULL,
NULL,0
);
if(
acceptSocket==
SOCKET_ERROR)
{
std::cout<<
"WSAAccept failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

pHandleData=
(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,
sizeof(
PER_HANDLE_DATA)
);
if(
pHandleData=
NULL)
{
std::cout<<
"GlobalAlloc( HandleData ) failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

pHandleData->socket=
acceptSocket;
if(
CreateIoCompletionPort((HANDLE)acceptSocket,completionPort,(ULONG_PTR)pHandleData,0
)==
NULL)
{
std::cout<<
"CreateIoCompletionPort failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

pIoData=
(LPPER_IO_OPERATION_DATA
)GlobalAlloc(GPTR,
sizeof(
PER_IO_OPERATEION_DATA)
);
if(
pIoData==
NULL)
{
std::cout<<
"GlobalAlloc( IoData ) failed. Error:"<<
GetLastError()<<
std::endl;
return;
}

ZeroMemory(&(
pIoData->overlapped),
sizeof(
pIoData->overlapped)
);
pIoData->bytesSend=
0;
pIoData->bytesRecv=
0;
pIoData->databuff.len=
DataBuffSize;
pIoData->databuff.buf=
pIoData->buffer;

flags=
0;
if(
WSARecv(acceptSocket,&(pIoData->databuff),1,
&recvBytes,
&flags,
&(pIoData->overlapped),NULL
)==
SOCKET_ERROR)
{
if(
WSAGetLastError()!=
ERROR_IO_PENDING)
{
std::cout<<
"WSARecv() failed. Error:"<<
GetLastError()<<
std::endl;
return;
}
else
{
std::cout<<
"WSARecv() io pending"<<
std::endl;
return;
}
}
}
}

DWORD
WINAPI ServerWorkThread(LPVOID
CompletionPortID)
{
HANDLEcomplationPort
=(HANDLE)CompletionPortID;
DWORDbytesTransferred;
LPPER_HANDLE_DATApHandleData
=NULL;
LPPER_IO_OPERATION_DATApIoData
=NULL;
DWORDsendBytes
=0;
DWORDrecvBytes
=0;
DWORDflags;

while(
1)
{
if(
GetQueuedCompletionStatus(complationPort,&bytesTransferred,(PULONG_PTR)&pHandleData,(LPOVERLAPPED*)&pIoData,INFINITE
)==
0)
{
std::cout<<
"GetQueuedCompletionStatus failed. Error:"<<
GetLastError()<<
std::endl;
return0;
}

// 检查数据是否已经传输完了
if(
bytesTransferred==
0)
{
std::cout<<
" Start closing socket..."<<
std::endl;
if(
CloseHandle((HANDLE)pHandleData->socket)
==SOCKET_ERROR
)
{
std::cout<<
"Close socket failed. Error:"<<
GetLastError()<<
std::endl;
return0;
}

GlobalFree(pHandleData
);
GlobalFree(pIoData
);
continue;
}

// 检查管道里是否有数据
if(
pIoData->bytesRecv==
0)
{
pIoData->bytesRecv=
bytesTransferred;
pIoData->bytesSend=
0;
}
else
{
pIoData->bytesSend+=
bytesTransferred;
}

// 数据没有发完,继续发送
if(
pIoData->bytesRecv>
pIoData->bytesSend)
{
ZeroMemory(&(pIoData->overlapped),sizeof(
OVERLAPPED ));
pIoData->databuff.buf=
pIoData->buffer+
pIoData->bytesSend;
pIoData->databuff.len=
pIoData->bytesRecv-
pIoData->bytesSend;

// 发送数据出去
if(
WSASend(pHandleData->socket,&(pIoData->databuff),1,
&sendBytes,
0,
&(pIoData->overlapped),NULL
)==
SOCKET_ERROR)
{
if(
WSAGetLastError()!=
ERROR_IO_PENDING)
{
std::cout<<
"WSASend() failed. Error:"<<
GetLastError()<<
std::endl;
return0;
}
else
{
std::cout<<
"WSASend() failed. io pending. Error:"<<
GetLastError()<<
std::endl;
return0;
}
}

std::cout<<
"Send "<<
pIoData->buffer<<
std::endl;
}
else
{
pIoData->bytesRecv=
0;
flags=
0;

ZeroMemory(&(pIoData->overlapped),sizeof(
OVERLAPPED ));
pIoData->databuff.len=
DataBuffSize;
pIoData->databuff.buf=
pIoData->buffer;

if(
WSARecv(pHandleData->socket,&(pIoData->databuff),1,
&recvBytes,
&flags,
&(pIoData->overlapped),NULL
)==
SOCKET_ERROR)
{
if(
WSAGetLastError()!=
ERROR_IO_PENDING)
{
std::cout<<
"WSARecv() failed. Error:"<<
GetLastError()<<
std::endl;
return0;
}
else
{
std::cout<<
"WSARecv() io pending"<<
std::endl;
return0;
}
}
}
}
}

整个过程还是类似于最基础的socket连接方式,主要部分就是使用IOCP的两个函数,创建IOCP和检测当前的状态。

大家先凑活看吧,后面本博客会有更精彩的IOCP内容呈现给大家,我也是逐步在学习,大家稍安勿躁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: