利用DELPHI开发完成例程(1)
发布日期:2021-09-04 21:35:41
浏览次数:2
分类:技术文章
本文共 3823 字,大约阅读时间需要 12 分钟。
对于网络开发者来说,完成例程应该是一个不陌生的概念(什么?你不知道?去看看书吧)。
我在网上看了一下,发现完整实现完成例程的代码很少。前些日子由于工作不是很忙就自己写了一个,今天将如何实现公布出来,希望对大家有个帮助。由于水平有限,代码中难免会有不对的地方,希望各位看客能不吝指出。
言归正传。完成例程在其性能上仅次于IOCP 。通过我的测试,觉得一般支持1000-2000 的客户端应该没有什么问题。而且实现起来比IOCP要简单很多。
首先介绍一下我使用的IODATA 结构:
PIOData = ^TIOData;
TIOData = record
Overlapped: OVERLAPPED;
DataBuf: TWSABUF;
Socket:TSocket; // 套接字
BufferLen:Integer; // 数据长度
Buffer:array[0..DATA_BUFSIZE-1] of char;// 数据信息,包括数据头信息
FNetClass:Pointer; // 类的指针
end;
其中需要说明的是FNetClass:Pointer ,由于完成例程主要使用的是WSARECV 和WSASEND 函数的最后一个参数,即接收和发送完成以后使用的回调函数。而在帮助中这两个的回调函数的定义为:
void CALLBACK CompletionROUTINE(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
);
可以看出当我们对相应的数据包处理的时候如果没有指定的类,那么处理起来将比较麻烦。所以我在定义这个数据结构的时候,将类指针直接带来进去。
下来是启动网络监听。
FListensc := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);
if FListensc = SOCKET_ERROR then
begin
closesocket(FListensc);
WSACleanup();
if Assigned(OnError) then
begin
OnError(GetLastError);
end;
Exit;
end;
Fsto.sin_family := AF_INET;
Fsto.sin_port := htons(FPort);
Fsto.sin_addr.s_addr := htonl(INADDR_ANY);
if bind(FListensc, Fsto, sizeof(Fsto)) = SOCKET_ERROR then
begin
closesocket(FListensc);
if Assigned(OnError) then
begin
OnError(GetLastError);
end;
Exit;
end;
listen(FListensc, 200);
FListenThreadID:=CreateThread(nil, 0, @AcceptThread, Self,0,ThreadID);
这个代码就不用解释了,开发过网络的朋友都知道什么意思。
看看监听线程的处理代码。
var
FComletionRoutine:TCompletionRoutine;
p_IOData:PIOData;
Acceptsc:TSocket;
FSendHash:PSendHash;
begin
FComletionRoutine:=TCompletionRoutine(m_NetServer);
while True do
begin
Acceptsc:= WSAAccept(FComletionRoutine.FListensc, nil, nil, nil, 0);
if Acceptsc<>INVALID_SOCKET then
begin
if Assigned(FComletionRoutine.OnConect) then
begin
FComletionRoutine.OnConect(Acceptsc,'');
end;
p_IOData:=FComletionRoutine.RequestsMem;
p_IOData.FNetClass:=FComletionRoutine;
p_IOData.Socket:=Acceptsc;
FSendHash:=FComletionRoutine.FSendHashCtrl.CreateSendHash(p_IOData.Socket);
FComletionRoutine.PostRecv(p_IOData);
end
else
begin
break;
end;
end;
其中p_IOData:=FComletionRoutine.RequestsMem 是我写的一个函数,其作用是在一个内存池中为p_IOData 申请一块空间。
FSendHash:=FComletionRoutine.FSendHashCtrl.CreateSendHash(p_IOData.Socket) 的作用是将这个申请后的空间放入一个HASH 表中。
然后投递一个接收请求。
来看看这个投递接收请求是如何实现的。
Result:=true;
Flags := 0;
ZeroMemory(@(PerIoData.Overlapped), sizeof(OVERLAPPED));
PerIoData.DataBuf.len := DATA_BUFSIZE;
ZeroMemory(@PerIoData.Buffer, sizeof(@PerIoData.Buffer));
PerIoData.DataBuf.buf := @PerIoData.Buffer;
if (WSARecv(PerIoData.Socket, @(PerIoData.DataBuf), 1, @RecvBytes, @Flags, @(PerIoData.Overlapped), @RecvWorkerThread) = SOCKET_ERROR) then
begin
if (WSAGetLastError() <> ERROR_IO_PENDING) then
begin
Result:=false;
end;
end;
在WSARecv 函数的最后一个参数指定了当接收数据完成以后,我们需要处理的回调函数。
而在这个回调函数中处理的方式是这样的。
FComletionRoutine:=TCompletionRoutine(IoData.FNetClass);
EnterCriticalSection(FComletionRoutine.FIOCPInter);
try
if (Error<>0) and (BytesTransferred=0) then
begin
if Assigned(FComletionRoutine.OnShutDown) then
begin
FComletionRoutine.OnShutDown(IoData.Socket);
end;
// 删除发送缓存列表
FComletionRoutine.FSendHashCtrl.DelSort(IoData.Socket);
closesocket(IoData.Socket);
Exit;
end;
IoData.BufferLen:=BytesTransferred;
FSendHash:=FComletionRoutine.FSendHashCtrl.SelSort(IoData.Socket);
FComletionRoutine.FSendHashCtrl.DealData(FSendHash,IoData);
// 这里进行粘包处理,并将处理后的整包数据发送给相应的代码。(关于如何处理粘包,我这次就不写出来了。不过建议大家不要使用我前面BLOG 中写的粘包处理方法,那种方法处理起来效率比较低。但是那种思路可以借鉴,就是在发送的数据包前带入本次发送的数据长度)。
// 再次投递
if not FComletionRoutine.PostRecv(IoData) then
begin
closesocket(IoData.Socket);
Exit;
end;
finally
LeaveCriticalSection(FComletionRoutine.FIOCPInter);
end;
首先就是从IO 数据中得到处理的类,这为以后我们操作HASH 、发送队列将会有很大帮助。
然后判断是否接收正常,对于接收不正常的,我们将关闭这个套接字。
对于接收数据正常的,我们从HASH 表中得到它上次需要处理剩余的数据并和这次的数据结合进行处理。OK, 这样我们就完成了对接收数据的回调函数的实现。下次我将写出如何实现发送的部分。
本文转自狗窝博客51CTO博客,原文链接http://blog.51cto.com/fxh7622/91909如需转载请自行联系原作者
fxh7622
转载地址:https://blog.csdn.net/weixin_34223655/article/details/90076552 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
感谢大佬
[***.8.128.20]2024年03月30日 01时39分55秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
重拾《 两周自制脚本语言 》- 支持中文标识符
2021-06-29
Java实现文本编辑时基于拼音输入的补全原型
2021-06-29
从立创EDA,Gratipay看中文编程开发环境和推广运营的一个趋势
2021-06-29
中文代码之Django官方入门:建立模型
2021-06-29
Python实现推流直播
2021-06-29
你不得不了解的卷积神经网络发展史
2021-06-29
你不得不了解的机器学习知识
2021-06-29
你不得不了解的深度学习知识(一)
2021-06-29
你不得不了解的深度学习知识(二)
2021-06-29
AI算法之Encoder-Decoder 和 Seq2Seq
2021-06-29
AI算法之Attention机制
2021-06-29
人体口罩佩戴检测实战
2021-06-29
[YoLoV3目标检测实战] keras+yolov3训练自身口罩检测数据集
2021-06-29
[实战]200类鸟类细粒度图像分类
2021-06-29
【实战】英文垃圾短信分类
2019-04-26
FFmpeg初遇见_基本命令
2019-04-26
Urbansound8k声音分类深度学习实战
2019-04-26
pytorch版本下的yolov3训练实现火焰检测
2019-04-26