Delphi编写事件模型客户端(1)

简介:
好久没有写新的东西了, BLOG 都快荒废了。今天有时间来写一写东西了。
以前我已经写了 IOCP 和完成例程的基本思路,都是写的服务端部分。今天我来写一写如何使用事件模型编写一个客户端。
事件模型编写客户端比较容易。
首先确定事件模型中使用到的类。
TWorkThread =class;     工作线程类,此类主要用于相应接收、发送和断开等网络事件。
TIOEvents = class;        IO 事件类,此类主要用于实际处理。
下来我们定义一些相关的常量,和使用的相关结构。
const
  DATA_BUFSIZE        = 1024 * 4;           
  DEFAULT_BUFSIZE     = 1024 * 8;
  IOC_IN              = $80000000;
  IOC_VENDOR          = $18000000;
  IOC_out             = $40000000;
  SIO_KEEPALIVE_VALS  = IOC_IN or IOC_VENDOR or 4;
  HEAP_ZERO_MEMORY    = $00000008;
type
  // 心跳结构
  TTCP_KEEPALIVE = record
    onoff: Integer;                             // 开关
    keepalivetime: Integer;               // 间隔时间
    keepaliveinterval: Integer;          // 在间隔时间中的次数
  end;
  PTCP_KEEPALIVE = ^TTCP_KEEPALIVE;
type
  TOperation=(IO_ACCEPT,IO_WRITE,IO_READ,IO_CLOSE);
  //IO 结构
  PIOData = ^TIOData;
  TIOData = record
    Overlapped: OVERLAPPED;
    DataBuf: TWSABUF;
    Socket:TSocket;                         // 套接字
    BufferLen:Integer;                      // 数据长度
    Buffer:array[0..DATA_BUFSIZE-1] of char;// 数据信息,包括数据头信息
    FNetClass:Pointer;                      // 类的指针
  end;
  // 发送队列结构
  PSendBuffer = ^TSendBuffer;
  TSendBuffer = record
    Buf:Pchar;
    BufLen:Integer;
    Next:PSendBuffer;
  end;
 
我来解释一下以上常量和结构的作用。
DATA_BUFSIZE 为每次发送的数据长度。
DEFAULT_BUFSIZE 为默认数据长度,用来限制每次发送数据包的最大长度。会在发送数据的时候使用到它。
DEFAULT_BUFSIZE 以下的常量和 TTCP_KEEPALIVE 结构都是用做心跳设置的,具体可以看我的 BLOG 中关于心跳的文章。
PIOData IO 结构。其中 FNetClass 用于定义的类指针此指针指向 TIOEvents
PSendBuffer 结构是一个发送队列。
以下是定义的事件信息。
type
  (**** 事件定义部分 ****)
  TOnConnect    = procedure of object;
  TOnDisConnect = procedure of object;
  TOnReceive    = procedure(Data: Pchar; DataLen: Integer) of object;
  TOnSend       = procedure(Data: Pchar; DataLen: Integer) of object;
  TOnError      = procedure(ErrorID:Integer) of Object;
 
然后定义相关的类。
TIOEvents 类中定义使用的相关信息:
FRecvIOData,
FSendIOData:TIOData;         // 接收和发送的 IO 结构
Sending:Boolean;              // 是否正在发送
FFirstNode,
FLastNode:PSendBuffer;        // 发送队列
FTotalCount:Cardinal;          // 需要发送的数量
FEventCS:TRTLCriticalSection;  // 发送缓冲的临界区
FWorkThread:TWorkThread;     // 工作者线程
function  SetKPAlive:Boolean;   // 开启心跳
function  PostRecv:Boolean;     // 投递接收
function  PostSend:Boolean;     // 投递发送
procedure ClearBuffer;          // 清空所有发送 BUFFER
 
具体实现方式我会在后面逐一说明。在这里先说明一下 FSendIOData 结构。对于发送数据来说,我的处理方法是:将数据放入发送队列中。如果当前数据正在发送,则不做处理,如果当前数据已经发送完毕,那么我会检测发送队列中是否还有数据需要发送,如果有,则继续发送。
FSocket:TSocket;                 // 套接字
FEventNums:Word;              // 当前事件数量
FEventArray:Array [0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT; // 事件数组
FSocketArray:array[0..WSA_MAXIMUM_WAIT_EVENTS - 1] of TSocket;    // 套接字数组  
类的 PUBLIC 为。
FSending:Boolean;         // 是否正在发送数据
procedure Start;            // 启动网络
function  SocketWrite(Data: Pchar; DataLen: Integer):Boolean; // 发送数据
procedure Close;           // 关闭
 
对于 TIOEvents 类的属性有以下信息。
{ 属性 }
property Active:Boolean read FActive write SetActive;                 // 是否启动
property MainIP:String read FMainIP write SetMainIP;                  // 服务端 IP
property MainPort:Integer read FMainPort write SetMainPort;     // 服务端端口
property SendLen:Cardinal read FSendLen write SetSendLen;      // 发送的对大数据长度
property KeepAlive:Boolean read FKeepAlive write SetKeepAlive;  // 是否启动 Keepalive
property KeepTime:Word read FKeepTime write SetKeepTime;   //Keep 的监听时间
{ 事件 }
property OnConect:TOnConnect read FOnConect write SetOnConect;  // 客户端连接
property OnDisConnect:TOnDisConnect read FOnDisConnect write SetOnDisConnect;   // 客户端正常断开
property OnRecive:TOnReceive read FOnRecive write SetOnRecive;   // 接收客户端数据
property OnSend:TOnSend read FOnSend write SetOnSend;       // 向客户端发送数据
property OnError:TOnError read FOnError write SetOnError;        // 返回错误信息
工作者线程类处理的信息就比较简单了。
// 工作者线程
  TWorkThread = class(TThread)
  private
    { Private declarations }
    FParent:TIOEvents;
    procedure SocketRead;
    procedure SocketWrite;
    procedure SocketClose;
  protected
    { Protected declarations }
    procedure Execute; override;
  public
    { Public declarations }
    constructor Create(Parent:TIOEvents);
    destructor Destroy; override;
  end;
到此,定义类已经定义完成,下篇我将写 TIOEvents 类如何实现。希望我的代码对大家有一定的帮助。
本文转自狗窝博客51CTO博客,原文链接http://blog.51cto.com/fxh7622/159224如需转载请自行联系原作者

fxh7622
相关文章
|
3月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
163 0
|
SQL 缓存 监控
纯干货:客户端代码框架设计!
纯干货:客户端代码框架设计!
纯干货:客户端代码框架设计!
|
C# C++
C# Winform WCF 调试服务端的程序(三种方法)
推荐方法三 作者:jiankunking 出处:http://blog.csdn.net/jiankunking 方法一: 服务端设置: 1、打开需要调试的解决方案,在WCF服务端项目上右键,将其设置为启动项目 2、在该解决方案下点击 或者直接按F5启动WCF服务端项目。 3、寻找端口号: i、方式一 启动调试后,浏览器中会出现下面的界面 需要留意的是地址栏中的端口号。 ii、方式二
4226 0