Delphi编写系统服务七:完成端口演示

本文介绍了如何使用Delphi编写一个利用完成端口处理大量并发Socket连接的服务程序。文章详细阐述了完成端口在高并发场景下的优势,并提供了一个能处理5100个客户端连接的简单示例,展示了如何创建、管理和使用完成端口,以及在Windows XP上的测试结果。主要关注点包括线程管理、内存分配和服务器性能优化。
摘要由CSDN通过智能技术生成

 在开发大量Socket并发服务器,完成端口加重叠I/O是迄今为止最好的一种解决方案,下面是简单的介绍:
   “完成端口”模型是迄今为止最为复杂的一种I/O模型,特别适合需要同时管理为数众多的套接字,采用这种模型,往往可以达到最佳的系统性能。但是只适合Windows NT和Windows 2000及以上操作系统。因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千套接字的时候,而且希望随着系统内安装的CPU数量增多,应用程序的性能也可以线性提升,才考虑采用“完成端口”模型。
    重叠I/O(Overlapped I/O)模型使应用程序达到更佳的系统性能。重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构,一次投递一个或多个Winsock I/O请求。针对哪些提交的请求,在它们完成之后,应用程序可为它们提供服务。该模型适用于除Windows CE之外的各种Windows平台。
    开发完成端口最具有挑战是线程个数和管理内存,创建一个完成端口后,就需要创建一个或多个“工作者线程”,以便在I/O请求投递给完成端口对象后,为完成端口提供服务。但是到底应创建多少个线程,这实际正是完成端口最为复杂的一个方面,一般采用的是为每一个CPU分配一个线程(有的是CPU个数加1,有的是CPU*2的线程个数)。内存分配效率低是因为应用程序在分配内存的时候,系统内核需要不停的Lock/UnLock,而且在多CPU的情况下,会成为整个程序性能的瓶颈,不能随CPU的个数增加而性能提高,一种比较好的做法一个一次分配多块内存。
    下面是我写一个的完成端口的演示程序,在我的电脑上测试可以达到链接5100个客服端,服务器性能还很好,由于我写的客服端占用资源比较的,最后直接重启了,具体见代码。演示程序主要的瓶颈在于发消息的这一块,在实际应用中应去掉。

(配置:操作系统 Microsoft Windows XP Professional 操作系统 Service Pack 版本 Service Pack 2;CPU:Intel(R) Pentium(R)4 2.40GHz 2.40GHz;内存:2G;主板:华硕P4P800)。

主要源代码:(Delphi 7编写),下载地址:http://download.csdn.net/source/818039

{*******************************************************}
{                                                       }
{       高性能服务器,这个是一个演示DEMO                }
{                                                       }
{       联系邮箱:fansheng_hx@163.com                   }
{                                                       }
{*******************************************************}

unit IOCPSvr;

interface

uses
 Windows, Messages, WinSock2, Classes, SysUtils, SyncObjs;

const
  {* 每一次发送和接收的数据缓冲池大小 *}
  MAX_BUFSIZE = 4096;
  {* 关闭客户端通知消息 *}
  WM_CLIENTSOCKET = WM_USER + $2000;

type
  {* Windows Socket 消息 *}
  TCMSocketMessage = packed record
    Msg: Cardinal;
    Socket: TSocket;
    SelectEvent: Word;
    SelectError: Word;
    Result: Longint;
  end;

  {* IOCP服务器运行轨迹 *}
  TSocketEvent = (seInitIOPort, seUninitIOPort, seInitThread, seUninitThread,
    seInitSocket, seUninitSocket, seConnect, seDisconnect, seListen, seAccept, seWrite, seRead);
const
  CSSocketEvent: array[TSocketEvent] of string = ('InitIOPort', 'UninitIOPort', 'InitThread', 'UninitThread',
    'InitSocket', 'UninitSocket', 'Connect', 'Disconnect', 'Listen', 'Accept', 'Write', 'Read');
type
  {* 产生错误类型 *}
  TErrorEvent = (eeGeneral, eeSend, eeReceive, eeConnect, eeDisconnect, eeAccept);

  {* 完成端口传递的结构体 *}
  TIOCPStruct = packed record
    Overlapped: OVERLAPPED;
    wsaBuffer: TWSABUF;
    Event: TSocketEvent; //读或写
    Buffer: array [0..MAX_BUFSIZE - 1] of Char;
    Assigned: Boolean;  //表示已经分配给某个客户端
    Active: Boolean;    //客服端内部使用,表示是否正在使用
  end;
  PIOCPStruct = ^TIOCPStruct;

  EMemoryBuffer = class(Exception);
  ESocketError = class(Exception);

  TMemoryManager = class;
  TServerSocket = class;
  TSymmetricalSocket = class;

  TMemoryManager = class
  private
    {* 管理内存使用 *}
    FList: TList;
    {* 分配和释放时候使用的锁 *}
    FLock: TCriticalSection;
    {* 服务器 *}
    FServerSocket: TServerSocket;
    function GetCount: Integer;
    function GetIOCPStruct(AIndex: Integer): PIOCPStruct;
  public
    constructor Create(AServerSocket: TServerSocket; ACount: Integer); overload;
    constructor Create(AServerSocket: TServerSocket); overload;
    destructor Destroy; override;

    {* 分配内存使用权 *}
    function Allocate: PIOCPStruct;
    {* 释放内存使用权 *}
    procedure Release(AValue: PIOCPStruct);
    property Server: TServerSocket read FServerSocket;
    property Count: Integer read GetCount;
    property Item[AIndex: Integer]: PIOCPStruct read GetIOCPStruct;
  end;

  {* 客服端链接服务器触发此事件,如果要拒绝链接,把AConnect := False *}
  TOnBeforeConnect = procedure(ASymmIP: string; AConnect: Boolean) of object;
  {* 链接完成之后触发此事件 *}
  TOnAfterConnect = procedure(ASymmetricalSocket: TSymmetricalSocket) of object;
  {* 断开连接触发事件 *}
  TOnAfterDisconnect = procedure(ASymmetricalSocket: TSymmetricalSocket) of object;
  {* 收到数据会触发此事件 *}
  TOnDataEvent = procedure(ASymmetricalSocket: TSymmetricalSocket; AData: Pointer;
    ACount: Integer) of object;
  {* 错误触发事件 *}
  TOnErrorEvent = procedure(AError: Integer; AErrorString: string; AInfo: string; var AHandleError: Boolean) of object;
  {* 服务器运行LOG *}
  TOnLog = procedure (ASocketEvent: TSocketEvent; AInfo: string) of object;

  {* 服务器,负责建立完成端口,管理内存和管理客服端,及Socket消息循环 *}
  TServerSocket = class
  private
    {* 内存管理 *}
    FMemory: TMemoryManager;
    {* 端口 *}
    FPort: Integer;
    {* 套接字 *}
    FSocket: TSocket;
    {* 完成端口句柄 *}
    FIOCPHandle: THandle;
    {* 消息循环句柄 *}
    FHandle: THandle;
    {* 对等的客服端 *}
    FClients: TList;
    {* 服务器运行线程 *}
    FThreads: TList;
    {* 监听线程 *}
    FAcceptThread: TThread;
    {* 表示是否激活 *}
    FActive: Boolean;
    {* 锁 *}
    FLock: TCriticalSection;
    {* 错误触发事件 *}
    FOnError: TOnErrorEvent;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值