您现在的位置:首页 >> 网络通讯 >> 网络通讯 >> 内容

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

时间:2011/9/3 15:12:04 点击:

  核心提示:很多人费尽心思,都没有找到一个完美的 I/O CP 例程,甚至跟人于误解,先将本人编写的例程公布出来,希望对那些苦苦寻觅的人带来收获。本例程可以作为初学者的学习之用,亦可以作为大型服务程序的通讯模块。...

很多人费尽心思,都没有找到一个完美的 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 的人对它有更深入的了解。
例程测试结果:
600) this.width = 600;">

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

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

相关源码和例程请关注作者博客:http://blog.csdn.net/GuestCode/archive/2009/07/20/4365243.aspx

作者:GuestCode 来源:转载
共有评论 0相关评论
发表我的评论
  • 大名:
  • 内容:
  • 盒子文章(www.2ccc.com) © 2024 版权所有 All Rights Reserved.
  • 沪ICP备05001939号