用完成端口关联文件操作

完成端口”模型是Window平台最复杂同时也是效率最高的的一种I/O模型,
在Socket服务器上得到了广泛的应用,“从本质上说,完成端口模型要求
我们创建一个Win32完成端口对象,通过指定数量的线程,对重叠I/O请求进行管理,
以便为已经完成的重叠I/O请求提供服务。”(引自《Window网络编程》)
   笔者最近在做Socket服务器时,由于要对频繁发生的网络事件记录
日志,也就是文件操作,在做性能测试的时候,发现有可能在极短的时间
内涉及到大量的I/O文件操作,因此就想有没有什么比较好的方式来进行管理,
由于同步读写肯定不适合这种场景,因此考虑选择异步方式,关于文件的异步
操作常用的是重叠I/O(可参考笔者的 OVERLAP文章),在这种方式下,需要使用
针对每个OVERLAP结构进行GetOverlappedResult或者WaitForSingleObject,
从程序的结构而言控制起来要麻烦一些,因此自然而然的想起用IOCP来
替代一下,毕竟是“最强大”的嘛。:)
    关于在Socket上使用IOCP,讨论的文章非常多,而且很多代码也开源了,
这里不多讨论,使用IOCP来关联文件操作的话,其实也差不多,不过有
几个地方要稍微注意一下,下面用代码来简单的说明一下,毕竟代码是最能
说明问题的。^_^。
//-------------------------------------------------------------------
首先介绍一个数据结构,具体里面的含义同大多数IOCP的应用一样,
在传递Overlapped可以带些user data过去

    enum IOCode
    {
        IORead,
        IOWrite
    };
    typedef struct
    {
       OVERLAPPED   Overlapped;
       CHAR         Buf[DATA_BUFSIZE];
       IOCode       CmdCode ;
       DWORD        nNumberOfBytesToSend;
       DWORD        nNumberOfBytesSent;

    } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;

//-------------------------------------------------------------------
1。 初始化IOCP并用文件句柄关联:

    HANDLE ThreadHandle;
    DWORD ThreadID;

    hFile = ::CreateFile(filename,
                         GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ,
                         NULL,
                         OPEN_ALWAYS,
                         FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
                         NULL);

    if (hFile ==  INVALID_HANDLE_VALUE)
        return false;

    if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
    {
        return false;
    }

    if (CreateIoCompletionPort((HANDLE) hFile, CompletionPort, (DWORD)hFile,
       0) == NULL)
    {
       int errorcode = GetLastError();
       return false;
    }

    要注意的就是CreateFile要用FILE_FLAG_OVERLAPPED的方式打开
//-------------------------------------------------------------------
2。 启动Woker Thread来获取IOCP上的事件
    if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, this,
       0, &ThreadID)) == NULL)
    {
       return false;
    }

    CloseHandle(ThreadHandle);

    that's simple..
//-------------------------------------------------------------------
3。 在Worker Thread上等待事件
DWORD WINAPI ServerWorkerThread(LPVOID file)
{
    CIOCPFile * iofile = (CIOCPFile * ) file;
    HANDLE CompletionPort = (HANDLE) iofile->CompletionPort;

  
    while (true)
    {

        DWORD BytesTransferred  = 0;
        DWORD CompleteKey       = 0;

        LPPER_IO_OPERATION_DATA PerIoData;

        int ret = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
                   (LPDWORD)&CompleteKey, (LPOVERLAPPED *) &PerIoData, INFINITE);

        if (ret == 0)
        {
            // error handling
        }

        if (PerIoData->CmdCode == IOWrite)
        {
            iofile->position = iofile->position + BytesTransferred;
            if (BytesTransferred == PerIoData->nNumberOfBytesToSend)
            {
                // this means ok
            }
            else
            {
                // error handling
            }

            GlobalFree(PerIoData);
            continue;
        }
        ..
    }
}
    也比较简单,就一个扫描线程等事件而已,并且对一些带过来的用户
数据进行业务处理,其中positiond稍晚讲到
//-------------------------------------------------------------------
4。 写数据(日志记录主要是写操作)
bool CIOCPFile::WriteFileData(char * buf, int len)
{

    DWORD Written = 0;
    LPPER_IO_OPERATION_DATA PerIoData;

    if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) == NULL)
    {
       return false;
    }

    ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
    ZeroMemory(PerIoData->Buf, DATA_BUFSIZE);
    memcpy(PerIoData->Buf, buf, len);
    PerIoData->Overlapped.Offset    = position;
    PerIoData->CmdCode              = IOWrite;
    PerIoData->nNumberOfBytesToSend = len;
    PerIoData->nNumberOfBytesSent   = 0;


    if (!WriteFile(hFile,
                     PerIoData->Buf,
                     len,
                     NULL,
                     &(PerIoData->Overlapped)))
    {
        if (ERROR_IO_PENDING != GetLastError())
        {
            return false;
        }

    }


    return true;

}
这边有几个地方要注意:
1。由于进行overlap操作的时候,系统不会对文件指针进行管理,
因此需要自己去手工的设置文件指针(When FILE_FLAG_OVERLAPPED
 is specified, the system does not maintain the file pointer)
这边假设不超过64K,所以OffsetHigh没有用到
2。不需要通过CreateEvent的方式去设置Overlapped结构,IOCP会自动管理
3。投递读写请求的时候不要用那个扩展函数,“After an instance of
 an open file is associated with an I/O completion port,
it cannot be used in the ReadFileEx or WriteFileEx function.”
//-------------------------------------------------------------------

由上可知,IOCP的使用还是比较简单的,不过由于目前对于文件操作的
相关说明比较少,所以验证了一下,算是填补偶自己的一个技术空白吧。
呵呵。在使用上,由于现在是异步操作,对于我们要发出的比如记录日志
的请求,一个异步马上返回,根本不用考虑效率,而对于另一个线程(Worker)
的检测,我们只需要对出错部分,比如没有写全等做个处理即可(重投一个
写操作,写的数据是剩余没有写完的部分),其他我们根本不用考虑,怎么样,
爽吧。^_^。
    当然我们也可以手工实现异步方式,比如把要投递的请求先扔到一个缓存
里面,另外开个线程来一批批的处理这些请求,但跟上面的做法比起来,
要考虑线程同步,要考虑内存管理,这样无疑是上面的做法要优雅的多。
使用上主要就是上面哪几个注意事项,其他的都差不多,欢迎大家讨论。
谢谢
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Microsoft SQL Server 是一种关系型数据库管理系统,用于存储和管理数据。以下是使用 Microsoft SQL Server 的一些基本步骤: 1. 安装 SQL Server:首先需要下载并安装 SQL Server。可以从 Microsoft 官网下载安装程序。 2. 创建数据库:安装完成后,可以使用 SQL Server Management Studio 工具创建数据库。在创建数据库时,需要指定数据库名称、数据文件和日志文件的位置等信息。 3. 创建表格:在数据库中创建表格,可以使用 SQL Server Management Studio 工具或者 SQL 语句来完成。在创建表格时,需要指定表格名称、列名、数据类型等信息。 4. 插入数据:在表格中插入数据,可以使用 SQL 语句来完成。例如,可以使用 INSERT INTO 语句来向表格中插入数据。 5. 查询数据:查询数据是 SQL Server 的主要功能之一。可以使用 SELECT 语句来查询表格中的数据。 6. 更新和删除数据:可以使用 UPDATE 和 DELETE 语句来更新和删除表格中的数据。 以上是使用 Microsoft SQL Server 的一些基本步骤。当然,SQL Server 还有很多高级功能,需要进一步学习和掌握。 ### 回答2: Microsoft SQL Server 是一种关系数据库管理系统,用于存储和管理大量的结构化数据。下面是关于如何使用 Microsoft SQL Server 的简要介绍。 首先,你需要在你的计算机上安装 Microsoft SQL Server。你可以从官方网站下载 SQL Server 的安装程序,并按照指示进行安装。 安装完成后,你可以打开 SQL Server Management Studio(SSMS),这是一个用于管理和操作 SQL Server 数据库的工具。在 SSMS 中,你可以连接到你的 SQL Server 实例,并在其中创建、修改和删除数据库、表、视图、存储过程等。 在创建数据库之后,你可以使用 Transact-SQL(T-SQL)语言编写查询来与数据库进行交互。T-SQL 是一种 SQL 的扩展语言,用于执行各种数据库操作,如插入、更新、删除数据,以及查询数据等。 在编写查询之前,你需要先了解数据库的结构和表之间的关系。根据需要,你可以创建新的表、字段和索引,并将数据导入到表中。你可以使用 T-SQL 语句创建表,定义字段的类型、约束和关联关系。 当你完成了数据库的设计和表的创建后,你可以使用 T-SQL 语句执行各种查询操作。例如,你可以使用 SELECT 语句查询表中的数据,使用 INSERT 语句插入新的数据,使用 UPDATE 语句更新数据,使用 DELETE 语句删除数据等。 此外,你还可以使用 SSMS 来执行诸如备份和还原数据库、创建和管理用户权限、监视服务器性能等高级操作。 总的来说,使用 Microsoft SQL Server 需要先安装 SQL Server,然后使用 SSMS 连接到数据库实例,通过 T-SQL 语句来操作数据库,执行各种查询和管理任务。对于初学者来说,熟悉和掌握 T-SQL 语法是使用 SQL Server 的关键。- ### 回答3: Microsoft SQL Server是微软公司开发的一款关系型数据库管理系统。它提供了强大的功能和灵活的工具,可以用于管理和处理大量的数据。以下是关于如何使用Microsoft SQL Server的一些简要步骤。 首先,安装SQL Server。您可以从微软官方网站上下载并安装SQL Server的最新版本。根据您的需求,可以选择安装完整版或仅安装某个组件。 安装完成后,您需要配置SQL Server实例。通过SQL Server配置管理器,您可以为实例设置网络协议、端口号和其他相关配置项。确保配置项符合您的需求。 接下来,您需要连接到SQL Server实例。您可以使用SQL Server Management Studio(SSMS)来连接到SQL Server。SSMS是SQL Server的集成开发环境,可以用于管理数据库、执行查询和进行其他相关操作。 通过SSMS,您可以创建数据库、表和其他对象。您可以使用Transact-SQL(T-SQL)语言来编写和执行查询,也可以使用SSMS提供的图形界面来进行操作。 一旦数据库和表创建完成,您可以插入、更新和删除数据。通过T-SQL语言,您可以编写insert、update和delete语句来操作数据。您还可以使用SSMS提供的图形界面来执行这些操作。 除了基本的数据操作,SQL Server还提供了其他高级功能,如事务处理、备份和恢复、安全性管理等。您可以根据需要进行配置和使用这些功能。 最后,定期维护和监控SQL Server是很重要的。您可以设置日志、性能监控和自动化任务,以保证数据库的稳定性和高效性。 总之,通过安装、配置和使用SSMS,您可以轻松地管理和操作Microsoft SQL Server。从创建数据库和表到执行查询和进行数据操作,SQL Server提供了强大的功能和灵活的工具来满足各种需求。通过学习和实践,您可以熟练地使用SQL Server进行数据管理和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xrbeck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值