• Delphi实现高性能的Socket通讯服务器(完成端口模型IOCP)


    很多人费尽心思,都没有找到一个完美的 I/O CP 例程,甚至跟人于误解,先将本人编写的例程公布出来,希望对那些苦苦寻觅的人带来收获。本例程可以作为初学者的学习之用,亦可以作为大型服务程序的通讯模块。其处理速度可以说,优化到了极点。如果理解了本例程的精髓,加上一个高效的通讯协议,你完全可以用它来构建一个高性能的通讯服务器。

      在公布代码前,先谈谈I/O CP。对I/O CP的函数不多做说明了,网上很多,都一样。在此本人仅说一些技术上要注意的问题

    一、如何管理内存
    1、IO数据缓冲管理
     动态分配内存,是一种灵活的方式。但对于系统资源浪费是巨大的。因此本人采用的是预先分配服务器最大需要的内存,用链表来管理。任何时候分配交还都不需要遍历,仅需要互斥而已。
     更巧妙的是,将IO发送信息和内存块有机的结合在一起,减少了链表的管理工作。

     //IO操作标志
     TIOFlag = (IO_ACCEPT, IO_READ, IO_WRITE);
     //IO操作信息
     PIOInfo =^ TIOInfo;
     TIOInfo = packed record
      Overlapped: TOverlapped;  //重叠结构
      DataBuf: TWSABUF;          //IO数据信息
      Socket: TSocket;
      Flag: TIOFlag;
      TickCountSend: DWord;
      Next: PIOInfo;
      Prior: PIOInfo;
     end;

     PUNode =^ TUNode;
     TUNode = record
      Next: Pointer;
     end;

     PIOMem =^ TIOMem;
     TIOMem = packed record
      IOInfo: TIOInfo;
      Data: array[1..IO_MEM_SIZE] of Byte;
      //申请内存的时候,返回的是Data的地址
     end;


    2、链路数据管理
     采用双向链表结构,减少删除节点时遍历消耗的时间

     //每个连接的信息
     PLink =^ TLink;
     TLink = record
      Socket: TSocket;
      RemoteIP: string[30];
      RemotePort: DWord;
      //最后收到数据时的系统节拍
      TickCountActive: DWord;
      //处理该连接的当前线程的信息
      Worker: PWorker;
      Data: Pointer;    //应用层可以设置这个成员,当OnReceive的时候,就不要每次遍历每个连接对应的数据区了
      Section: TRTLCriticalSection;
      Next: PLink;
      Prior: PLink;
     end;

    二、如何管理线程
     每个工作线程创建的时候,调用:OnWorkerThreadCreateEvt,该函数可以返回这个线程对应的信息,比如为该线程创建的数据库连接控件或对应的类等,在OnReceive的可以从Link的Worker访问该成员Worker^.Data。

    //工作线程信息
     PWorker =^ TWorker;
     TWorker = record
      ID: THandle;
      CompletionPort: THandle;
      Data: Pointer;  //调用OnWorkerThreadCreateEvt返回的值
      //用于反应工作情况的数据
      TickCountLong,
      TickCountActive: DWord;
      ExecCount: Integer;
      //线程完成后设置
      Finished: THandle;
      Next: PWorker;
     end;

     同理,服务线程也是具有一样的特点。相见源码。

      关于线程同步,一直是众多程序头疼的问题。在本例程中,尽量避免了过多的互斥,并有效地防止了死锁现象。用RTLCriticalSection,稍微不注意,就会造成死锁的灾难。哪怕是两行代码的差别,对多线程而言都是灾难的。在本例程中,对数据同步需要操作的是在维护链路链表方面上。服务线程需要计算哪个连接空闲超时了,工作线程需要处理断线情况,应用层主动发送数据时需要对该链路独占,否则一个在发送,一个在处理断线故障,就会发送冲突,导致灾难后果。

     在本人的压力测试中,已经有效的解决了这个问题,应用层部分不需要做什么同步工作,可以安心的收发数据了。同时每个线程都支持了数据库连接。


    三、到底要创建多少个工作线程合适
     很多文章说,有N个CPU就创建N个线程,也有说N*2+2。最不喜欢说话不负责任的人了,本例程可以让刚入门 I/O CP 的人对它有更深入的了解。

    四、该不该使用类
      有人说,抛弃一切类,对于服务器而言,会为类付出很多代价,从我的观点看,为类付出代价的,主要是动态创建的原因。其实,类成员访问和结构成员访问一样,需要相对地址。如果都是预先创建的,两者没有多大的差别。本例程采用裸奔函数的方式,当然在应用层可以采用类来管理,很难想象,如果没有没有类,需要多做多少工作。

    五、缺点
      不能发大数据包,只能发不超过固定数的数据包。但对于小数据报而言,它将是优秀的。

    原文:http://club.topsage.com/thread-2234914-1-1.html

  • 相关阅读:
    atitit.ntfs ext 文件系统新特性对比
    Atitit.图片木马的原理与防范 attilax 总结
    Atitit.图片木马的原理与防范 attilax 总结
    Atitit.jdk java8的语法特性详解 attilax 总结
    Atitit.jdk java8的语法特性详解 attilax 总结
    Atitit.远程接口 监控与木马   常用的api 标准化v2 q216
    Atitit.远程接口 监控与木马   常用的api 标准化v2 q216
    Atitit..jdk java 各版本新特性 1.0 1.1 1.2 1.3 1.4 1.5(5.0) 1.6(6.0) 7.0 8.0 9.0 attilax 大总结
    Atitit..jdk java 各版本新特性 1.0 1.1 1.2 1.3 1.4 1.5(5.0) 1.6(6.0) 7.0 8.0 9.0 attilax 大总结
    Atitit.跨平台预定义函数 魔术方法 魔术函数 钩子函数 api兼容性草案 v2 q216  java c# php js.docx
  • 原文地址:https://www.cnblogs.com/tc310/p/1837980.html
Copyright © 2020-2023  润新知