一个简单的IOCP(IO完成端口)服务器/客户端类
来源:互联网 发布:云计算平台 开源 编辑:程序博客网 时间:2024/04/29 04:52
作者:Amin Gholiha 翻译:高庆余
CompletionKey
参数,
还有OVERLAPPED
结构体。
CompletionKey
参数是第一个参数,是一个DWORD类型的变量。你可以给它传递你想要的任何值,这些值总是和这个参数联系。通常,指向结构体的指针,或者包含客户端指定对象的类的指针被传递给这个参数。在本文的源代码中,一个ClientContext
结构体的指针被传递给CompletionKey
参数。CreateIoCompletionPort
函数可以将一个套接字和完成端口进行绑定,像下面的方法:- BOOL IOCPS::AssociateSocketWithCompletionPort(SOCKET socket,
- HANDLE hCompletionPort,
- DWORD dwCompletionKey)
- {
- HANDLE h = CreateIoCompletionPort((HANDLE) socket, hCompletionPort,
- dwCompletionKey, m_nIOWorkers);
- return h == hCompletionPort;
- }
WSASend
,WSARecv
函数,进行实际的异步调用。这些函数也需要包含将要被用到的内存指针的参数WSABUF。通常情况下,当服务器/客户端想要执行一个I/O调用操作,它们并不直接去做,而是发送到完成端口,这些操作被I/O工作线程执行。这是因为,要公平的分配CPU。通过给完成端口传递一个状态,进行I/O调用。象下面这样:
BOOL bSuccess = PostQueuedCompletionStatus( m_hCompletionPort,
pOverlapBuff->GetUsed(),
(DWORD) pContext,
&pOverlapBuff->m_ol );
GetQueuedCompletionStatus
函数进行线程的同步(看下面)。这个函数也提供了CompletionKey
参数OVERLAPPED
参数。
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort, // handle to completion port
LPDWORD lpNumberOfBytes, // bytes transferred
PULONG_PTR lpCompletionKey, // file completion key
LPOVERLAPPED *lpOverlapped, // buffer
DWORD dwMilliseconds // optional timeout value
);
WSAEWOULDBLOCK
,
当非阻塞接收失败时,也没有数据被阻塞。这种设计的目的是,在牺牲数据吞吐量的情况下,能够处理最大量的并发连接。当然,对于客户端如何和服务器交互,你知道的越多越好。在以前的例子中,每当0字节的接收完成,返回存储了的数据,马上执行非阻塞接收。假如服务器知道客户端突然发送数据,当0字节接收一旦完成,为防止客户端发送一定数量的数据(大于每个套接字默认的8K内存大小),它可以投递一个或多个重叠接收。WSARead
()
函数(见
OnZeroByteRead
())。
当调用完成,我们知道数据在TCP/IP栈中,通过采用几个异步WSARead
()
函数读取MAXIMUMPACKAGESIZE的内存。这个方法在数据达到时仅仅锁定物理内存,解决了WSAENOBUFS问题。但是这个方案降低了服务器的吞吐量(见第9部分的Q6和A6例子)。ProcessPackage(..)
函数中找到。CompletionKey
参数中,我们为它传递一个包含了客户端特定数据的结构体的指针。假如我们释放被ClientContext
结构体占用的内存,被同一个客户端执行I/O调用所返回的错误码,我们为ClientContext
指针传递双字节的CompletionKey
变量,试图访问或删除CompletionKey
参数,这些情况下会发生什么?一个访问紊乱发生了。ClientContext
结构体增加一个阻塞I/O调用的计数(m_nNumberOfPendlingIO
),
当我们知道没有阻塞I/O调用时我们删除这个结构体。EnterIoLoop(..)
函数和 ReleaseClientContext(..)
.函数就是这样做的。CIOCPBuffer
:管理被异步I/O调用使用的内存的类。IOCPS
:处理所有通信的主要类。JobItem
:包含被逻辑工作线程所执行工作的结构体。ClientContext
:保存客户端特定信息的结构体(例如:状态、数据 )。
BOOL Start(int nPort=999,
int iMaxNumConnections=1201,
int iMaxIOWorkers=1,
int nOfWorkers=1,
int iMaxNumberOfFreeBuffer=0,
int iMaxNumberOfFreeContext=0,
BOOL bOrderedSend=TRUE,
BOOL bOrderedRead=TRUE,
int iNumberOfPendlingReads=4);
nPortt
:服务器将监听的端口号(在客户端模式设为-1)。l
iMaxNumConnections
:最多允许连接数。
iMaxIOWorkers
:输入/输出工作线程数。l
nOfWorkers
:逻辑工作者数(在运行时能被改变)。
iMaxNumberOfFreeBuffer
:保留的重复利用的内存的最大数量(-1:无,0:无穷)。iMaxNumberOfFreeContext
:保留的重复利用的客户端信息的最大数量(-1:无,0:无穷)。bOrderedRead
:用来进行顺序读。bOrderedSend
:用来进行顺序发送。iNumberOfPendlingReads
:等待数据的异步读循环的数量。在连接到一个远端的连接时调用下面的函数:Connect( const CString &strIPAddr, int nPort)
strIPAddr
:远端服务器的IP地址。nPort
:端口。
ShutDown
()。
if(!m_iocp.Start( -1, 1210, 2, 1, 0, 0))
AfxMessageBox("Error could not start the Client");
….
m_iocp.ShutDown();
5.1 文件传输
6 源代码例子
NotifyReceivedPackage
是重点。描述如下:int nSize,ClientContext *pContext)
{
BYTE PackageType=pOverlapBuff->GetPackageType();
switch(PackageType)
{
case Job_SendText2Client :
Packagetext(pOverlapBuff,nSize,pContext);
break;
case Job_SendFileInfo :
PackageFileTransfer(pOverlapBuff,nSize,pContext);
break;
case Job_StartFileTransfer:
PackageStartFileTransfer(pOverlapBuff,nSize,pContext);
break;
case Job_AbortFileTransfer:
DisableSendFile(pContext);
break;
};
}
6.1 编译器问题
“if (pContext->m_File.m_hFile != INVALID_HANDLE_VALUE) <-error C2446: '!=' : no conversion ""from 'void *' to 'unsigned int'”
7 注意点和解决规则
解决规则 #1:
ClientContext
)之前进行读/写。通知函数(像:Notify*(ClientContext *pContext)
)已经是“线程安全的”,你访问ClientContext
的成员函数,而不考虑上下文的加锁和解锁- // Do not do it in this way
- // …
- If(pContext->m_bSomeData)
- pContext->m_iSomeData=0;
- // …
- // Do it in this way.
- // ….
- pContext->m_ContextLock.Lock();
- If(pContext->m_bSomeData)
- pContext->m_iSomeData=0;
- pContext->m_ContextLock.Unlock();
- // …
解决规则 #2:
//… code code ..
pContext2->m_ContextLock.Lock();
// code code..
pContext2->m_ContextLock.Unlock();
// code code..
pContext->m_ContextLock.Unlock();
解决规则 #3:
Notify*(ClientContext *pContext)
)的外面访问一个客户端的上下文。假如你必须这样做,务必使用m_ContextMapLock.Lock();
…m_ContextMapLock.Unlock()
对它进行封装。如下面代码所示:m_ContextMapLock.Lock();
pContext = FindClient(ClientID);
// safe to access pContext, if it is not NULL
// and are Locked (Rule of thumbs#1:)
//code .. code..
m_ContextMapLock.Unlock();
// Here pContext can suddenly disappear because of disconnect.
// do not access pContext members here.
- 一个简单的IOCP(IO完成端口)服务器/客户端类(英文版)
- 一个简单的IOCP(IO完成端口)服务器/客户端类(中文版)
- 一个简单的IOCP(IO完成端口)服务器/客户端类(1/2)
- 一个简单的IOCP(IO完成端口)服务器/客户端类(2/2)
- 一个简单的IOCP(IO完成端口)服务器/客户端类
- 一个简单的IOCP(IO完成端口)服务器/客户端类
- 一个简单的IOCP(IO完成端口)服务器/客户端类
- 一个简单的IOCP(IO完成端口)服务器/客户端类(1/2)
- 一个简单的IOCP(IO完成端口)服务器/客户端类
- IOCP入门详解(重加文章:一个简单的完成端口(服务端/客户端)类)
- 一个简单的IOCP服务器/客户端类
- 一个简单的IOCP服务器/客户端类
- 一个简单而又灵活的IOCP模块——完成端口通讯服务器(IOCP Socket Server)设计(四)
- 一个简单而又灵活的IOCP模块——完成端口通讯服务器(IOCP Socket Server)设计(四)
- IOCP完成端口的一个简单封装类
- IOCP完成端口的一个简单封装类 (转)
- IOCP完成端口的一个简单封装类
- IOCP完成端口的一个简单封装类
- light oj 1020 - A Childhood Game (博弈)
- caffe总结
- PhantomJS在Windows7下实现网站自动下载截图
- 数据结构与算法分析 | 01 | 引论
- Struts2配置详解
- 一个简单的IOCP(IO完成端口)服务器/客户端类
- 记录shiro的配置
- servlet request response
- Spring读取properties
- SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
- slice,substr和substring的区别
- CentOS 关机 自动 umount 强制卸载 nfs的方法
- 【linux】SecureCRT的安装及设置
- 一个ubuntu phper的自我修养(ubuntu安装)