【DIOCP-说明书】常见问题(FAQ)持续更新

1.diocp-v5使用在哪里可以得到帮助
http://www.diocp.org/?page_id=159
  可以浏览官方社区的一些基础说明,该文档会陆续进行整理。

 

2.diocp-v5怎么样编译DEMO 
  <<关于DEMO的编译>>
http://www.diocp.org/?p=16
2.1
Q:在编译DiocpV5DEMO是编译不通过
A:你编译DEMO的时候,把diocpv5\source加入到路径就好
  不要把所有Source下的文件添加到工程,因为有一些文件是给android/ios平台用的当然不能在windows下面编译,
  例如:diocp.core.rawPosixSocket.pas

 

3.服务端避免不了和多线程打交道,请先好好熟悉多线程的基础知识和注意实现。下面有多线程的一些文章,请认真看看。
http://blog.qdac.cc/?p=890

 

 

Q:服务端在线信息显示为何与实际有差异?
Q:服务端许多死链接都显示在线?
A:DIOCP默认关闭了心跳。所以会造成很多死链接,仍然显示在线,详细原因点击查看该文章:http://www.diocp.org/?p=189
(【DIOCP3-说明书】关于服务端的KeepAlive属性(心跳))

 

Q: 在Diocp中我需要另外开线程去处理逻辑吗?
A: 在编码层(TDiocpCoderTcpServer), 逻辑处理事件(OnContextAction), 是由DiocpTask/Qworker(编译开关(QDAC_QWorker)可以进行切换)驱动的,和底层的通信线程是不相干扰。所以在编码层不需要使用另外的线程池,来处理逻辑。

 

Q: 同一个连接的OnContextAction会同时被多个线程触发吗?
A:编码层的OnContextAction是排队处理的,底层接收到数据后交由注册的解码器进行解码, 解码成功后,放到任务队列,然后依次触发OnContextAction,所以同一个连接的OnContextAction同时只会有一个线程调用。

 

Q: Diocp的断开日志
A: Diocp在DEBUG模式下面会记录详细的日志。会记录每个连接断开的原因。如下
   1> xxxx:[2824]投递发送数据请求时出现了错误。错误代码:10054
     说明: 这种日志在发送数据的时候系统返回了错误代码,可以根据错误代码(如10054),查询到相应的错误信息。
   2> xxxx: [720]响应接收请求时出现了错误。错误代码:64!
     说明:这种日志在响应接收请求时系统返回的错误,根据错误代码查询响应的信息。
   3> xxxx: [704]接收到0字节的数据,该连接将断开!
     说明: 服务端接收到长度为0字节的数据,认为对方关闭了连接。服务端相应的会释放连接触发OnDiscconected函数。
   4> xxxx :[812]执行[CheckNextSendRequest::lvRequest.ExecuteSend]失败: 处理投递发送请求数据包时,发现异步关闭请求(Request.Tag = -1)。进行关闭处理!
     说明: 这种日志一般在服务端投递了异步关闭请求(PostWSAClose)时,会出现该日志。
   上面列举的日志都是正常的,DIOCP只是记录详细情况,以便出现问题时能有据可查,请勿惊慌。
   * 中括号[]中的数字是连接对应的SocketHandle,是连接的套接字句柄。

   MSDN上面错误代码说明:

      https://msdn.microsoft.com/en-us/library/ms740668.aspx 

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

 

Q: Diocp如何关闭日志记录。
A: Diocp在DEBUG模式会记录详细日志。可以把DEBUG模式关闭
   D2007工程为例: Project->Options->Directories/Conditionals->Conditional defines 中把DEBUG删除

DIOCP 小白精简版

DIOCP 修改版(YangYxd)

【当前版本】
2015.05.27.001

【主要更改】
– ———————————–
– 2015.05.27
– ———————————–
+ 加载HTTP服务,增加表单提交支持,可上传小文件。
HTTP模块主要功能:
1. 支持标准Http访问服务
2. 支持局部传输
3. 支持文件下载,断点续传
4. 支持表单提交
5. 支持GZip压缩传输
– ———————————–
– 2015.05.26
– ———————————–
* 修复TIocpCustomContext中使用内存池Push时未加锁引发的严重BUG。
* 优化HTTP服务。
+ 同步DIOCP在TIocpConnection中将请求加入队列,以免连续多个请求时产生多线程处理一个连接,引发问题。
– ———————————–
– 2015.05.21
– ———————————–
* 修复TIocpTcpCodecServer解码流程中的BUG。
* 优化HTTP服务
+ 增加超时自动踢除功能
– ———————————–
– 2015.05.20
– ———————————–
* 修复多处BUG。如句柄数问题。
* 修复HTTP服务多处BUG
+ HTTP服务增加文件下载功能,支持超大文件传输。
– 将YxdHash、YxdStr精简出IOCP专用版本
+ 添加YxdIocp.dbk,可直接安装成控件
– ———————————–
– 2015.05.15
– ———————————–
* 简化使用接口。使用时只需要引用 iocp.pas 单元即可。
* 去除重复代码。
* 使用了一些自己编写的新类替换掉原来的类似模块,如 TIocpStream, TIocpHttpServer 等
* 用法更简单了
* D2007下调试通过,其它版本IDE暂未测试。

【感谢】
感谢DIOCP原作者天地弦,以及DIOCP官方群、QDAC官方群所有参与本项目测试、修改的朋友。

【注意事项】
您在使用中发现任何BUG、更改意见等请及时联系我们,不胜感激。
使用本源码时您需要自行承担一切后果,作者和项目组不为此承担任何法律责任。
您可以任意复制、更新本源码,但请注明来源,或在您的软件特定位置标注说明。

【联系我们】
yangyxd QQ: 2514718952
原作者天地弦QQ: 185511468
DIOCP 官方群: 320641073
QDAC 官方群: 250530692

【网站】
yangyxd blog: http://www.cnblogs.com/yangyxd
DIOCP 官方网站: http://www.diocp.org
QDAC 官方网站: http://www.qdac.cc

 

【下载】

百度网盘: http://pan.baidu.com/s/1kTKgHoj   提取密码: ma5w

 

[DIOCP视频]-DIOCPFileServer视频

本次视频简单讲解了DiocpFileServer

+ 客户端使用接口方式,通信方面可以方便的在DiocpBlockTcpClient和IdTcpClient组件之间切换。

+ 添加单独的EXE客户端(MyBean插件依然可以直接使用)

 

下载地址(Diocp共享视频下载)

http://pan.baidu.com/s/1bnrWjkN

 

 

diocp|mybean官方网站: 主域名: www.diocp.org (备用域名:diocp.wedelphi.com)

diocp|mybean官方论坛: http://delphi.net.cn

DIOCP-DIOCPv5的处理能力

今天和BB讨论了下DiocpV5的单连接处理能力。一直没有做过这方面的测试,稍微试了一下。

把开始的时候客户端Sleep(10),为了测试处理能力,把Sleep(10)去掉了,20秒(实际应该算17秒,点开就开始计时了),可以处理75W的请求而且进行了投递回去。已经足够实际应用了。

和qsl讨论过,一般每秒可以处理200-300个请求,实际的应用可以达到 1W的并发处理。

直接上个图,留个痕迹

 

 

diocp|mybean官方网站: 主域名: www.diocp.org (备用域名:diocp.wedelphi.com)

diocp|mybean官方论坛: http://delphi.net.cn

DIOCP 运作核心探密

原文连接:

http://blog.qdac.cc/?p=2362

 

来自网友天地弦的DIOCP早已经广为人知了,有很多的同学都用上了它,甚至各种变异、修改版本也出了不少。我最近也在学习DIOCP,打算将它用于自己的服务端,今天让我们来一起探密它(DIOCP)的运作核心吧。

 

DIOCP作为对Windows的IOCP完成端口封装,拥有了很高的性能,经过对ECHO示例的测试,它能轻松应对几万连接和并发。网络通讯一般分为6大阶段:请求连接、接受连接、接收数据、处理数据、回复数据、断开连接,下面我就从这6大阶段入手,来看看DIOCP是如何实现的。

 

一、 请求连接

 

实际上这第一阶段由客户端发起,指定HOST和Port请求连接我们的DIOCP服务。

 

二、 接受连接

 

在第一阶段,客户端请求连接后,我们的DIOCP服务会收到一个连接请求,这时默认会接受连接。在iocp.Sockets中,可以看到我们的服务类TIocpCustomTcpServer,它继承自TIocpCustom,就是TIocpCustomTcpServer完成了这整个网络通讯的各种请求的管理。

 

TIocpCustomTcpServer是一个用户能直接使用的DIOCP服务端类,在TIocpCustomTcpServer被调用Open(或Start)方法后,它先是开启IOCP任务引擎IocpEngine,初始化监听Socket,绑定监听端口,开始监听并将Socket绑定到IOCP句柄。接下来它会初始化指定数量的请求接受对象,然后再调用TiocpAcceptExRequest.PostRequest(内部调用IocpAcceptEx),像望夫石一样的守候着监听端口,等着客户端的连接。有人可能会问了,任务引擎怎么知道任务是什么,让谁来处理?好吧,我们可以看看TiocpRequest,它内部有一个Foverlapped,在Create时,Foverlapped.iocpRequest被设定为Self, TiocpAcceptExRequest是继承自TiocpRequest的。在PostRequest方法中调用AcceptEx时,有一个参数就是@FOverlapped,在任务引擎中GetQueuedCompletionStatus函数会返回Overlapped,这下明白了吧。

 

监听Socket:用于监听客户连接请求,开启指定的端口进入侦听状态,并调用任务引擎中的IocpCore对象Bind自己的Socket句柄到任务引擎的IOCP句柄(实际就是调用CreateIoCompletionPort函数来实现),这样监听Socket就可以在接收到IO请求时,由内核将请求加入IOCP任务队列,在IOCP引擎的工作线程中就可以通过GetQueuedCompletionStatus函数来直接取到任务进行处理了。请求响应、分配工作线程都是由任务引擎完成的。

 

在监听Socket收到连接请求时,会对Request进行必要的初始化(如状态设置、记录工作开始时间等),然后调用Request的FonResponse或HandleResponse。这里会优先调用FonResponse,目的是如果有指定外部的响应函数,就完全由外部接管,这样的封装增加了整体的灵活性。

 

在TIocpAcceptExRequest.HandleResponse中,会调用getPeerInfo函数获取远程客户端的IP地址和当前连接通讯端口,再产生一个OnAcceptedEx事件。接着调用Owner(TiocpCustomTcpServer)的DoAcceptExResponse方法,这时如果设置了OnContextAccept事件,则会产生此事件,你可以在这个事件中确定是否接受连接,默认会接受连接。接受连接后,根据KeepAlive开关判断是否设置TCP心跳。再调用IocpCore.Bind将当前连接的SocketHandle绑定到IOCP端口,如果成功会调用Context的DoConnected方法,在DoConnected里面会为当前连接分配一个标识句柄(实际上是一个计数器),设置Active状态为True,添加到在线列表,然后产生OnContextConnected事件,并调用OnConnected方法(你可以在子类中在这个地方做额外的处理),Context将状态设置为连接成功状态,并请求接收数据。如果在建立连接的过程中发生了错误,会关闭当前连接,产生OnContextError事件。

 

另外,在TiocpCustomTcpServer中内设了一个连接请求管理器IocpAcceptorMgr,它内部有一个TIocpAcceptExRequest对象池,目的是为了提升性能。IocpAcceptorMgr还能控制并发的最大请求数,超过上限时不再立即接受连接,而是等待对像池有空闲的对象时才返回。这里实际上就是一个排队效果了。

 

三、 接收数据

 

在第二阶段,连接成功后会马上调用当前连接的PostWSARecvRequest方法,请求接受数据。在TiocpCustomContext中,本身会初始化一个TIocpRecvRequest对象,它的作用就是产生一个数据接收请求,并在收到请求时,在HandleResponse方法中进行初步处理。

 

先来看看PostWSARecvRequest,它的实现很简单,就是直接去调用TiocpRecvRequest. PostRequest。 PostRequest函数会调用系统WSARecv函数产生一个接收数据的请求。这个请求当然也是异步的。由于TiocpRecvRequest也是继承于TiocpRequest,所以在调用WSARecv时也通过Foverlapped参数将自己与一次IO事件绑定了,在任务引擎中接收到数据时,会自动进入TiocpRecvRequest的HandleResponse方法。如果PostRequest失败,会触发OnContextError事件。

 

在TiocpRecvRequest. HandleResponse中,如果前面没有出错,会调用DoReceiveData,触发Context.OnRecvBuffer虚方法和IocpTcpServer的OnDataReceived事件,在这两个地方,用户可以对数据进行处理。紧接着,HandleResponse函数会再次产生一个接收请求,用于接收新的数据。(必须的哦,比如1G的文件,显然不能一个包就发送完^_^)

 

四、 处理数据

 

我们在使用TiocpCustomTcpServer时,可以通过注册一个被重载OnRecvBuffer的Context类来处理数据,也可以在OnDataReceived事件中处理。整体来说这个封装还是很自由的。

 

五、 回复数据

 

在处理数据时,我们经常需要回复一些东西给客户端。以使用OnDataReceived处理数据为例,我们先看看这个事件的声明:

 

 

在事件中,有当前接收的数据缓冲区地址、长度,还有一个Context。我们要回复数据或是查看远程客户端的IP端口等信息,就需要用到它。使用Context.Send()函数就可以发送我们的数据了。Send函数有几个重载版本,其中有一个里面包含BufReleaseType参数的,是指定正要被发送的数据缓冲区释放方式,默认dtNone,即不管它。如果使用dtFreeMem和dtDispose,则分别是调用FreeMem或Dispose来自动释放缓冲区内存。

 

Send函数同样也是异步的,会立即返回。在内部实际上是产生一个PostWSASendRequest请求。当然你也可以直接调用PostWSASendRequest请求,Send只是一个简化使用的封装。在PostWSASendRequest函数中,首先通过调用Owner(TIocpTcpServer).GetSendRequest函数从FsendRequestPool池中获取一个请求对象,将数据与这个请求对象绑定,即调用SendRequest. SetBuffer函数来初始化。在初始化完成后,将这个请求加入待发送队列中,成功后立即调用CheckNextSendRequest函数一次。

 

为何要调用CheckNextSendRequest? 其实CheckNextSendRequest是一个触发函数,它会从队列中Pop一个发送请求,成功后再调用TiocpSendRequest. ExecuteSend函数,在ExecuteSend里面会再次判断要发送的数据长度是否为0,然后再通过内部的InnerPostRequest来真正产生一个发送IO请求。这里面是通过WSASend来产生IO请求的,由于TiocpSendRequest也是基于TiocpRequest,所以WSASend时使用的Foverlapped参数就将对象与本次IO事件绑定了。在系统内核发送完数据或出错时,任务引擎会自动调用这个请求的HandleResponse方法。

 

在TIocpSendRequest.HandleResponse中,如果数据发送失败会产生OnContextError事件,成功则调用Context的虚方法DoSendRequestCompleted。然后立即调用Conetext. PostNextSendRequest方法,处理队列中的下一个发送请求。从这里可以看出,我们在Send之后调用CheckNextSendRequest时,可能并不是当前投递的待发送请求被响应。我们Send的请求可能会在前面的请求HandleResponse后才真正发送。

 

在Send数据时,我们用到了队列,其目的一是保证数据的发送顺序,二是能通过设置队列的大小来增强系统的稳定性,三是我们随时能观测到服务的状态。另外我们还使用了发送请求对象池,用来提升性能。

 

六、断开连接

 

IOCP服务在与客户建立连接后,内部只在发生错误或系统退出的时候才主动断开连接。平常时候默认由客户端来断开。在处理数据时,我们也可以直接调用Context. Disconnect来断开当前连接。在调用Disconnect时,会关闭当前连接的Socket,产生OnContextDisconnected事件,调用Context. OnDisconnected虚方法,并从在线列表中删除这个连接。在删除时Context会被还回到ContextPool中。

 

我们可以在OnContextDisconnected事件,或重载的Context的OnDisconnected方法对断开连接作额外的处理。

 

需要注意的是,前面的接受连接、接收数据、回发数据等都是异步的,只有断开连接是立即的。

 

七、结束语

 

至此,本文已经差不多结束了。在上面我们分析了DIOCP整个网络通讯的运作流程和基本使用方法。通过这些分析,你会发现,DIOCP到现在的V5阶段,整体流程已经很合理高效了,至少我暂时没发现明显的沉余。至于优化我想的是,在Send拷贝时,可以将GetMem换成环形内存,降低内存碎片的产生。

 

另外,本文讲的DIOCP为自己修改后的版本,已经将原来diocp-v5中的diocp.sockets.pas和diocp.tcp.server、diocp.tcp.client合并,部分类名有些细小的改变。将在线列表、HashTable换成了我自己的TYXDHashMapLinkTable,它是一个将HashTable和双向链表综合起来的怪物,个人感觉还是比较好用的。

 

本文只是我个人的一些初步理解,必竞才学习Diocp三天(三天打鱼两天晒网 :( ),很可能存在一些错误,欢迎大家指正。

 

最后感谢弦弦哥,能将这么好的东西奉献给大家,真是辛苦了。

有兴趣了解更多DIOCP的同学还可以直接访问它的官方网站: diocp.wedelphi.com

也可以直接加入QQ群: 320641073

【被C折腾系列】用C调DIOCP编码客户端通信

前几天有个朋友,说他们公司做手游,服务端用的DIOCP3里面做文件服务器,客户端用cocos-x,在调试与diocp通信时老是失败!

 

于是,我下载了一个Codeblocks经过几个小时的折腾,终于折腾出来了,把其中的一些心酸记录下,以便以后查阅。

 

1.windows下面使用socket的一些函数时,需要设置工程的选项或者工具的编译选项

[project –> build options]->[linker settings]或者[setting]->[complier]->Linker settings]中添加

F:\Tools\C\CodeBlocks\MinGW\lib\*.a 到 [link libraries]中

要不然会出现编译错误

undefined reference to `WSAStartup@8′

 

2.关于c中的extern和static

  百度上面有比较多的解释,

  extern 表示修饰的函数或者变量是从其他文件中引用。(extern告诉编译器这个变量或者函数在其他文件里已经被定义了)

  static 表示修饰的函数或者变量只能在本文件中使用。static还可以修饰局部变量,表示局部变量在退出函数后,该变量不可用,但是再次进入到函数时,该变量会保存之前的值。

 

下面为调试的截图(该代码放在目录diocp-v5\samples\StreamCoderDEMO\c_codeblocks编写)

上面截图说明与服务器进行了一次通信,服务器接收特定的编码格式(Flag + CheckValue + datalen + data)

发送过去后,服务端会进行编码原样返回。

 

总结:

    接触一门其他语言,需要多去写代码,只有在写的过程当中才会去碰到问题,解决问题,积累被坑的经验。思路和想法有了,剩下的就是多被坑点就好了

【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自己进行心跳处理。