BlockingTCPClient.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. {
  2. 文件名:BlockingTCPClient.pas
  3. 功 能:阻塞方式TCP客户端组件,单独线程接收数据。
  4. 建 立:尹进
  5. 历 史:
  6. 2005.12.23:补文件说明信息(尹进)
  7. }
  8. unit BlockingTCPClient;
  9. interface
  10. uses
  11. WinSock2, RealICQSocket, RealICQProxy,
  12. SysUtils, Classes, Windows;
  13. type
  14. TBlockingTCPClientRecvThread = class;
  15. TBlockingTCPClientReceivedDataEvent = procedure(Sender: TObject; RecvThread: TBlockingTCPClientRecvThread; RecvBytes: Integer) of object;
  16. TBlockingTCPClientSendedDataEvent = procedure(Sender: TObject; SendBytes: Integer) of object;
  17. TBlockingTCPClientBeforeSendDataEvent = procedure(Sender: TObject; var Buf; Size: Integer) of object;
  18. TBlockingTCPClient = class
  19. private
  20. FEncryptCriticalSection: TRTLCriticalSection;
  21. FConnected: Boolean;
  22. FSocket: TSocket;
  23. FRemoteAddress: String;
  24. FRemotePort: Integer;
  25. FProxy: TProxy;
  26. FRecvBufferSize: Integer;
  27. FCallSynchronize: Boolean;
  28. FLocalAddress: String;
  29. FLocalPort: Integer;
  30. FNoDelay: Boolean;
  31. FOnConnected: TNotifyEvent;
  32. FOnDisconnected: TNotifyEvent;
  33. FOnReceivedData: TBlockingTCPClientReceivedDataEvent;
  34. FOnSendedData: TBlockingTCPClientSendedDataEvent;
  35. FOnBeforeSendData: TBlockingTCPClientBeforeSendDataEvent;
  36. procedure SetRemoteAddress(Value:String);
  37. procedure SetRemotePort(Value:Integer);
  38. procedure SetProxy(Value:TProxy);
  39. procedure SetRecvBufferSize(Value:Integer);
  40. procedure SetNoDelay(Value: Boolean);
  41. protected
  42. procedure DoConnected;
  43. procedure DoDisconnected;
  44. procedure DoReceivedData(RecvThread: TBlockingTCPClientRecvThread; RecvBytes: Integer);
  45. procedure DoSendedData(SendBytes: Integer);
  46. procedure DoBeforeSendData(var Buf; Size: Integer);
  47. public
  48. constructor Create;
  49. destructor Destroy; override;
  50. procedure Connect(StartRecvThread: Boolean = True);
  51. procedure Disconnect;
  52. procedure SendBuffer(var Buf; Size: Integer);
  53. property SocketNO: TSocket read FSocket;
  54. property NoDelay: Boolean read FNoDelay write SetNoDelay;
  55. property CallSynchronize: Boolean read FCallSynchronize write FCallSynchronize;
  56. property RemoteAddress: String read FRemoteAddress write SetRemoteAddress;
  57. property RemotePort: Integer read FRemotePort write SetRemotePort;
  58. property Proxy: TProxy read FProxy write SetProxy;
  59. property Connected: Boolean read FConnected;
  60. property RecvBufferSize: Integer read FRecvBufferSize write SetRecvBufferSize;
  61. property LocalAddress: String read FLocalAddress write FLocalAddress;
  62. property LocalPort: Integer read FLocalPort write FLocalPort;
  63. property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
  64. property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected;
  65. property OnReceivedData: TBlockingTCPClientReceivedDataEvent read FOnReceivedData write FOnReceivedData;
  66. property OnSendedData: TBlockingTCPClientSendedDataEvent read FOnSendedData write FOnSendedData;
  67. property OnBeforeSendData: TBlockingTCPClientBeforeSendDataEvent read FOnBeforeSendData write FOnBeforeSendData;
  68. end;
  69. //用于接收数据的线程
  70. TBlockingTCPClientRecvThread = class(TThread)
  71. private
  72. FBlockingTCPClient: TBlockingTCPClient;
  73. FBuf: array of Byte;
  74. FNotProcessedBufferLength: Integer;
  75. FRecvBytes: Integer;
  76. procedure DoDisconnect;
  77. procedure DoReceivedData();
  78. protected
  79. procedure Execute; override;
  80. public
  81. constructor Create(ABlockingTCPClient: TBlockingTCPClient);
  82. destructor Destroy; override;
  83. procedure CopyRecvBufferTo(var Buf; Offset: Integer; Size: Integer);
  84. procedure CutRecvBufferTo(var Buf; Offset: Integer; Size: Integer);
  85. published
  86. property NotProcessedBufferLength: Integer read FNotProcessedBufferLength;
  87. end;
  88. implementation
  89. uses
  90. LoggerImport;
  91. {TBlockingTCPClient}
  92. //------------------------------------------------------------------------------
  93. procedure TBlockingTCPClient.SetNoDelay(Value: Boolean);
  94. begin
  95. if FSocket <> INVALID_SOCKET then
  96. begin
  97. FNoDelay := Value;
  98. setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY, @FNoDelay, SizeOf(FNoDelay));
  99. end;
  100. end;
  101. //------------------------------------------------------------------------------
  102. constructor TBlockingTCPClient.Create;
  103. begin
  104. inherited Create;
  105. InitializeCriticalSection(FEncryptCriticalSection);
  106. FCallSynchronize := True;
  107. FNoDelay := False;
  108. FConnected := False;
  109. FSocket := INVALID_SOCKET;
  110. FProxy := TProxy.Create;
  111. FRecvBufferSize := 65534;
  112. // FRecvBufferSize := 8192 * 2;
  113. end;
  114. //------------------------------------------------------------------------------
  115. destructor TBlockingTCPClient.Destroy;
  116. begin
  117. try
  118. if Connected then Disconnect;
  119. FProxy.Free;
  120. finally
  121. EnterCriticalSection(FEncryptCriticalSection);
  122. DeleteCriticalSection(FEncryptCriticalSection);
  123. inherited Destroy;
  124. end;
  125. end;
  126. //------------------------------------------------------------------------------
  127. procedure TBlockingTCPClient.Connect(StartRecvThread: Boolean = True);
  128. var
  129. serverAddr: TSockAddrIn;
  130. BindProxyAddr: TSockAddrIn;
  131. lastError: Integer;
  132. ARemoteIP: String;
  133. ret:Integer;
  134. LocalAddr: TSockAddrIn;
  135. Length: Integer;
  136. begin
  137. if FRemoteAddress = '' then raise TSocketException.Create('Server address empty');
  138. if FRemotePort = 0 then raise TSocketException.Create('Server port empty');
  139. if Connected then Disconnect;
  140. case FProxy.ProxyType of
  141. ptNone:
  142. begin
  143. FSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  144. if FSocket = INVALID_SOCKET then raise TSocketException.CreateFmt('ErrorCode:%d',[WSAGetLastError]);
  145. if not HostToIP(FRemoteAddress, ARemoteIP) then ARemoteIP := FRemoteAddress;
  146. serverAddr.sin_family:= AF_INET;
  147. serverAddr.sin_port:= htons(FRemotePort);
  148. serverAddr.sin_addr.S_addr:= inet_addr(PAnsiChar(ARemoteIP));
  149. ret := WinSock2.connect(FSocket,@serverAddr,SizeOf(serverAddr));
  150. if ret = SOCKET_ERROR then begin lastError := WSAGetLastError(); if lastError <> 0 then raise TSocketException.CreateFmt('连接失败:%d',[lastError]); end;
  151. end;
  152. ptSocks5:
  153. begin
  154. if FProxy.Address = '' then raise TSocketException.Create('Proxy server address Empty');
  155. if FProxy.Port = 0 then raise TSocketException.Create('Proxy server port Empty');
  156. FSocket := ConnectToSocks5Proxy(FRemoteAddress, FRemotePort,
  157. FProxy.Address, FProxy.Port,
  158. FProxy.Username, FProxy.Password,
  159. ppTCP, BindProxyAddr);
  160. end;
  161. ptHttp:
  162. begin
  163. if FProxy.Address = '' then raise TSocketException.Create('Proxy server address Empty');
  164. if FProxy.Port = 0 then raise TSocketException.Create('Proxy server port Empty');
  165. FSocket := ConnectToHttpProxy(FRemoteAddress, FRemotePort,
  166. FProxy.Address, FProxy.Port,
  167. FProxy.Username, FProxy.Password, FProxy.Domain);
  168. end;
  169. end;
  170. Length := SizeOf(LocalAddr);
  171. getSockname(FSocket, LocalAddr, Length);
  172. FLocalAddress := inet_ntoa(LocalAddr.sin_addr);
  173. FLocalPort := ntohs(localAddr.sin_port);
  174. FConnected := True;
  175. DoConnected;
  176. if StartRecvThread then TBlockingTCPClientRecvThread.Create(Self);
  177. end;
  178. //------------------------------------------------------------------------------
  179. procedure TBlockingTCPClient.Disconnect;
  180. begin
  181. if FSocket <> INVALID_SOCKET then
  182. begin
  183. try
  184. try
  185. shutdown(FSocket, SD_BOTH);
  186. finally
  187. closeSocket(FSocket);
  188. FSocket := INVALID_SOCKET;
  189. end;
  190. finally
  191. FConnected := False;
  192. DoDisconnected;
  193. end;
  194. end;
  195. end;
  196. //------------------------------------------------------------------------------
  197. procedure TBlockingTCPClient.SendBuffer(var Buf; Size: Integer);
  198. var
  199. SendBytes,
  200. ErrorCode:Integer;
  201. begin
  202. try
  203. EnterCriticalSection(FEncryptCriticalSection);
  204. DoBeforeSendData(Buf, Size);
  205. SendBytes:= send(FSocket, Buf, Size, 0);
  206. if SendBytes = 0 then
  207. begin
  208. Info('SendBytes = 0:' + SysErrorMessage(ErrorCode),'TBlockingTCPClient.SendBuffer');
  209. Disconnect;
  210. Exit;
  211. end;
  212. if SendBytes = SOCKET_ERROR then
  213. begin
  214. ErrorCode := GetLastError;
  215. if (ErrorCode = WSAECONNABORTED) or
  216. (ErrorCode = WSAECONNRESET) or
  217. (ErrorCode = WSAETIMEDOUT) or
  218. (ErrorCode = WSAENOTSOCK) then
  219. begin
  220. Info('发送数据时报错:' + SysErrorMessage(ErrorCode),'TBlockingTCPClient.SendBuffer');
  221. Disconnect;
  222. end;
  223. end
  224. else
  225. begin
  226. DoSendedData(SendBytes);
  227. end;
  228. finally
  229. LeaveCriticalSection(FEncryptCriticalSection);
  230. end;
  231. end;
  232. //------------------------------------------------------------------------------
  233. procedure TBlockingTCPClient.DoConnected;
  234. begin
  235. if Assigned(FOnConnected) then FOnConnected(Self);
  236. end;
  237. //------------------------------------------------------------------------------
  238. procedure TBlockingTCPClient.DoBeforeSendData(var Buf; Size: Integer);
  239. begin
  240. if Assigned(FOnBeforeSendData) then FOnBeforeSendData(Self, Buf, Size);
  241. end;
  242. //------------------------------------------------------------------------------
  243. procedure TBlockingTCPClient.DoSendedData(SendBytes: Integer);
  244. begin
  245. if Assigned(FOnSendedData) then FOnSendedData(Self, SendBytes);
  246. end;
  247. //------------------------------------------------------------------------------
  248. procedure TBlockingTCPClient.DoReceivedData(RecvThread: TBlockingTCPClientRecvThread; RecvBytes: Integer);
  249. begin
  250. if Assigned(FOnReceivedData) then FOnReceivedData(Self, RecvThread, RecvBytes);
  251. end;
  252. //------------------------------------------------------------------------------
  253. procedure TBlockingTCPClient.DoDisconnected;
  254. begin
  255. if Assigned(FOnDisconnected) then FOnDisconnected(Self);
  256. end;
  257. //------------------------------------------------------------------------------
  258. procedure TBlockingTCPClient.SetRemoteAddress(Value: String);
  259. begin
  260. FRemoteAddress := Value;
  261. end;
  262. //------------------------------------------------------------------------------
  263. procedure TBlockingTCPClient.SetRemotePort(Value: Integer);
  264. begin
  265. if (Value<0) or (Value>65535) then raise TSocketException.Create('端口号必须为0-65535之间的数值');
  266. FRemotePort := Value;
  267. end;
  268. //------------------------------------------------------------------------------
  269. procedure TBlockingTCPClient.SetProxy(Value: TProxy);
  270. begin
  271. if Assigned(Value) then FProxy.Assign(Value);
  272. end;
  273. //------------------------------------------------------------------------------
  274. procedure TBlockingTCPClient.SetRecvBufferSize(Value: Integer);
  275. begin
  276. if (Value<1) or (Value>65535) then raise TSocketException.Create('缓冲区大小必须为1-65535之间的数值');
  277. if Connected then raise TSocketException.Create('连接已建立时不允许更改缓冲大小');
  278. FRecvBufferSize := Value;
  279. end;
  280. {TBlockingTCPClientRecvThread}
  281. //------------------------------------------------------------------------------
  282. procedure TBlockingTCPClientRecvThread.Execute;
  283. var
  284. ErrorCode: Integer;
  285. begin
  286. FreeOnTerminate := True;
  287. while (not Terminated) do
  288. begin
  289. FRecvBytes := recv(FBlockingTCPClient.FSocket, FBuf[FNotProcessedBufferLength], Length(FBuf)-FNotProcessedBufferLength, 0);
  290. if FRecvBytes = SOCKET_ERROR then
  291. begin
  292. ErrorCode := GetLastError;
  293. if (ErrorCode = WSAECONNABORTED) or
  294. (ErrorCode = WSAECONNRESET) or
  295. (ErrorCode = WSAETIMEDOUT) or
  296. (ErrorCode = WSAENOTSOCK) then
  297. begin
  298. try
  299. Error('接收数据时报错:' + SysErrorMessage(ErrorCode) + ' ' +FBlockingTCPClient.FRemoteAddress + ':' + IntToStr(FBlockingTCPClient.FRemotePort),'recv');
  300. Synchronize(DoDisconnect);
  301. except
  302. end;
  303. Exit;
  304. end
  305. else
  306. begin
  307. Sleep(1);
  308. Continue;
  309. end;
  310. end;
  311. if FRecvBytes = 0 then
  312. begin
  313. try
  314. Error('RecvBytes = 0' + SysErrorMessage(ErrorCode),'recv');
  315. Synchronize(DoDisconnect);
  316. except
  317. end;
  318. Exit;
  319. end
  320. else
  321. begin
  322. FNotProcessedBufferLength := FNotProcessedBufferLength + FRecvBytes;
  323. try
  324. if FBlockingTCPClient.FCallSynchronize then
  325. Synchronize(DoReceivedData)
  326. else
  327. DoReceivedData;
  328. except
  329. end;
  330. end;
  331. end;
  332. end;
  333. //------------------------------------------------------------------------------
  334. procedure TBlockingTCPClientRecvThread.DoReceivedData();
  335. begin
  336. if Assigned(FBlockingTCPClient) then FBlockingTCPClient.DoReceivedData(Self, FRecvBytes);
  337. end;
  338. //------------------------------------------------------------------------------
  339. procedure TBlockingTCPClientRecvThread.DoDisconnect;
  340. begin
  341. // Info('断开:' + SysErrorMessage(GetLastError),'DoDisconnect');
  342. if Assigned(FBlockingTCPClient) then FBlockingTCPClient.Disconnect;
  343. end;
  344. //------------------------------------------------------------------------------
  345. procedure TBlockingTCPClientRecvThread.CopyRecvBufferTo(var Buf; Offset: Integer; Size: Integer);
  346. begin
  347. CopyMemory(@Buf,@FBuf[Offset],Size);
  348. end;
  349. //------------------------------------------------------------------------------
  350. procedure TBlockingTCPClientRecvThread.CutRecvBufferTo(var Buf; Offset: Integer; Size: Integer);
  351. begin
  352. CopyMemory(@Buf, @FBuf[Offset], Size);
  353. CopyMemory(FBuf, @FBuf[Offset+Size], Length(FBuf)-(Offset+Size));
  354. FNotProcessedBufferLength := FNotProcessedBufferLength - (Offset+Size);
  355. end;
  356. //------------------------------------------------------------------------------
  357. constructor TBlockingTCPClientRecvThread.Create(ABlockingTCPClient: TBlockingTCPClient);
  358. begin
  359. inherited Create(True);
  360. FBlockingTCPClient := ABlockingTCPClient;
  361. SetLength(FBuf,FBlockingTCPClient.RecvBufferSize);
  362. FNotProcessedBufferLength := 0;
  363. Resume;
  364. end;
  365. //------------------------------------------------------------------------------
  366. destructor TBlockingTCPClientRecvThread.Destroy;
  367. begin
  368. inherited Destroy;
  369. end;
  370. end.