DELPHI高性能大容量SOCKET并發(fā)_第1頁(yè)
DELPHI高性能大容量SOCKET并發(fā)_第2頁(yè)
DELPHI高性能大容量SOCKET并發(fā)_第3頁(yè)
DELPHI高性能大容量SOCKET并發(fā)_第4頁(yè)
DELPHI高性能大容量SOCKET并發(fā)_第5頁(yè)
已閱讀5頁(yè),還剩45頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、DELPHI高性能大容量 SOCKET弁發(fā)(一):IOCP完成端口例子介紹例子主要包括IOCP控件封裝、服務(wù)端實(shí)現(xiàn)、傳輸協(xié)議和日志、控制、 SQL查詢(xún)、上傳、下載等協(xié)議實(shí) 現(xiàn),并包括一些初步的性能測(cè)試結(jié)果。服務(wù)端:界面截圖如下:提供服務(wù)和桌面方式運(yùn)行,桌面方式可直接打開(kāi)程序,方便日常調(diào)試,可以使用命令行注冊(cè)或卸載服務(wù), 在 CMD 中輸入 D:DEMOIOCPDemoBinIOCPDemoSvr.exe -install來(lái)注冊(cè)服務(wù),在 CMD 輸入D:DEMOIOCPDemoBinIOCPDemoSvr.exe -uninstall來(lái)卸載服務(wù)??蛻?hù)端:界面截圖如下:主要實(shí)現(xiàn)了服務(wù)端日志查看,服

2、務(wù)端協(xié)議類(lèi)表查看,SQL語(yǔ)句執(zhí)行協(xié)議,上傳、下載協(xié)議實(shí)現(xiàn),其中對(duì)上傳、下載實(shí)現(xiàn)了一個(gè)多線程同時(shí)傳,用于測(cè)試服務(wù)器并發(fā)性能。性能:支持超過(guò)2000個(gè)鏈接及以上同時(shí)上傳文件,不過(guò)每個(gè)連接上傳速度只有1到2K。支持超過(guò)2W個(gè)連接同時(shí)在線傳輸命令。單實(shí)例上傳下載測(cè)試結(jié)果:4ncDr1上檔餐呼瞋制這r或闔內(nèi)向4石而,卬+小 KB 1:?rq.o |* t. Kf.)叮 IftS J(KB 1jcXP ( KB四Wtt (KSJ5 .nHOKSft41535.Q7661.6454 9155J211,當(dāng)2719.&11041.917S7J397S2.6-T."4】m.的2244J77題勺1

3、429.4-1225.067iJI 輒 513ML»iiaui2H7J21河啦S166iii£.ta削25匕91253.625J.ZSJ27M1.4§1J9WWI45& B277D537W,17JLQ&4175M M71那姆44次&11U»第強(qiáng)B9T8 39184%7&41 31<u.nr|O75412923. 316757 0«2B5593司 116713512上網(wǎng)9圍2059.421134KlS24Z7.M10M17046.71】的4生上謠mg3.iC16KSL*IK?=T1口庫(kù)松總電株試H空q用麗向土

4、小: kb:iIVO 11B» i KB?$)14? )1 !?«»(KfV5 ( ke 而E (KB/5e3l«IKB1635.-I75SL5 74181F2C28*5,15B®9 47U5413如帖MWAIIS面££.a214七的舊1021制922316建箝目3m.M1嗨料9125937.256J治一城i6a.aa£41M也加24H.4S名&m17moi2111«4 J穌1 o75633421.3481.4151230332.a7ZB4M 57432.MMM20gg弼111段第49.N從測(cè)試結(jié)

5、果可以看出隨著發(fā)送包增大,速度變快。這里存在一個(gè)風(fēng)險(xiǎn),就是SOCKET傳輸失敗的次數(shù)也會(huì)增加。#(二):IOCP完成端口控件封裝IOCP完成端口介紹:完成端口模型是 Windows平臺(tái)下SOCKET端口模型最為復(fù)雜的一種I/O模型。如果一個(gè)應(yīng)用程序需要同時(shí)管理為數(shù)眾多的套接字,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,應(yīng)用程序的性能也可以線性提升,采用完成端口模型,往往可以達(dá)到最佳的系統(tǒng)性能。完成端口可以管理成千上萬(wàn)的連接,長(zhǎng)連接傳文件可以支持5000個(gè)以上,長(zhǎng)連接命令交互可以支持20000個(gè)以上。這么大并發(fā)的連接,更需要考慮的是應(yīng)用場(chǎng)景,按照100M的網(wǎng)卡傳輸速度12.5MB/S ,如果是5

6、000個(gè)傳文件連接,則每個(gè)連接能分到的速度2.56KB/S ;如果是20000個(gè)命令交互連接,則每個(gè)連接分到的吞吐量是655B/S ,這種速度的吞吐量對(duì)很多應(yīng)用是不滿(mǎn)足,這時(shí)就要考慮加大網(wǎng)卡的傳輸速度或?qū)崿F(xiàn)水平擴(kuò)展, 這個(gè)我們后續(xù)會(huì)介紹。完成端口是由系統(tǒng)內(nèi)核管理多個(gè)線程之間的切換,比外部實(shí)現(xiàn)線程池性能要高,CPU利用率上內(nèi)核和用戶(hù)態(tài)可以達(dá)到1:1,很多應(yīng)用線程池是無(wú)法達(dá)到的。因此同等連接數(shù)的情況下,完成端口要比INDY的TCPServer傳輸速度要快,吞吐量更高。要使用完成端口,主要是以下三個(gè)函數(shù)的使用:CreateIoCompletionPort 、GetQueuedCompletionSt

7、atus 、PostQueuedCompletionStatus 。CreateIoCompletionPort的功能是:1、創(chuàng)建一個(gè)完成端口對(duì)象;2、將一個(gè)句柄和完成端口關(guān)聯(lián)在一起;GetQueuedCompletionStatus是獲取完成端口狀態(tài),是阻塞式調(diào)用,在指定時(shí)間內(nèi)如果沒(méi)有事件通知,會(huì)一直等待;PostQueuedCompletionStatus用于向完成端口投遞一個(gè)完成事件通知。function CreateIoCompletionPort(FileHandle, ExistingCompletionPort: THandle; CompletionKey, NumberOfC

8、oncurrentThreads: DWORD): THandle; stdcall;NumberOfConcurrentThreads參數(shù)定義了在一個(gè)完成端口上,同時(shí)允許執(zhí)行的線程數(shù)量。將 NumberOfConcurrentThreads 設(shè)為0表示每個(gè)處理器各自負(fù)責(zé)一 個(gè)線程的運(yùn)行,為完成端口提供服務(wù),避免過(guò)于頻繁的線程場(chǎng)景切換。因此可以使用下列語(yǔ)句來(lái)創(chuàng)建一個(gè)完成 端口 FIocpHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);執(zhí)行線程個(gè)數(shù)創(chuàng)建完成端口后,就可以將套接字句柄與對(duì)象關(guān)聯(lián)在一起,這時(shí)就需要?jiǎng)?chuàng)建工作

9、者線程,以便在完成端口 收到數(shù)據(jù)后,為完成端口提供處理數(shù)據(jù)線程。到底創(chuàng)建多少個(gè)線程為完成端口服務(wù),這個(gè)是完成端口最為復(fù)雜 的一方面,創(chuàng)建多了線程會(huì)造成頻繁的線程場(chǎng)景切換;創(chuàng)建少了線程如果某一個(gè)處理非常耗時(shí),如連接數(shù)據(jù)庫(kù)、讀寫(xiě)文件,又會(huì)造成完成端口擁塞,因此這個(gè)參數(shù)需要提供設(shè)置,并根據(jù)最終的應(yīng)用場(chǎng)景反復(fù)測(cè)試得出一個(gè)結(jié) 果。一般的經(jīng)驗(yàn)值是設(shè)置為CPU的個(gè)數(shù)*2+4 ;IOCP完成端口一般使用步驟1、創(chuàng)建一個(gè)完成端口;2、判斷系統(tǒng)內(nèi)安裝了多少個(gè)處理器;3、創(chuàng)建工作者線程;4、創(chuàng)建一個(gè)SOCKET套接字開(kāi)始監(jiān)聽(tīng);5、使用Accept接收連接;6、調(diào)用CreateIoCompletionPort將連接和

10、完成端口綁定在一起;7、投遞接收數(shù)據(jù)請(qǐng)求8、工作者線程調(diào)用 GetQueuedCompletionStatus獲取事件通知,處理數(shù)據(jù);IOCP控件核心代碼第1步到第4步實(shí)現(xiàn)代碼:delphi view plaincopy1. procedure TIocpServer . Open;2. var3. WsaData: TWsaData;4. iNumberOfProcessors, i, iWorkThreadCount: Integer;5. WorkThread: TWorkThread;6. Addr: TSockAddr;7. begin8. if WSAStartup( $0202

11、, WsaData) <>0 then / 初始化 SOCKET9. raise ESocketError . Create(GetLastWsaErrorStr);10. FIocpHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE,0, 0, 0);創(chuàng)建一個(gè)完成端口11. if FIocpHandle =0 then12. raise ESocketError . Create(GetLastErrorStr);13. FSocket := WSASocket(PF_INET, SOCK_STREAM, 0, nil ,

12、0, WSA_FLAG_OVERLAPPED); 創(chuàng)建一個(gè) SOCKET 句柄14. if FSocket = INVALID_SOCKETthen15. raise ESocketError . Create(GetLastWsaErrorStr);16. FillChar(Addr, SizeOf(Addr),0);17. Addr . sin_family := AF_INET;18. Addr . sin_port := htons(FPort);19. Addr . sin_addr . S_addr := htonl(INADDR_ANY); /在任何地址上監(jiān)聽(tīng),如果有多塊網(wǎng)卡,會(huì)

13、每塊都監(jiān)聽(tīng),也可 以指定只監(jiān)聽(tīng)某一個(gè)IP地址20. if bind(FSocket, Addr, SizeOf(Addr) <>0 then 把 SOCKE詢(xún)柄綁定端口21. raise ESocketError . Create(GetLastWsaErrorStr);22. if listen(FSocket, MaxInt) <>0 then23. raise ESocketError . Create(GetLastWsaErrorStr);24. iNumberOfProcessors := GetCPUCount;/ 獲取 CPU個(gè)數(shù)25. iWorkThr

14、eadCount := iNumberOfProcessors *2 + 4; /由于服務(wù)器處理可能比較費(fèi)時(shí)間,因此線程設(shè)為CPU*2+426. if iWorkThreadCount < FMinWorkThrCountthen /限定最大工作者線程和最小工作者線程27. iWorkThreadCount := FMinWorkThrCount;28. if iWorkThreadCount > FMaxWorkThrCountthen29. iWorkThreadCount := FMaxWorkThrCount;30. for i :=0 to iWorkThreadCoun

15、t -1 do / 創(chuàng)建工作者線程31. begin32. WorkThread := TWorkThread . Create(Self, True);33. FWorkThreads . Add(WorkThread);34. WorkThread . Resume;35. end;36. FAcceptThreadPool . Active := True; / 啟動(dòng)監(jiān)聽(tīng)線程池37. FAcceptThread := TAcceptThread . Create(Self, True); / 啟動(dòng)監(jiān)聽(tīng)線程38. FAcceptThread . Resume;39. end;第5步和第6步

16、實(shí)現(xiàn)代碼:delphi view plaincopy1. procedure TIocpServer . AcceptClient;2. var3. ClientSocket: TSocket;4. begin5. ClientSocket := WSAAccept(FSocket,nil , nil , nil , 0); / 接收連接6. if ClientSocket <> INVALID_SOCKET then7. begin9. begin10. closesocket(ClientSocket);11. Exit;12. end;13. FAcceptThreadPoo

17、l . PostSocket(ClientSocket);/這里使用線程池主要作用是為了判斷發(fā)送的第一個(gè)字節(jié)身份標(biāo)識(shí),用來(lái)是判斷協(xié)議類(lèi)型14. end;15. end;FAcceptThreadPool 是一個(gè)用完成端口實(shí)現(xiàn)的線程池TIocpThreadPool ,主要是有TCheckThread 來(lái)判斷完成端口的返回,并檢測(cè)是否6s內(nèi)有發(fā)送標(biāo)志位上來(lái),主要實(shí)現(xiàn)過(guò)程是響應(yīng) OnConnect事件,并在OnConnect 事件中判斷是否允許連接。delphi view plaincopy1. procedure TIocpServer . CheckClient( const ASocket:

18、TSocket);2. var3. SocketHandle: TSocketHandle;4. iIndex: Integer;5. ClientSocket: PClientSocket;6. begin7. SocketHandle := nil ;8. if not DoConnect(ASocket, SocketHandle)then / 如果不允許連接,則退出9. begin10. closesocket(ASocket);11. Exit;12. end;13. FSocketHandles . Lock; / 加到列表中14. try15. iIndex := FSocket

19、Handles. Add(SocketHandle);16. ClientSocket := FSocketHandles. ItemsiIndex;17. finally18. FSocketHandles . UnLock;19. end;20. if CreateIoCompletionPort(ASocket, FIOCPHandle, DWORD(ClientSocket),0) = 0 then / 將連接和完成端口綁定在一起21. begin22. DoError( 'CreateIoCompletionPort' , GetLastWsaErrorStr);23

20、. FSocketHandles . Lock; /如果投遞到列表中失敗,則刪除24. try25. FSocketHandles. Delete(iIndex);26. finally27. FSocketHandles. UnLock;28. end;29. end30. else31. begin32. SocketHandle . PreRecv( nil ); / 投遞接收請(qǐng)求33. end;34. end;5delphi view cedure TDMDispatchCenter.IcpSvrConnect( const ASocket: Cardina

21、l;2.var AAllowConnect: Boolean;var SocketHandle: TSocketHandle);3.var4.BaseSocket: TBaseSocket;5.chFlag: Char;6.7.function GetSocket( constAEnable: Boolean; BaseSocketClass: TBaseSocketClass): TBaseSocket;8.begin9.if AEnable then10.Result := BaseSocketClass.Create(IcpSvr, ASocket)11.else12.Result :=

22、nil ;13.end;14.begin15.if (GIniOptions.MaxSocketCount >0) and (IcpSvr . SocketHandles . Count >= GIniOptions . MaxSocketCount)thend',4.35.tHandle9.40.beginAAllowConnect := False;Exit;end;if IcpSvr . ReadChar(ASocket, chFlag,6

23、*1000) then /必須在6s內(nèi)收到標(biāo)志begincase TSocketFlag(Byte(chFlag)ofsfSQL: BaseSocket := GetSocket(GIniOptionssfUpload: BaseSocket := GetSocket(GIniOptionssfDownload: BaseSocket := GetSocket(GIniOptionssfControl: BaseSocket := GetSocket(GIniOptionssfLog: BaseSocket := GetSocket(GIniOptionselseBaseSocket :=ni

24、l ;.SQLProtocol, TSQLSocket);.UploadProtocol, TUploadSocket);.DownloadProtocol, TDownloadSocket);.ControlProtocol, TControlSocket);.LogProtocol, TLogSocket);end;if BaseSocket <>nil thenbeginSocketHandle := BaseSocket;WriteLogMsg(ltDebug, Format(SocketHandle.RemotePort);'Client Connect, Loc

25、al Address: %s:%d; Remote Address: %s:%.LocalAddress, SocketHandleWriteLogMsg(ltDebug, Format(AAllowConnect := True;endelseAAllowConnect := False;'Client Count: %d'.LocalPort, SocketHandle.RemoteAddress, Socke,IcpSvr . SocketHandles . Count +1);41.end742. else43. AAllowConnect := False;44. e

26、nd;其中ReadChar函數(shù)是用于判斷指定時(shí)間是否有數(shù)據(jù)上來(lái),函數(shù)實(shí)現(xiàn)過(guò)程用Select函數(shù)檢測(cè):delphi view plaincopy 31. function TIocpServer . ReadChar( const ASocket: TSocket; var AChar: Char; const ATimeOutMS: Inte ger): Boolean;2. var3. iRead: Integer;4. begin5. Result := CheckTimeOut(ASocket, ATimeOutMS);6. if Resultthen7. begin8. iRead :

27、= recv(ASocket, AChar,1, 0);9. Result := iRead =1;10. end;11. end;12.13. function TIocpServer . CheckTimeOut( const ASocket: TSocket;14. const ATimeOutMS: Integer): Boolean;15. var16. tmTo: TTimeVal;17. FDRead: TFDSet;18. begin19. FillChar(FDRead, SizeOf(FDRead),0);20. FDRead . fd_count :=1;21. FDRe

28、ad . fd_array 0 := ASocket;22. tmTo . tv_sec := ATimeOutMS div 1000;23. tmTo . tv_usec := (ATimeOutMSmod 1000) *1000 ;24. Result := Select( 0, FDRead, nil , nil , tmTO) =1;25. end;第7步實(shí)現(xiàn)代碼接收連接之后要投遞接收數(shù)據(jù)請(qǐng)求,實(shí)現(xiàn)代碼:delphi view plaincopy1. procedure TSocketHandle . PreRecv(AIocpRecord: PIocpRecord);2. var3.

29、 iFlags, iTransfer: Cardinal;4. iErrCode: Integer;5. begin6. if not Assigned(AIocpRecord) then7. begin8. New(AIocpRecord);9. AIocpRecord. WsaBuf. buf := FIocpRecvBuf;10. AIocpRecord. WsaBuf. len := MAX_IOCPBUFSIZE;11. FIocpRecv := AIocpRecord;12. end;13. AIocpRecord . Overlapped . Internal :=0;14.AI

30、ocpRecord.Overlapped.InternalHigh :=015.AIocpRecord.Overlapped.Offset :=0;16.AIocpRecord.Overlapped.OffsetHigh :=0;17.AIocpRecord.Overlapped.hEvent :=0;18. AIocpRecord.WsaBuf.buf :=FIocpRecvBuf;19. /AIocpRecord.WsaBuf.len :=MAX_IOCPBUFSIZE;20. AIocpRecord . locpOperate:= ioRead;21. iFlags := 0;22. i

31、f WSARecv(FSocket, AIocpRecord . WsaBuf, 1, iTransfer, iFlags, AIocpRecord.Overlapped,23. nil ) = SOCKET_ERROR then24. begin25. iErrCode := WSAGetLastError;26. if iErrCode = WSAECONNRESET then / 客戶(hù)端被關(guān)閉27. FConnected := False;28. if iErrCode <> ERROR_IO_PENDINGthen / 不拋出異常,觸發(fā)異常事件29. begin30. FI

32、ocpServer . DoError( 'WSARecv' , GetLastWsaErrorStr);31. ProcessNetError(iErrCode);32. end;33. end;34. end;第8步實(shí)現(xiàn)代碼: delphi view plaincopy1. function TIocpServer . WorkClient: Boolean;2. var3. ClientSocket: PClientSocket;4. IocpRecord: PIocpRecord;5. iWorkCount: Cardinal;6. begin7. IocpRecord

33、 :=nil ;8. iWorkCount :=0;9. ClientSocket :=nil ;10. Result := False;11. if not GetQueuedCompletionStatus(FIocpHandle, iWorkCount, DWORD(ClientSocket),12. POverlapped(IocpRecord), INFINITE)then / 此處有可能多個(gè)線程處理同一個(gè)SocketHandle 對(duì)象,因此需要加鎖13. begin /客戶(hù)端異常斷開(kāi)14. if Assigned(ClientSocket) and Assigned(ClientS

34、ocket . SocketHandle) then15. begin16. ClientSocket. SocketHandle . FConnected := False;17. Exit;18. end;19. end;20. if Cardinal(IocpRecord) = SHUTDOWN_FLAG then21. Exit;24. Result := True;25. if Assigned(ClientSocket)and Assigned(ClientSocket . SocketHandle) then26. begin27. if ClientSocket. Socket

35、Handle . Connected then28. begin29. if iWorkCount >0 then30. begin31. try32. ClientSocket. Lock. Enter;33. try34. if Assigned(ClientSocket . SocketHandle) then35. ClientSocket. SocketHandle . ProcessIOComplete(IocpRecord, iWorkCount);36. finally37. ClientSocket. Lock. Leave;38. end;39. if Assigne

36、d(ClientSocket . SocketHandle) and ( not ClientSocket . SocketHandle . Connected)then40. begin41. ClientSocket. Lock. Enter;42. try43. if Assigned(ClientSocket . SocketHandle) then44. FreeSocketHandle(ClientSocket. SocketHandle);45. finally46. ClientSocket. Lock . Leave;47. end;48. end;49. except50.

37、 on E: Exception do51. DoError('ProcessIOComplete' , E . Message);52. end;53. end54. else /如果完成個(gè)數(shù)為0,且狀態(tài)為接收,則釋放連接55. begin56. if IocpRecord . IocpOperate = ioRead then57. begin58. ClientSocket. Lock. Enter;59. try60. if Assigned(ClientSocket . SocketHandle) then61. FreeSocketHandle(ClientSock

38、et. SocketHandle);62. finally63. ClientSocket. Lock. Leave;64. end;65. end66. else67. DoError('WorkClient' , 'WorkCount = 0, Code:'+ IntToStr(GetLastError) +', Message:'+ GetLastErrorStr);68. end;1169. end70. else/斷開(kāi)連接71. begin72. Clientsocket.Lock. Enter;73. try74. if Assign

39、ed(ClientSocket . SocketHandle) then75. FreeSocketHandle(ClientSocket. SocketHandle);76. finally77. ClientSocket. Lock. Leave;78. end;79. end;80. end81. else /WorkCount 為0表示發(fā)生了異常,記錄日志82. DoError( 'GetQueuedCompletionStatus' , 'Return SocketHandle nil');83. end;第8步主要是使用 GetQueuedCompl

40、etionStatus函數(shù)來(lái)獲取完成端口事件通知,如果有事件完成,則可以通過(guò)lpCompletionKey 來(lái)知道是哪個(gè)句柄有數(shù)據(jù)收到。這里有個(gè)技巧是我們?cè)诮壎ǖ臅r(shí)候使用如下語(yǔ)句:CreateIoCompletionPort(ASocket, FIOCPHandle,DWORD(ClientSocket), 0),用lpCompletionKey 來(lái)傳遞了一個(gè)指針,這其中就包含了一個(gè)鎖和服務(wù)對(duì)象,定 義結(jié)構(gòu)如下:delphi view plaincopy1. *客戶(hù)端對(duì)象和鎖*2. TClientSocket = record3. Lock: TCriticalSection;4. Socke

41、tHandle: TSocketHandle;5. end;6. PClientSocket = ATClientSocket;因而我們?cè)谑盏绞录ㄖ?,就可以直接調(diào)用TSocketHandle對(duì)象來(lái)完成分包解包以及后續(xù)的邏輯處理。 這樣一個(gè)完成端口的整體骨架就有了,后續(xù)還有收發(fā)數(shù)據(jù)、分包解包和業(yè)務(wù)邏輯處理,以及對(duì)象和鎖分離等細(xì) 節(jié)處理。更詳細(xì)代碼見(jiàn)示例代碼的lOCPSocket單元。15(三):接收、發(fā)送、緩存接收完成端口是結(jié)合重疊端口 一起使用的,在接收數(shù)據(jù)之前,我們需要投遞一個(gè)接收請(qǐng)求,使用functionWSARecv(s: TSocket; IpBuffers: LPWSABUF;

42、dwBufferCount: DWORD;var IpNumberOfBytesRecvd,IpFlags: DWORD; lpOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine:LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer 函數(shù),接收連接后,就需要投遞一個(gè)請(qǐng)求,代碼 如下: delphiview plaincopy 31. procedure TSocketHandle . PreRecv(AIocpRecord: PIocpRecord);2. var3. iFlags, iTransfer: Car

43、dinal;4. iErrCode: Integer;5. begin6. if not Assigned(AIocpRecord) then7. begin8. New(AIocpRecord);9. AIocpRecord. WsaBuf. buf := FIocpRecvBuf;10. AIocpRecord. WsaBuf. len := MAX_IOCPBUFSIZE;11. FIocpRecv := AIocpRecord;12. end;13. AIocpRecord . Overlapped . Internal :=0;14. AIocpRecord . Overlapped

44、 . InternalHigh :=0;15. AIocpRecord. Overlapped.Offset :=0;16. AIocpRecord . Overlapped . OffsetHigh :=0;17. AIocpRecord. Overlapped.hEvent :=0;18. AIocpRecord. IocpOperate :=ioRead;19. iFlags :=0;20. if WSARecv(FSocket, AIocpRecord . WsaBuf, 1, iTransfer, iFlags, AIocpRecord. Overlapped,21. nil ) =

45、 SOCKET_ERROR then22. begin23. iErrCode := WSAGetLastError;24. if iErrCode = WSAECONNRESET then / 客戶(hù)端被關(guān)閉25. FConnected := False;26. if iErrCode <> ERROR_IO_PENDINGthen / 不拋出異常,觸發(fā)異常事件27. begin28. FIocpServer . DoError( 'WSARecv' , GetLastWsaErrorStr);29. ProcessNetError(iErrCode);30. en

46、d;31. end;|32. end;|WSARecv會(huì)返回錯(cuò)誤,可以幫助我們定位網(wǎng)絡(luò)問(wèn)題,如連接斷了,具體函數(shù)代碼如下: delphiview plaincopy1. procedure TSocketHandle . ProcessNetError( const AErrorCode: Integer);2. begin3. if AErrorCode = WSAECONNRESET then / 客戶(hù)端斷開(kāi)連接4. FConnected:= False5. else if AErrorCode = WSAEDISCONthen / 客戶(hù)端斷開(kāi)連接6. FConnected:= False

47、7. else if AErrorCode = WSAENETDOWN then / 網(wǎng)絡(luò)異常8. FConnected:= False9. elseifAErrorCode= WSAENETRESETthen/心跳包攔截到的異常10. FConnected:= False11. elseifAErrorCode= WSAESHUTDOWNthen/連接被關(guān)閉12. FConnected:= False13. elseifAErrorCode= WSAETIMEDOUTthen/連接斷掉或者網(wǎng)絡(luò)異常14. FConnected:= False;15. end;WSARecv函數(shù)還有另外一個(gè)作用

48、,如果客戶(hù)端斷開(kāi)連接了,WSARecv會(huì)收到一個(gè)0字節(jié)的返回,我們可以根據(jù)這個(gè)釋放連接對(duì)象。如果客戶(hù)端直接拔網(wǎng)線,WSARecv是檢測(cè)不到,這是我們要根據(jù)超時(shí)來(lái)檢測(cè)。接收連接之后我們投遞一次接收數(shù)據(jù)請(qǐng)求,然后收到數(shù)據(jù)處理后,再投遞下一次請(qǐng)求,因而我們相當(dāng)于每 次只有一個(gè)接收請(qǐng)求,我們只需要定義一個(gè)接收結(jié)構(gòu)體和一個(gè)固定大小的緩存就可以了,類(lèi)似: delphi view plaincopy 七1. *保存投遞請(qǐng)求的地址信息 *2. FIocpRecv: PIocpRecord;3. FIocpRecvBuf:array 0. MAX_IOCPBUFSIZE-1 of Char;接收的數(shù)據(jù)我們一起放

49、在一個(gè)內(nèi)存流中,主要代碼如下:delphiview plaincopy1. procedure TSocketHandle.ProcessIOComplete(AIocpRecord: PIocpRecord;2. const ACount: Cardinal);3. begin4. case AIocpRecord . IocpOperateof5. ioNone: Exit;6. ioRead:/收到數(shù)據(jù)7. begin8. FActiveTime := Now;9. ReceiveData(AIocpRecord. WsaBuf. buf, ACount);10. if FConnect

50、ed then11. PreRecv(AIocpRecord);/ 投遞請(qǐng)求12. end;13. ioWrite:/發(fā)送數(shù)據(jù)完成,需要釋放 AIocpRecord 的指針14. begin15. FActiveTime := Now;16. FSendOverlapped. Release(AIocpRecord);17. end;18. ioStream:19. begin20. FActiveTime := Now;21. FSendOverlapped . Release(AIocpRecord);22. WriteStream; / 繼續(xù)發(fā)送流23. end;24. end;25.

51、end;26.27. procedure TSocketHandle . ReceiveData(AData: PAnsiChar; const ALen: Cardinal);28. begin29. FInputBuf . Write(ADataA, ALen);30. Process;31. end;有很多完成端口為了提高效率,會(huì)對(duì)接收隊(duì)列使用內(nèi)存池,以減少內(nèi)存的分配和釋放次數(shù),使用內(nèi)存流會(huì) 有頻繁的申請(qǐng)和釋放操作,也容易造成碎片,我們這里沒(méi)有使用內(nèi)存池,而是使用FastMM來(lái)加快速度。FastMM在多線程和內(nèi)存碎片方面做的比DELPHI自帶的內(nèi)存管理器要強(qiáng),一般能提高2到3倍的性能。發(fā)

52、送發(fā)送和接收是類(lèi)似的,使用 function WSASend(s: TSocket; IpBuffers: LPWSABUF; dwBufferCount:DWORD; var IpNumberOfBytesSent: DWORD; dwFlags: DWORD; IpOverlapped:LPWSAOVERLAPPED; IpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer 函數(shù),如發(fā)送一個(gè) Buffer的示例代碼如下: delphi view plaincopy1. procedure TSocketHandle

53、 . WriteBuffer( const ABuffer; const ACount: Integer;2. const AIocpOperate: TIocpOperate);3. var4. IocpRec: PIocpRecord;5. iErrCode: Integer;6. dSend, dFlag: DWORD;7. begin8. IocpRec := FSendOverlapped . Allocate(ACount);9. FillChar(IocpRec . Overlapped, SizeOf(IocpRec . Overlapped), 0);10. IocpRec . IocpOperate := AIocpOperate;11. System . Move(ABuffer, IocpRec . WsaBuf. bufA, ACount);12. dFlag :=0;13. if WSASend(FSocket, IocpRec . WsaBuf, 1, dSend, dFlag, IocpRec . Overlapped, nil ) = SOCKET_ER ROR then14. begin15. iErrCode := WSAGetL

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論