好久没有写新的东西了,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