【杂谈接口】-Delphi中使用接口的一些注意事项

原则一:

   如果一个接口需要as成其他接口使用, 如果该接口的实例在该过程中可能会被释放,那么最好将 as 的接口用临时变量保存起来, 然后在使用完后及时将临时接口:= nil;

   如果用as成其他接口直接使用时(如  (lvIntf as IOpera).DoSomething() )系统会自动生成临时变量,存放对应的接口,然后在过程退出时,进行接口变量清理时,会触发接口实例的_release方法。如果对应实例的内存块被破坏,就有可能会导致访问违规的异常。

// 下面这段执行时可能会出现AV异常
var
  lvPlugin: IPlugin;
  lvStoProcessor   : IStoProcessor;
begin
  //物料库存_逻辑处理
  lvPlugin := FPluginConsole.CreatePlugin('STOLogic');
  try
    lvPlugin.PrepareForCreate;
    
    with lvPlugin as IStoProcessor do
    begin
      //处理结存
      DoOnHand(pvFormKey, OPERA_UNAPPR);
      CheckNegative(pvFormKey);
    end;
  finally
    lvPlugin.FreePlugin;
    lvPlugin := nil;
  end;

  // 执行库存处理后的其他脚本
  ExecuteAfterScript(pvFormKey, FJsnConfig.O['STO']);

end;



// 这样做是最安全的
var
  lvPlugin: IPlugin;
  lvStoProcessor   : IStoProcessor;
begin
  //物料库存_逻辑处理
  lvPlugin := FPluginConsole.CreatePlugin('STOLogic');
  try
    lvPlugin.PrepareForCreate;
    
    lvStoProcessor := lvPlugin as IStoProcessor;
    //处理结存
    lvStoProcessor.DoOnHand(pvFormKey, OPERA_UNAPPR);
    lvStoProcessor.CheckNegative(pvFormKey);

    lvStoProcessor := nil;
  finally
    lvPlugin.FreePlugin;
    lvPlugin := nil;
  end;

  // 执行库存处理后的其他脚本
  ExecuteAfterScript(pvFormKey, FJsnConfig.O['STO']);
end;

 

 

…. 未完待续

 

 

 

 

 

==========================================

DIOCP官方社区|MyBean官方社区

http://diocp.wedelphi.com/

==========================================

【DIOCP-DEMO说明】所有演示DEMO的简要说明

samples目录下面为自带的DEMO

发现有很多朋友不知道如何开始DIOCP,下面是DEMO的简单说明,希望对大家有用

C#\Simple
  用C#写的一个简单的回传测试,服务端开启ECHO服务器即可
 
samples\ECHO
  IOCP回射测试,在服务端收到数据后立即发送回客户端,
  可以从这个DEMO看出如何在服务端接收数据,返回数据。
 
samples\iocpTask
  iocpTask演示DEMO,异步任务执行DEMO,
  可以在该DEMO中学到,如何投递任务让主线程去执行(访问UI最好在主线程访问)。
  可以学到如果注册信号任务,如何触发信号任何,如何反注册信号。
    信号任务可以在一个位置进行注册,在子线程中通过信号进行触发,可以有效的进行解耦你的代码。
  更多的功能可以使用QDAC3中的QWorkers
 

samples\qdac3-source
  qdac3项目源码, 优秀的开源项目,你可以更新到qdac项目中去更新最新版,
  一些DEMO中有使用到。
 
samples\safeLogger
  SafeLogger的使用DEMO,
  可以在该DEMO中学习到如何使用SafeLogger将日志显示到MEMO,
  如何使用SafeLogger将日志记录到文件。
 
samples\simple
  最简单的DIOCP使用例子
  客户端采用阻塞模式,发送数据到服务端,然后服务端自己返回,客户端等待接收数据.
 
=================================================================================
samples\socket-Coder
  编码器方式的DEMO,编码器模式的存在可以让大家在使用DEMO的时候直接传递对象,而不用你去处理粘包问题,
  编码器模式在解码成功后将接收到的对象投递到iocpTask/qworkers的线程中去执行,这样不会阻塞diocp的通信线程,即使在处理复杂的业务逻辑,也不影响到数据的接收和发送。
 
samples\socket-Coder\diocpCoders
  常用的一些编码和解码器,
  JsonStream: 包含一个Json和一个Stream 
  TStream : 通用简单的TStream对象,头标记 + CRC + 数据长度 + 数据
  TMsgPackCoder: msgpack对象。使用Qmsgpack进行解析
 
  推荐搭建使用TStream的编码和解码模式,因为很多对象都能序列化到流和从流中还原。
  例如 msgpack -> TStream -> diocp -> TStream ->msgpack
 
samples\socket-Coder\StreamCoder
  Stream编码器模式例子。
  可以在该DEMO中学习到如果直接发送一个TStream对象,如何在服务端中接收到到这个对象,如果将一个Stream对象推送给客户端。
  可以在该DEMO学习到iocpCoderClient(客户端iocp)的使用。
  客户端有阻塞模式(RawTcpClient)和异步接受(Client)模式。
 
samples\socket-Coder\DataModuleDEMO-Andriod
  手机Andriod客户端与diocp服务器交换数据的例子
  可以学习到如何在andriod中请求diocp执行sql语句返回一个TClientDataSet的数据,并在手机上进行显示
 
samples\socket-Coder\DataModuleDEMO
  三层数据演示
  可以学习到如何在客户端中请求diocp执行sql语句返回一个TClientDataSet的数据,并在客户端进行显示
samples\socket-Coder\DataModuleDEMO-SimpleMsgPack
  使用simpleMsgPack代替QMsgPack,可以在D7中使用
 
samples\socket-Coder\DIOCPFileSERVER
  演示DIOCP如何作为文件服务器使用
  可以学习到如果分块上传文件和下载文件

【DIOCP3-说明书】DIOCP3的输出日志

DIOCP3除了有详细的监控面板之外,还有详细的输出日志,当然需要打开日志编译开关!

在工程选项加入DEBUG编译指令,这样在运行中就可以看到DIOCP3的运行详细日志

日志输出在EXE相同目录的LOG文件夹下面。

 

日志说明:

8276_iocpSVR_2015010712.log

8276      是进程ID

iocpSVR是程序中iocpTcpSERVER的名字

2015010712 日志文件生成时间

 

12:30:24:318[message][PID:8276,ThreadID:5520]:[756]接收到0字节的数据,该连接将断开!

PID是进程ID

ThreadID: 工作线程ID

756 套接字

收到一个0字节长度的数据,diocp认为对方已经断开,diocp将关闭套接字对应的连接。

发生在响应WSARecv的事件中。

 

12:37:45:764[message][PID:8276,ThreadID:5520]:[760]响应接收请求时发现IOCP服务关闭

IOCP服务已经关闭, 一般发生在程序关闭,IOCP服务停止的时候,有客户端进行了连接的时候

发生在响应WSARecv的事件中。

 

12:37:45:764[message][PID:8276,ThreadID:5520]:[760]投递发送数据请求时出现了错误。错误代码:10038

在进行发送数据时出现的异常,在执行底层API时出现的异常,错误代码是系统给出的,可以百度,或者进入CMD运行

net helpmsg 10038 查看对应的错误原因。

错误代码121:  信号灯超时已到

一般发生情况,iocpTcpServer打开了KeepAlive,建议关闭KeepAlive自己进行心跳处理。

 

【DIOCP3-说明书】关于服务端的KeepAlive属性(心跳)

DIOCP3中服务端(IocpTcpServer)中提供一个KeepAlive属性,之前版本默认开启,2014-12-30之后的版本默认是关闭的。

首先要搞明白KeepAlive的作用是什么,服务端开启KeepAlive后在建立连接的时候会设置Socket的SO_KeepAlive选项

 

MSDN的解释是这样的:

http://msdn.microsoft.com/en-us/library/windows/desktop/ee470551(v=vs.85).aspx

SO_KEEPALIVE socket option

The SO_KEEPALIVE socket option is designed to allow an application to enable keep-alive packets for a socket connection.

SO_KEEPALIVE 套接字选项是被设计允许应用程序对一个套接字连接开启keep-alive包(翻得不好,意思基本上对了)

 

开启后,如果客户端关闭(或者网络异常)等情况,服务端就会马上知道。这样服务端就会断开该连接。但是该选项并不是所有的环境下面都能运作的很好,据我所知,如果客户端是Andriod,就不行,还有一些硬件设备,也是不行的,所以我改成默认是关闭状态。

关闭后,问题就来了,客户端如果断线,可能服务器端就无法及时发现,这样服务端会出现很多死链接。只有当进行对socket进行操作(比如发送数据socket.send)时遇到异常时才会发现该连接无效,然后进行断开。

一般实际应用,如果要保持长连接,可以在客户端发送心跳数据包(这个数据包因为要根据自己定义的协议格式进行发送,所以无法在底层完成。)然后在服务端做超时检测,如果超过一定的时间(这个时间自己协定)就把该连接主动T掉。