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

【DIOCP应用案例】-案例2(无毒公主)

案例2<无毒公主>

服务端应用场景:

金融行业交易行情、交易控制指令发布推送服务器。主要是要稳定和快速,对负载要求不高。日常在线20个左右。服务器和客户端都用的diocp实现的iocp异步模式。由于是金融类的应用,对稳定性和推送实时性有非常高的要求(关键时候如果指令延时长了,就会损失真金白银),而一路跟着diocp1走到现在diocp3稳定一年多了 ,各方面的表现完全满足需求。

公司名称:

某金融投资公司

DIOCP版本:

从diocp1到DIOCP3 <自定义的编码器>

QQ昵称:

五毒公主(782834)

建议与问题:

我现在这个还只是一个非常小规模的应用,但是有不同的客户端(指令控制端、管理员端、交易终端)会登录到系统,在不同的客户端多了以后 协议就会多很多。 现在是在在一个编码解码器上按命令字来区分再进行业务层的解析。 虽然能满足需求。但是总觉得 如果要做非常庞大和复杂的系统的话 ,协议的管理将非常麻烦。 所以觉得如果可以一个服务器开放多个端口,每个端口有自己的在线列表,不同的客户端登录到不同的端口、应用不同的编码解码器,同时又能统一管理(应为不同的客户端之前需要数据交、转发)这样就是极好的。

 

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

DIOCP官方社区|MyBean官方社区

http://diocp.wedelphi.com/

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

【DIOCP应用案例】-案例1(Tiger)

2014101153544361

DIOCP是Delphi下进行IOCP服务端通讯开发的一个非常好的开源框架,稳定、高效并且使用起来十分简单。 自己两个多月之前因为需要使用Delphi开发一个TCP服务端,当时也是到处爬文,希望找到一个稳定且好用的IOCP开源框架,整整一周的时间,看了很多,也对比了很多,最后选择了DIOCP,现在服务器端正式版本已经上线运行了36天的时间了,所有运行一切稳定,无论是CPU占用、内存使用还是通信稳定性,DIOCP的表现真的没有让我失望,表现得非常优秀。

服务器环境:
OS:Windows Server 2003
CPU:Intel 双核      内存:2G
网络带宽:5M
网络类型:BGP
实际性能数据:
程序用户同时在线数:1200 – 1300之间   峰值:1511(此峰值为真实用户同时在线数,不是理论性能峰值,目前的用户数只有这些,自己测试的时候,连接数测试过5K,也同样运行稳定)。
服务器端运行到发文时已经36天多了,内存峰值也只有76M,CPU占用一直都稳定在10%以下,应该说各项指标都非常的好,非常满意。

QQ:
Tiger

 

 

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

DIOCP官方社区|MyBean官方社区

http://diocp.wedelphi.com/

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

【DIOCP知识库】连接上下文TIocpClientContext

【概述】

     该类管理远程连接,每一个远程连接会对应一个该类的实例,开发时可以通过继承该类,扩展属性,可以存储更多的连接信息,可以重写OnRecvBuffer方法进行数据逻辑的处理

 

【字段/属性说明】

  • property RemoteAddr: String read FRemoteAddr;

说明:远程连接IP。

 

  • property RemotePort: Integer read FRemotePort;

说明:远程连接端口。

 

  • property SocketHandle: TSocket read FSocketHandle;

说明:连接套接字.

 

【方法说明】

  • procedure DoCleanUp;virtual;

说明: 该方法从对象池中获取一个对象时(TIocpTcpServer.getClientContext)执行该方法,进行相关资源的清理,在该实例不再需要,放回到连接池时(TIocpTcpServer.releaseClientContext)也会执行该方法,该方法同时只会一个线程访问和执行。

 

  • procedure PostNextSendRequest; virutal;

说明:该方法在发送请求得到响应完成并成功(TIocpSendRequest.HandleResponse),会再次调用该方法可以进行下一个请求的发送,如果响应中发现GetLastError不为0或者服务关闭已经停止则不会执行该方法

 

  • procedure OnRecvBuffer(buf: Pointer; len: Cardinal; ErrCode: WORD); virtual;

说明:如果接收到远程数据时会触发该函数的执行,该方法在响应投递的接收数据的请求(TIocpRecvRequest.HandleResponse)中调用,如果响应中发现GetLastError不为0或者服务关闭已经停止则不会执行该方法。

 

 

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

DIOCP官方社区|MyBean官方社区

http://diocp.wedelphi.com/

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

【DIOCP知识库】-粘包的处理

什么是粘包:

   第一次发送  12345, 第二次发送abcde, 底层socket可能会一次性进行发送12345abcde,或者对方可能一次性进行了接收,那接收的时候,你可能想分两次接收才对,第一次接受到12345, 第二次想接收abcde,但是事与愿违,一次就接收完了,这就是粘包的问题。需要自己处理。

 

怎么样处理粘包:

  常见的方法是在包数据的前面加长度比如我先发送一个字节代表长度5 然后再发数据包12345, 这样接收的时候,先读数据包长度,然后再获取包的数据,这样就可以处理粘包问题了。

diocp3的StreamCoder,可以让你直接发送TStream对象,不需要处理粘包的问题。接收到的数据对象就是一个TStream对象

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

DIOCP官方社区|MyBean官方社区

http://diocp.wedelphi.com/

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

【DIOCP3-说明书】监控面板说明

IV](4]8GMYH)GSB9~A])TRN

发送队列 sending Queue: 

    push 任务压入<投递的发送任务>,

    pop 任务取出<投递到IOCP队列时弹出>,

    complted: 完成<IOCP投入完成后得到了响应>

    abort:取消<客户端断开,队列中还没有来得及发送的任务取消>

 

acceptex: 投递了100个,响应了1个

context info: 创建了100个连接上下文(对应的客户端连接)实例, 借出去了100次,还回来1次

sendRequest:创建了1个发送请求<TSendRequest>实例,借出去21次,还回来21次。

 

workers: 3个iocp工作线程(点击[数量]可以看出线程的工作状态<阻塞(忙)/等待(空闲)>)

【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的配合,需要谨慎对待。

DIOCP-3.5.02版本发布

DIOCP 3.5.01
1.修复DHashTable中计算数组位置时可能出现负数,而导致访问违规错误(2014-10-20 14:18:43)

2.在AcceptEx出现异常后,销毁Socket句柄,以免下次重用时出现相同的错误。

3.响应完成后TIocpRecvRequest.HandleResponse,
  先判断是否需要再次投递RecvRequest,然后再减少Context的引用计算器。避免该Context进行返回到连接池后,又进行借出。导致在后面再进行操作时对应的Context不对应

4.添加TIocpCoderClient组件,客户端应用,可以对单个客户端连接注册解码器和编码器,可以连接多个服务器,统一由同一个iocp引擎进行管理。
  DEMO位与:samples\socket-Coder\diocpPusher\Client – IocpCoderClient
  2014-10-23 12:53:07

5.编码器/解码器模式,在连接上下文关闭时,清理已经接收数据,以免上下文对象再次使用时,出现解码错误。感谢(夢難留?  278010908)反馈bug
  2014-10-31 22:09:43

6.优化了PostSendRequest流程

DIOCP 3.5.02
  2014-11-02 00:25:41

【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模式