写在前面的话
这篇文章的内容是关于 DIOCP-v5 项目使用的一个入门文章,至于为什么更新这个的入门,最近在鼓捣传奇引擎的项目,发现项目的网络组件分这么几种情况,JSocket、DIOCP-V5、自己封装的 IOCP
据说 JSocket 实现的是轮询模型,其支持并发的次数很低,代码我没看,因为觉得没必要。如果要我选的话我肯定不会选择这个模型,而是在 Windows 系统上最高效的模型 IOCP(相当于 Linux 的 EPOLL)
在很早之前我甚至不知道服务器有 Windows 版本,一直到接触到网络安全(我的工作经历中曾经有一段时间是讲网络安全),我才知道 Windows 也有服务器版本,而传奇引擎很不巧的是部署在 Windows 上
其次是 JSocket,先不说低效的问题,哪怕是现在的高版本 Delphi 的 IDE 都很难找到一个可以适配的版本。所以继上一篇 Delphi-Corss-Socket 之后又出现了这篇文章
项目地址:
https://github.com/ymofen/diocp-v5
我不知道现在这个时间节点还会有多少人会看这篇文章,毕竟现在 AI 大行其道,什么东西不会了直接问 AI 基本上都会有答案
DIOCP-V5 的目录结构
我觉得还是有必要说一下目录结构的,理由嘛,网络上不知道有没有类似的入门教程的文章(至少我没找到)。
E:\MIR\MIR\DIOCP-V5
├─doc 这个目录很明显存放的是说明档,一般情况下开源项目都会有这个
├─samples 这里其实存放的就是官方的示例代码,而我的入门其实也是从这里开始的
└─source 这里存放的就是 DIOCP 的源码,需要我们在项目中引入的目录
这里需要特别说明一下,DIOCP 貌似不挑 Delphi IDE 的版本,因为我在 Delphi2007 和 Delphi XE12.2 上分别可以使用
如果说有什么缺点的话,就是和 Delphi-Corss-Socket 相比的话代码可能没那么明确。这个观点是在直播的适合有一个网友说的,其实对我来说都差不多,反正都不会
DIOCP-V5 的快速上手
和之前的项目一样,都需要一个可以快速上手的项目,但是很可惜官方的案例中好像没有这种项目
uses
diocp_tcp_server, utils_safeLogger, utils_BufferPool, utils_fileWriter, utils_async; //首先是包的引入
//其次就是指定服务的事件处理函数,具体的函数实现我就不写了,只是需要注意的是这些函数必须放在类中
procedure TForm2.FormCreate(Sender: TObject);
begin
FTcpServer := TDiocpTcpServer.Create(Self);
FTcpServer.Port :=10086;
FTcpServer.DefaultListenAddress := '127.0.0.1';
FTcpServer.Name := 'iocpSVR';
FTcpServer.OnDataReceived := OnRecvBuffer;
FTcpServer.OnContextAccept := OnAccept;
FTcpServer.OnContextConnected := OnContextConnected ;
FTcpServer.OnSendBufferCompleted := OnSendBufferCompleted;
FTcpServer.OnContextDisconnected := OnDisconnected;
end;
//重点说一下这个函数,里面有两个参数很重要,一个是指针,另一个是长度
procedure TForm2.OnRecvBuffer(pvClientContext: TIocpClientContext; buf: Pointer; len: cardinal; errCode: Integer);
var
receivedMessage: AnsiString;
begin
// 检查是否没有错误
if errCode = 0thenbegin
// 将接收到的字节流转换为字符串
SetLength(receivedMessage, len); // 设置字符串的长度为接收到的数据长度
Move(buf^, receivedMessage[1], len); // 从缓冲区复制数据到字符串中
// 显示接收到的消息
Memo1.Lines.add('收到的消息:: ' + receivedMessage);
//提交一个发送的请求
pvClientContext.PostWSASendRequest(buf,len);
endelsebegin
// 处理错误:可以根据错误码进行相应的处理
ShowMessage('Error occurred while receiving data. Error code: ' + IntToStr(errCode));
end;
end;
这里有一个问题,就是为什么接收的是指针,而不是像 JSocket 一样发送和接收可以直接是字符串。我猜的原因可能是发送的数据除了普通文本之外还有二进制数据
下面是客户端程序
uses
diocp_tcp_client,utils_safeLogger, ComCtrls, diocp_sockets, ExtCtrls, utils_async,
diocp_coder_tcpClient, utils_BufferPool, utils_fileWriter, diocp_tcp_blockClient, Registry,
utils_strings, diocp_task;
constructor TRunGate.Create(Memo: TMemo);
begin
try
//游戏客户端配置
ClientSocket := TDiocpTcpClient.Create(nil);
ClientSocket.OnContextConnected := ClientSocketConnect; //建立连接
ClientSocket.OnContextDisconnected := ClientSocketDisconnect; //关闭连接
ClientSocket.OnReceivedBuffer := ClientSocketRead;
//M2 连接配置
DiocpContext := TDiocpExRemoteContext(ClientSocket.Add);
DiocpContext.Host := '127.0.0.1';
DiocpContext.Port := 10086;
ClientSocket.open;
ClientSocket.DisableAutoConnect := True;
DiocpContext.Connect;
except
on E: Exception do
//AddMainLogMsg(E.Message, 1);
end;
end;
//这里需要说明一下,当时是为了一个传世的单子,这里是测试代码,开启了一个线程,但是即便是普通的按钮点击事件也是一样的
procedure TWorkThread.Execute;
var
Content: Pointer;
SendMsg:string;
Index:Integer;
begin
while True do
begin
SendMsg:='我是祖国的花朵'+IntToStr(Index);
Self.RunGate.DiocpContext.PostWSASendRequest(PChar(SendMsg),Length(SendMsg));
Sleep(1000);
Inc(Index);
end;
end;
end;
写在最后的话
我曾经好奇过为什么会有 JSocket 这种东西,后来想想其实没那么难理解,怎么说呢,我觉得是两个原因
• 传奇引擎的架构问题,它存在网关,而网关的一个很重要的功能就是缓存消息,我看到的代码都是由一个 TList 作为消息的临时存放,然后再慢慢的把消息从列表中转发给 M2
• 第二个其实应该是和时代有关系,传奇盛行的年代可能 IOCP 还没出现
最后,其实无论选择什么网络模型,套用 GM 的话,能跑起来的引擎就是好引擎。我这里说的只是针对网络的学习。
还有一件事差点忘了说,第二季的视频不是停更了,是因为我遇到了问题,第 1 个是屏幕截屏的算法,第 2 个是屏幕截屏之后怎么压缩发送数据。如果哪位同道知道的话还望不吝赐教
领取专属 10元无门槛券
私享最新 技术干货