zoukankan      html  css  js  c++  java
  • 完成端口模型开发

    完成端口模型实现步骤:

    1.创建完成端口

    2.创建服务线程,通常服务线程数量为CPU数量的2倍

    3.将套接字与完成端口关联在一起

    4.调用输入输出函数,发起重叠I/O操作

    5.在服务线程中,在完成端口上等待重叠I/O操作结果

    一、创建完成端口对象

    要在应用程序中利用完成端口模型,就必须首次创建完成端口对象。CreateIoCompletionPort()函数实现此功能,函数声明如下:

    HANDLE CreateIoCompletionPort(
    HANDLE FileHandle,                                       //文件句柄
    HANDLE ExistingCompletionPort,                  //存在的完成端口句柄
    DWORD  CompletionKey,        //完成键,通常应用程序利用该参数保存于套接字相关信息
    DWORD NumberOfConcurrentThreads //完成端口并发线程的数量。如果为0,则通知系统完成例程并发线程的数量等于CPU的数量
    );        

    如果该函数调用成功则返回完成端口的句柄,如果失败则返回NULL.

    在套接字应用程序中,要创建完成端口对象,需要设置FileHandle参数为INVALID_HANDLE_VALUE,ExisstingCompletionPort参数为NULL,CompletionKey参数为0,NumberOfConcurrentThreads参数为0,代码如下:

    1 HANDLE hIoCompletionPort;
    2 hIoCompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

    二、创建服务线程

    创建完成端口后,在创建服务线程。首先应用程序要获得计算机CPU的数量,着可以通过调用GetSystemInfo()函数来完成,然后调用CreateThread()或_beginthreadex()函数创建服务线程。通常应用程序要把完成例程作为线程参数传递给服务线程。

    以下代码演示了创建服务线程的过程:

     1 //服务线程函数
     2 DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);
     3 
     4 SYSTEM_INFO SystemInfo;   //计算机系统信息
     5 GetSystemInfo(&SystemInfo);     //获取计算机系统信息
     6 for(int i=0;i<SystemInfo.dwNumberOfProcessors*2;i++)
     7 {
     8 HANDLE ThreadHandle;
     9 DWORD  ThreadID;
    10 //创建服务线程
    11 if((ThreadHandle=CreateThread(NULL,0,ServerWorkerThread,CreateIoCompletionPort,0,&ThreadID))==NULL)
    12 {
    13 printf("调用创建线程函数失败!%d
    ",GetLastError());
    14 return;
    15 }
    16 CloseHnadle(ThreadHandle);
    17 }

    服务线程开始工作后有3种状态:

    (1)等待状态。在没有I/O操作完成通知包被投递到完成端口时,服务线程在完成端口上等待。

    (2)服务状态。当I/O操作完成通知到达时,服务线程按照LIFO方式被唤醒,开始为客户端提供服务,当完成服务后,服务线程回到完成端口,继续等待。

    (3)阻塞状态。为客户端提供服务的线程,调用了Sleep()之类的函数,线程进入阻塞状态。

    三、套接字与完成端口关联

    创建完成端口后,需要将套接字与完成端口关联在一起,此功能还是通过调用CreateIoCompletionPort()函数来实现,如调用成功返回端口句柄,即ExistingCompletionPort,失败则返回NULL.

    在套接字应用程序中,调用该函数即告知系统当I/O操作完成时,向完成端口发送一个I/O操作完成通知包。这些通知包按照FIFO方式,在完成端口上排队等待服务线程的读取。

    以下程序中_completionKey结构体保存客户端套接字和地址,在调用CreateIoCompletionPort()函数时,将该结构体指针作为完成键传递进去。

     1 //完成键
     2 //结构体
     3 typedef struct _completionKey
     4 {
     5 SOCKET  s;        
     6 SOCKADDR_IN   clientAddr;                  //客户端
     7 }COMPLETIONKEY,*PCOMPLETIONKEY;
     8 
     9 SOCKET sListen;       //服务器监听套接字
    10 SOCKET sAccept;     //客户端套接字
    11 HANDLE hIoCompletionPort;     //完成端口
    12 PCOMPLETIONKEY   pCompleKey;      //完成键
    13 SOCKADDR_IN  addr;                 //服务器地址
    14 SOCKADDR_IN  clientLen=sizeof(addr);     
    15 sAccept=accept(sListen,(SOCKADDR*)&addr,&addr);   //接受客户端连接请求
    16 //定义完成键
    17 pCompleKey->s=sAccept;      //端套接字
    18 pCompleKey->clientAddr=addr;    //地址
    19 //将套接字与完成端口关联起来
    20 HANDLE h=CreateIoCompletionPort((HANDLE)sAccept,hIoCompletionPort,(DWORD)pCompleKey,0)

    四、发起重叠I/O操作

    将套接字与完成端口关联起来后,应用程序调用下面的函数,发起发起重叠I/O操作

    (1)WSASend()和WSASenTo()函数:发送数据

    (2)WSARecv()和WSARecvFrom()函数:接收数据

    在应用程序中,通常声明一个和I/O操作相关的结构体,以保存I/O操作的相关信息

    以下代码演示了调用WSARecv()函数发起异步接收数据的过程

    如果函数返回ERROR_IO_PENDING,则说明成功的发起了一个异步接收数据操作。

     1 #define DATA_BUFSIZE 512
     2 SOCKET sAccept       //客户端套接字
     3 //I/O操作数据结构
     4 typedef   struct  _io_operation_data
     5 {
     6 WSAOVERLAPPEO   overlapped;     //重叠结构
     7 WSABUF       dataBuf;       
     8 CHAR  buffer[DATA_BUFSIZE];      //接收数据缓存区
     9 }IO_OPERATION_DATA,*PIO_OPERATION_DATA;
    10 
    11 
    12 //定义I/O操作数据
    13 PIO_OPERATION_DATA  pIoDate;
    14 pIoDate->dataBuf.buf=pIoDate->buffer;
    15 pIoDate->data.len=DATA_BUFSIZE;
    16 ZeroMemory(&pIoDate->overlapped,sizeof(pIoDate->overlapped))
    17 DWORD  flags=0;
    18 DWORD recvBytes;
    19 
    20 //接收数据
    21 if(WSARecv(sAccept,&(pIoDate->dataBuf),1,&recvBytes,&flags,&(pIoDate->overlapped),NULL)==SOCKET_ERROR)
    22 {
    23 if(WSAGetLastError()!=ERROR_IO_PENDING)    //成功发起异步接收数据操作
    24 {
    25 printf("调用WSARecv()函数失败%d
    ",WSAGetLastError());
    26 return;
    27 }
    28 }

    五、等待重叠I/O操作结果

    服务线程被启动后,调用GetQueuedCompletionStatus()(该函数的用法请参看MSDN)函数等待重叠I/O操作的完成结果。当重叠I/O操作完成时,I/O操作完成通知包被发送到完成端口上,此时该函数返回。完成通知包包含的信息有传输的字节数,完成键和重叠结构。

    以下代码演示了GetQueuedCompletionStatus()函数的用法:

     1 #define   THREAD_SLEEP_TIME        //超时时间
     2 //完成键
     3 typedef   struct_completionKey
     4 {
     5 SOCKET    s; 
     6 SOCKADDR_IN      clientAddr;    //客户端地址
     7 }COMPLETIONKEY,*PCOMPLETIONKEY;
     8 HANDLE hIoComPort;        //完成端口
     9 DWORD   dwNumberOfByte;      //传输字节
    10 PCOMPLETIONKEY    pCompletionKey;   //完成键
    11 LPOVERLAPPED     lpOverlapped;      //重叠结构指针
    12 BOOL   bRet=GetQueuedCompletionStatus(hIoComPort,&dwNumberOfByte,(LPDWORD )pCompletionKey,&lpOverlapped,THREAD_SLEEP_TIME);
    13 if(TRUE==bRet)
    14 {
    15 //成功的I/O异步操作,处理数据
    16 }
    17 else
    18 {
    19 int nErrCode=WSAGetLastError(); 
    20 if(NULL!=lpOverlapped)
    21 {
    22 //检查错误代码
    23 }
    24 else if(WAIT_TIMEOUT==nErrCode)
    25 {
    26 //函数调用超时
    27 continue;28 }
    29 else
    30 {
    31 //检查错误代码
    32 }
    33 }

    六、取消异步操作

    1 BOOL CancelIo
    2 {
    3 HANDLE hFile;
    4 }

    当关闭套接字应用程序时,如果此时系统中还有未完成的异步操作,那么应用程序可以调用CanceIo()函数取消等待执行的异步操作。该函数调用成功则返回值为TRUE,所有在曦套接字上等待的异步操作都被成功地取消。

    七、投递完成通知包

    当服务线程退出,应用程序可以调用PostQueuedCompletionStatus()(Posts an I/O completion packet to an I/O completion port.)函数想服务线程发送一个特殊的完成通知包。在服务线程中,接收到这通知包后,线程退出。

    PostQueuedCompletionStatus()函数功能为向完成端口上发送一个I/O操作完成通知包。在服务线程中,GetQueuedCompletionStatus()函数返回调用PostQueuedCompletionStatus()函数时传递的第2个、第3个和第4个参数值。

    以下代码表明利用PostQueuedCompletionStatus()函数向服务线程发送退出通知包的过程。设置PostQueuedCompletionStatus()函数的第3和第4个参数为NULL。在服务线程中,根据GetQueuedCompletionStatus()函数的第3和第4个参数值为NULL,服务线程退出。

     1 HANDLE    hIoComPort;      //完成端口
     2 PCOMPLETIONKEY   pCompletionKey;     //完成键
     3 LPOVERLAPPED       pOverlapped;      //重叠结构指针
     4 DWORD            dwNumberOfByte;     //传输字节数
     5 //发送服务线程退出通知包
     6 PostQueuedCompletionStatus(hIoComPort,0,(DWORD)NULL,NULL);
     7 //等待重叠I/O操作结果
     8 GetQueuedCompletionStatus(hIoComPort,&dwNumberOfByte,(LPDWORD)pCompletionKey,&pOverlapped,THREAD_SLEEP_TIME);
     9 //其他处理
    10 if(NULL==pOverlapped&&NULL==pCompletionKey)
    11 {
    12 //服务线程退出
    13 }

    这个笔记终于记完了。so pain

  • 相关阅读:
    grpc学习
    01
    样本1
    杀死长时间占用CPU的进程
    SWFTools pdf2swf 参数详解
    C#自动下载并保存文件示例
    Flex初始化时加载外部XML
    通过XPDF抽取PDF中的中文文本
    Flex操作Json数据示例
    C#下载文件和将文件转换为数据流下载的示例
  • 原文地址:https://www.cnblogs.com/newworldcom/p/3434595.html
Copyright © 2011-2022 走看看