DELPHI高性能大容量SOCKET并发Word格式.docx
- 文档编号:20941329
- 上传时间:2023-01-26
- 格式:DOCX
- 页数:60
- 大小:121.39KB
DELPHI高性能大容量SOCKET并发Word格式.docx
《DELPHI高性能大容量SOCKET并发Word格式.docx》由会员分享,可在线阅读,更多相关《DELPHI高性能大容量SOCKET并发Word格式.docx(60页珍藏版)》请在冰豆网上搜索。
DWORD):
stdcall;
NumberOfConcurrentThreads参数定义了在一个完成端口上,同时允许执行的线程数量。
将NumberOfConcurrentThreads设为0表示每个处理器各自负责一个线程的运行,为完成端口提供服务,避免过于频繁的线程场景切换。
因此可以使用下列语句来创建一个完成端口FIocpHandle:
=CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
执行线程个数
创建完成端口后,就可以将套接字句柄与对象关联在一起,这时就需要创建工作者线程,以便在完成端口收到数据后,为完成端口提供处理数据线程。
到底创建多少个线程为完成端口服务,这个是完成端口最为复杂的一方面,创建多了线程会造成频繁的线程场景切换;
创建少了线程如果某一个处理非常耗时,如连接数据库、读写文件,又会造成完成端口拥塞,因此这个参数需要提供设置,并根据最终的应用场景反复测试得出一个结果。
一般的经验值是设置为CPU的个数*2+4;
IOCP完成端口一般使用步骤
1、创建一个完成端口;
2、判断系统内安装了多少个处理器;
3、创建工作者线程;
4、创建一个SOCKET套接字开始监听;
5、使用Accept接收连接;
6、调用CreateIoCompletionPort将连接和完成端口绑定在一起;
7、投递接收数据请求
8、工作者线程调用GetQueuedCompletionStatus获取事件通知,处理数据;
IOCP控件核心代码
第1步到第4步实现代码:
[delphi]
viewplaincopy
1.procedure
TIocpServer.Open;
2.var
3.
WsaData:
TWsaData;
4.
iNumberOfProcessors,
i,
iWorkThreadCount:
Integer;
5.
WorkThread:
TWorkThread;
6.
Addr:
TSockAddr;
7.begin
8.
if
WSAStartup($0202,
WsaData)
<
>
0
then
//初始化SOCKET
9.
raise
ESocketError.Create(GetLastWsaErrorStr);
10.
FIocpHandle
:
=
CreateIoCompletionPort(INVALID_HANDLE_VALUE,
0,
0);
//创建一个完成端口
11.
12.
ESocketError.Create(GetLastErrorStr);
13.
FSocket
WSASocket(PF_INET,
SOCK_STREAM,
nil,
WSA_FLAG_OVERLAPPED);
//创建一个SOCKET句柄
14.
INVALID_SOCKET
15.
16.
FillChar(Addr,
SizeOf(Addr),
17.
Addr.sin_family
AF_INET;
18.
Addr.sin_port
htons(FPort);
19.
Addr.sin_addr.S_addr
htonl(INADDR_ANY);
//在任何地址上监听,如果有多块网卡,会每块都监听,也可以指定只监听某一个IP地址
20.
bind(FSocket,
@Addr,
SizeOf(Addr))
//把SOCKET句柄绑定端口
21.
22.
listen(FSocket,
MaxInt)
23.
24.
iNumberOfProcessors
GetCPUCount;
//获取CPU个数
25.
iWorkThreadCount
*
2
+
4;
//由于服务器处理可能比较费时间,因此线程设为CPU*2+4
26.
FMinWorkThrCount
//限定最大工作者线程和最小工作者线程
27.
FMinWorkThrCount;
28.
FMaxWorkThrCount
29.
FMaxWorkThrCount;
30.
for
i
to
-
1
do
//创建工作者线程
31.
begin
32.
WorkThread
TWorkThread.Create(Self,
True);
33.
FWorkThreads.Add(WorkThread);
34.
WorkThread.Resume;
35.
end;
36.
FAcceptThreadPool.Active
True;
//启动监听线程池
37.
FAcceptThread
TAcceptThread.Create(Self,
//启动监听线程
38.
FAcceptThread.Resume;
39.end;
第5步和第6步实现代码:
TIocpServer.AcceptClient;
ClientSocket:
TSocket;
4.begin
ClientSocket
WSAAccept(FSocket,
//接收连接
7.
not
FActive
closesocket(ClientSocket);
Exit;
FAcceptThreadPool.PostSocket(ClientSocket);
//这里使用线程池主要作用是为了判断发送的第一个字节身份标识,用来是判断协议类型
15.end;
FAcceptThreadPool是一个用完成端口实现的线程池TIocpThreadPool,主要是有TCheckThread来判断完成端口的返回,并检测是否6S内有发送标志位上来,主要实现过程是响应OnConnect事件,并在OnConnect事件中判断是否允许连接。
TIocpServer.CheckClient(const
ASocket:
TSocket);
SocketHandle:
TSocketHandle;
iIndex:
PClientSocket;
6.begin
SocketHandle
nil;
DoConnect(ASocket,
SocketHandle)
//如果不允许连接,则退出
closesocket(ASocket);
FSocketHandles.Lock;
//加到列表中
try
iIndex
FSocketHandles.Add(SocketHandle);
FSocketHandles.Items[iIndex];
finally
FSocketHandles.UnLock;
CreateIoCompletionPort(ASocket,
FIOCPHandle,
DWORD(ClientSocket),
0)
//将连接和完成端口绑定在一起
DoError('
CreateIoCompletionPort'
GetLastWsaErrorStr);
//如果投递到列表中失败,则删除
FSocketHandles.Delete(iIndex);
end
else
SocketHandle.PreRecv(nil);
//投递接收请求
34.end;
TDMDispatchCenter.IcpSvrConnect(const
Cardinal;
2.
var
AAllowConnect:
Boolean;
TSocketHandle);
3.var
BaseSocket:
TBaseSocket;
chFlag:
Char;
function
GetSocket(const
AEnable:
BaseSocketClass:
TBaseSocketClass):
AEnable
Result
BaseSocketClass.Create(IcpSvr,
ASocket)
14.begin
(GIniOptions.MaxSocketCount
and
(IcpSvr.SocketHandles.Count
GIniOptions.MaxSocketCount)
AAllowConnect
False;
IcpSvr.ReadChar(ASocket,
chFlag,
6*1000)
//必须在6S内收到标志
case
TSocketFlag(Byte(chFlag))
of
sfSQL:
BaseSocket
GetSocket(GIniOptions.SQLProtocol,
TSQLSocket);
sfUpload:
GetSocket(GIniOptions.UploadProtocol,
TUploadSocket);
sfDownload:
GetSocket(GIniOptions.DownloadProtocol,
TDownloadSocket);
sfControl:
GetSocket(GIniOptions.ControlProtocol,
TControlSocket);
sfLog:
GetSocket(GIniOptions.LogProtocol,
TLogSocket);
;
nil
BaseSocket;
WriteLogMsg(ltDebug,
Format('
Client
Connect,
Local
Address:
%s:
%d;
Remote
%d'
[SocketHandle.LocalAddress,
SocketHandle.LocalPort,
SocketHandle.RemoteAddress,
SocketHandle.RemotePort]));
Count:
[IcpSvr.SocketHandles.Count
1]));
39.
40.
41.
42.
43.
44.end;
其中ReadChar函数是用于判断指定时间是否有数据上来,函数实现过程用Select函数检测:
1.function
TIocpServer.ReadChar(const
AChar:
const
ATimeOutMS:
Integer):
iRead:
CheckTimeOut(ASocket,
ATimeOutMS);
iRead
recv(ASocket,
AChar,
1,
1;
11.end;
13.function
TIocpServer.CheckTimeOut(const
15.var
tmTo:
TTimeVal;
FDRead:
TFDSet;
18.begin
FillChar(FDRead,
SizeOf(FDRead),
FDRead.fd_count
FDRead.fd_array[0]
ASocket;
tmTo.tv_sec
ATimeOutMS
div
1000;
tmTo.tv_usec
(ATimeOutMS
mod
1000)
Select(0,
@FDRead,
@tmTO)
25.end;
第7步实现代码
接收连接之后要投递接收数据请求,实现代码:
TSocketHandle.PreRecv(AIocpRecord:
PIocpRecord);
iFlags,
iTransfer:
iErrCode:
5.begin
Assigned(AIocpRecord)
New(AIocpRecord);
AIocpRecord.WsaBuf.buf
@FIocpRecvBuf;
AIocpRecord.WsaBuf.len
MAX_IOCPBUFSIZE;
FIocpRecv
AIocpRecord;
AIocpRecord.Overlapped.Internal
0;
AIocpRecord.Overlapped.InternalHigh
AIocpRecord.Overlapped.Offset
AIocpRecord.Overlapped.OffsetHigh
AIocpRecord.Overlapped.hEvent
//AIocpRecord.WsaBuf.buf
//AIocpRecord.WsaBuf.len
AIocpRecord.IocpOperate
ioRead;
iFlags
WSARecv(FSocket,
@AIocpRecord.WsaBuf,
iTransfer,
@AIocpRecord.Overlapped,
nil)
SOCKET_ERROR
iErrCode
WSAGetLastError;
WSAECONNRESET
//客户端被关闭
FConnected
ERROR_IO_PENDING
//不抛出异常,触发异常事件
FIocpServer.DoError('
WSARecv'
ProcessNetError(iErrCode);
第8步实现代码:
TIocpServer.WorkClient:
IocpRecord:
PIocpRecord;
iWorkCount:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- DELPHI 性能 容量 SOCKET 并发