【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掉。

【DIOCP3-说明书】关于DIOCP3的服务申明

近段时间发现明显关注diocp3的人越来越多,首先感谢大家选择使用DIOCP3,虽然我不能保证diocp3底层100%不出问题,但是我会尽量去保障diocp3底层代码的稳定,使他成为一个稳定的通信库。由于作者现在还是一个苦逼的上班族,还得为老板卖命。为了不至于被老板炒鱿鱼。我今天慎重考虑了下,平常大家在使用diocp3的过程中,碰到bug,请大家点击<<【DIOCP3-说明书】关于DIOCP3服务端常见的问题>> (http://diocp.wedelphi.com/?p=115)看看是否有类似的情况。如果还是搞不定不要担心,没有请剥离一份简单的代码(客户端和服务端)请不要使用三方控件,发可以编译的工程文件给我,我尽量安排时间(一般在中午休息和晚上)调试程序。谢谢大家理解!

【DIOCP3-说明书】关于DIOCP3服务端常见的问题

这几天群里面使用DIOCP3的人越来越多,一般或多或少都会碰到一些问题,碰到问题并不可怕,可怕的是不知道如果下手去查找问题,解决问题。服务端的编写都一样,需要小心谨慎,毕竟我们要写的程序是需要长期运行的,不能有半点马虎。每一行代码都要认真对待,我现在把大家碰到的问题整理下,希望大家碰到问题时,好好检查下代码,看看是不是也有类似的问题。

 

第一,也是很多人容易犯的错误,不要在diocp3事件中,直接访问UI

    这个情况几乎我每次都强调,但基本上90%的人都犯这样的错误,因为diocp3中,所有的事件都在线程中触发的,UI资源一般都不是线程安全的,直接访问UI,一般会锁死线程,程序退出时无法退出进程,而且会出现不可预料的错误。
   日志记录方面如果要显示到Memo中,请使用线程日志安全类SafeLogger,可以参考safelogger的demo,虽然Memo是做的SendMessage处理,还是不建议直接在线程池对Memo直接进行操作

 

第二,访问违规错误。

    首先跟踪下代码看看,访问违规的错误代码是哪一行引起的,看看对象是否提前是否,或者是公用的对象,如果是公用的对象是否有做临界处理。如果异常停留在diocp底层的代码,多数情况下面是程序栈帧被破坏,检查下指针操作的位置(move,copyMemory的位置)看看目标指针是否有越界和指定错误的情况。建议应用层,少用指针和结构体,用操作对象或者内存流来代替结构体和指针操作。

 

第三,日志的问题。

    diocp底层记录日志在Debug模式下面记录日志比较详细,每个连接的断开<包括主动和对方断开>都做了日志记录。那些日志一般都是正常存在的。

 

第四,心跳和重连。

    diocp3中有设置KeepAlive的选项,但是这个选项不是万能的,有很多客户端突然断线,或者某些机器上面会不起作用。这个时候需要做业务层的心跳包,一般处理方法是,客户端定时发送很小的数据包,到服务端,服务端做轮询的超时检测,把超时的连接主动在服务端T掉,客户端在发送数据包的过程中也可以触发没有正常连接的异常,然后进行重连。

 

最后再强调下,服务端编程,要处理要多线程资源访问的问题,注意好指针操作。要写出稳健的服务端,还需要逻辑代码和diocp3的配合,需要谨慎对待。

【DIOCP3-说明书】iocpTask中添加信号触发任务功能

【应用场景】

信号触发任务是为了可以重复触发信号对应的任务,而且可以把回调函数的绑定和触发信号分离,比如你可以在主窗体中注册信号对应的事件,然后在任何的线程和单元中安全的触发信号。

 

【使用DEMO】

主窗体中注册信号绑定回调函数

//注册信号,并绑定信号对应的回调函数
iocpTaskManager.registerSignal(1, OnSignalWork);


// 回调事件函数
procedure TfrmMain.OnSignalWork(pvTaskRequest:TIocpTaskRequest);
var
  lvData:TSimpleDataObject;
begin
  lvData:= TSimpleDataObject(pvTaskRequest.TaskData);
  if GetCurrentThreadId = MainThreadID then
  begin
    Memo1.Lines.Add('exeucte signal task in main thead:' + lvData.DataString1);
  end else
  begin
    logMessage('exeucte signal task in thread:' + lvData.DataString1);
  end;
end;

 

 

 // 可以在任何的地方安全的触发信号
 iocpTaskManager.SignalATask(1, TSimpleDataObject.Create('signal param'), ftFreeAsObject);

 

【相关函数】

注册函数:

procedure registerSignal(pvSignalID: Integer; pvTaskWork: TOnTaskWork);

pvSignalID      注册的信号ID, 重复注册时会引发异常。

pvTaskWork   信号对应的回调函数

 

触发函数:

procedure SignalATask(pvSignalID: Integer; pvTaskData: Pointer = nil;
        pvDataFreeType: TDataFreeType = ftNone; pvRunInMainThread: Boolean = False;
        pvRunType: TRunInMainThreadType = rtSync);

 

pvSignalID             信号ID

pvTaskData          触发信号带入的一些参数数据,在回调函数中可以获取(pvTaskRequest.TaskData)

pvDataFreeType   参数数据在任务执行完成或者投递失败后释放的方式(; , 

                             ftNone, 不做任何处理

                             ftFreeAsObject 调用TObject(TaskData).Free

                             ftUseDispose,    调用Dispose(TaskData)

pvRunInMainThread 是否在主线程中执行回调函数

pvRunType         主线程同步的方式,不带包的DLL中不能用rtSync模式