BlockingUDPSocket.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. {
  2. 文件名:BlockingUDPSocket.pas
  3. 功 能:阻塞方式UDP套接字组件,单独线程接收数据。
  4. 建 立:尹进
  5. 历 史:
  6. 2005.12.23:补文件说明信息(尹进)
  7. }
  8. unit BlockingUDPSocket;
  9. interface
  10. uses
  11. WinSock2, RealICQSocket, dialogs,RealICQProxy,
  12. SysUtils, Classes, Windows;
  13. const
  14. DEFAULT_SENDBUF_SIZE = 8192;
  15. DEFAULT_RECVBUF_SIZE = 8192;
  16. type
  17. {$M+}
  18. //绑定UDP端口时出错,端口被占用
  19. TBindUDPPortFailException = class(TSocketException);
  20. TBlockingUDPSocketRevcBuf = array of byte;
  21. TBlockingUDPSocketReceivedDataEvent = procedure(Sender: TObject;
  22. RecvBuf: TBlockingUDPSocketRevcBuf;
  23. RecvBytes: Integer;
  24. RemoteAddress: String;
  25. RemotePort: Integer) of object;
  26. TBlockingUDPSocketRecvThread = class;
  27. TBlockingUDPSocket = class
  28. private
  29. FListening: Boolean;
  30. FListenSocket: TSocket;
  31. FListenPort: Integer;
  32. FProxySocket: TSocket;
  33. FProxy: TProxy;
  34. FBindProxyAddr: TSockAddrIn;
  35. //发送和接受缓冲大小
  36. FSendBufSize: Integer;
  37. FRecvBufSize: Integer;
  38. FRecvThread: TBlockingUDPSocketRecvThread;
  39. FCallSynchronize: Boolean;
  40. FConnectted: Boolean;
  41. FOnStartListen: TNotifyEvent;
  42. FOnStopListen: TNotifyEvent;
  43. FOnReceivedData: TBlockingUDPSocketReceivedDataEvent;
  44. procedure SetProxy(Value: TProxy);
  45. //得到和设置缓冲大小的函数
  46. function GetSendBufSize: Integer;
  47. function GetRecvBufSize: Integer;
  48. procedure SetSendBufSize(Value: Integer);
  49. procedure SetRecvBufSize(Value: Integer);
  50. function SendByProxy(var buf;
  51. Len: Integer;
  52. RemoteAddress: String;
  53. RemotePort: Integer): Integer;
  54. protected
  55. procedure DoStartListen;
  56. procedure DoStopListen;
  57. procedure DoReceivedData(RecvBuf: TBlockingUDPSocketRevcBuf;
  58. RecvBytes: Integer;
  59. RemoteAddress: String;
  60. RemotePort: Integer);
  61. public
  62. constructor Create;
  63. destructor Destroy; override;
  64. procedure StartListen(AListenPort: Integer);
  65. procedure StopListen;
  66. procedure Connect(RemoteAddress: String;
  67. RemotePort: Integer);
  68. procedure SendBuffer(var Buf;
  69. Size: Integer;
  70. RemoteAddress: String;
  71. RemotePort: Integer);
  72. procedure SendBufferToConnectted(var Buf;
  73. Size: Integer);
  74. published
  75. property Connectted: Boolean read FConnectted;
  76. property CallSynchronize: Boolean read FCallSynchronize write FCallSynchronize;
  77. property SendBufSize: Integer read GetSendBufSize write SetSendBufSize;
  78. property RecvBufSize: Integer read GetRecvBufSize write SetRecvBufSize;
  79. property Listening: Boolean read FListening;
  80. property ListenPort: Integer read FListenPort;
  81. property Proxy: TProxy read FProxy write SetProxy;
  82. property OnStartListen: TNotifyEvent read FOnStartListen write FOnStartListen;
  83. property OnStopListen: TNotifyEvent read FOnStopListen write FOnStopListen;
  84. property OnReceivedData: TBlockingUDPSocketReceivedDataEvent read FOnReceivedData write FOnReceivedData;
  85. end;
  86. //用于接收数据的线程
  87. TBlockingUDPSocketRecvThread = class(TThread)
  88. private
  89. FBlockingUDPSocket: TBlockingUDPSocket;
  90. FBuf: TBlockingUDPSocketRevcBuf;
  91. FRecvBytes: Integer;
  92. FRemoteAddr: TSockAddrIn;
  93. procedure DoReceivedData;
  94. protected
  95. procedure Execute; override;
  96. public
  97. constructor Create(ABlockingUDPSocket: TBlockingUDPSocket);
  98. destructor Destroy; override;
  99. published
  100. end;
  101. implementation
  102. {TBlockingUDPSocketRecvThread}
  103. //------------------------------------------------------------------------------
  104. procedure TBlockingUDPSocketRecvThread.DoReceivedData();
  105. begin
  106. if Assigned(FBlockingUDPSocket) then
  107. FBlockingUDPSocket.DoReceivedData(FBuf, FRecvBytes, inet_ntoa(FRemoteAddr.sin_addr), ntohs(FRemoteAddr.sin_port));
  108. end;
  109. //------------------------------------------------------------------------------
  110. procedure TBlockingUDPSocketRecvThread.Execute;
  111. var
  112. fromLen,
  113. ErrorCode: Integer;
  114. begin
  115. FreeOnTerminate := True;
  116. FRemoteAddr.sin_port:= htons(FBlockingUDPSocket.FListenPort);
  117. FRemoteAddr.sin_addr.S_addr:= htonl(INADDR_ANY);
  118. FRemoteAddr.sin_family:= AF_INET;
  119. fromLen := SizeOf(FRemoteAddr);
  120. while (not Terminated) do
  121. begin
  122. if FBlockingUDPSocket = nil then break;
  123. FRecvBytes := recvfrom(FBlockingUDPSocket.FListenSocket, FBuf[0], Length(FBuf), 0, FRemoteAddr, fromLen);
  124. if FRecvBytes = SOCKET_ERROR then
  125. begin
  126. ErrorCode := GetLastError;
  127. if (ErrorCode = WSAENOTSOCK) then
  128. begin
  129. FRecvBytes := 0;
  130. end
  131. else
  132. begin
  133. Sleep(1);
  134. Continue;
  135. end;
  136. end;
  137. if FRecvBytes = 0 then
  138. begin
  139. if Assigned(FBlockingUDPSocket) then
  140. Synchronize(FBlockingUDPSocket.StopListen);
  141. Exit;
  142. end;
  143. if FBlockingUDPSocket = nil then break;
  144. if (FBlockingUDPSocket.Proxy.ProxyType = ptSocks5) then
  145. begin
  146. if (FBlockingUDPSocket.FBindProxyAddr.sin_addr.S_addr = FRemoteAddr.sin_addr.S_addr) and
  147. (FBlockingUDPSocket.FBindProxyAddr.sin_port = FRemoteAddr.sin_port) and
  148. (FBuf[0] = $00) and
  149. (FBuf[1] = $00) and
  150. (FBuf[2] = $00) and
  151. (FBuf[3] = $01) then
  152. begin
  153. CopyMemory(@FRemoteAddr.sin_addr, @FBuf[4], 4); //远程机器地址
  154. CopyMemory(@FRemoteAddr.sin_port, @FBuf[8], 2); //远程机器端口
  155. FRecvBytes := FRecvBytes - 10;
  156. CopyMemory(@FBuf[0], @FBuf[10], FRecvBytes); //实际数据
  157. end;
  158. end;
  159. if FRecvBytes = 0 then
  160. begin
  161. if Assigned(FBlockingUDPSocket) then
  162. Synchronize(FBlockingUDPSocket.StopListen);
  163. Exit;
  164. end
  165. else
  166. begin
  167. try
  168. if Assigned(FBlockingUDPSocket) then
  169. begin
  170. if FBlockingUDPSocket.FCallSynchronize then
  171. Synchronize(DoReceivedData)
  172. else
  173. DoReceivedData;
  174. end;
  175. except
  176. end;
  177. end;
  178. end;
  179. end;
  180. //------------------------------------------------------------------------------
  181. constructor TBlockingUDPSocketRecvThread.Create(ABlockingUDPSocket: TBlockingUDPSocket);
  182. begin
  183. inherited Create(True);
  184. FBlockingUDPSocket := ABlockingUDPSocket;
  185. SetLength(FBuf, FBlockingUDPSocket.RecvBufSize);
  186. Resume;
  187. end;
  188. //------------------------------------------------------------------------------
  189. destructor TBlockingUDPSocketRecvThread.Destroy;
  190. begin
  191. if FBlockingUDPSocket <> nil then FBlockingUDPSocket.FRecvThread := nil;
  192. inherited Destroy;
  193. end;
  194. {TBlockingUDPSocket}
  195. //------------------------------------------------------------------------------
  196. procedure TBlockingUDPSocket.SetProxy(Value: TProxy);
  197. begin
  198. if Assigned(Value) then FProxy.Assign(Value);
  199. end;
  200. //------------------------------------------------------------------------------
  201. function TBlockingUDPSocket.GetRecvBufSize: Integer;
  202. begin
  203. Result:= FRecvBufSize;
  204. end;
  205. //------------------------------------------------------------------------------
  206. function TBlockingUDPSocket.GetSendBufSize: Integer;
  207. begin
  208. Result:= FSendBufSize;
  209. end;
  210. //------------------------------------------------------------------------------
  211. procedure TBlockingUDPSocket.SetRecvBufSize(Value: Integer);
  212. var
  213. ErrorCode: Integer;
  214. begin
  215. if FRecvBufSize <> Value then
  216. begin
  217. ErrorCode:= setsockopt(FListenSocket, SOL_SOCKET, SO_RCVBUF, @Value, sizeof(Value));
  218. if ErrorCode = SOCKET_ERROR then
  219. raise TSocketException.CreateFmt('设置接收缓冲区出错。错误码是%d', [GetLastError]);
  220. FRecvBufSize:= Value;
  221. end;
  222. end;
  223. //------------------------------------------------------------------------------
  224. procedure TBlockingUDPSocket.SetSendBufSize(Value: Integer);
  225. var
  226. ErrorCode: Integer;
  227. begin
  228. if FSendBufSize <> Value then
  229. begin
  230. ErrorCode:= setsockopt(FListenSocket, SOL_SOCKET, SO_SNDBUF, @Value, sizeof(Value));
  231. if ErrorCode = SOCKET_ERROR then
  232. raise TSocketException.CreateFmt('设置发送缓冲区错误。错误码是%d', [GetLastError]);
  233. FSendBufSize:= Value;
  234. end;
  235. end;
  236. //------------------------------------------------------------------------------
  237. function TBlockingUDPSocket.SendByProxy(var Buf;
  238. Len: Integer;
  239. RemoteAddress: String;
  240. RemotePort: Integer): Integer;
  241. var
  242. TempBuf: array[0..2047] of Byte;
  243. saRemote: TSockAddrIn;
  244. begin
  245. saRemote.sin_family:= AF_INET;
  246. saRemote.sin_port:= htons(RemotePort);
  247. saRemote.sin_addr.S_addr:= inet_addr(PChar(RemoteAddress));
  248. // 加上报头
  249. FillChar(TempBuf, 2047, $0);
  250. TempBuf[0]:= $00; //保留
  251. TempBuf[1]:= $00; //保留
  252. TempBuf[2]:= $00; //是否分段重组(此处不用)
  253. TempBuf[3]:= $01; //IPv4
  254. CopyMemory(@TempBuf[4], @saRemote.sin_addr, 4); //代理服务器地址
  255. CopyMemory(@TempBuf[8], @saRemote.sin_port, 2); //代理服务器端口
  256. CopyMemory(@TempBuf[10], @Buf, Len); //实际数据
  257. Result:= sendto(FListenSocket, TempBuf, Len + 10, 0, FBindProxyAddr, SizeOf(FBindProxyAddr));
  258. end;
  259. //------------------------------------------------------------------------------
  260. procedure TBlockingUDPSocket.Connect(RemoteAddress: String;
  261. RemotePort: Integer);
  262. var
  263. serverAddr: TSockAddrIn;
  264. lastError: Integer;
  265. ARemoteIP: String;
  266. begin
  267. FConnectted := False;
  268. if not HostToIP(RemoteAddress, ARemoteIP) then ARemoteIP := RemoteAddress;
  269. serverAddr.sin_family:= AF_INET;
  270. serverAddr.sin_port:= htons(RemotePort);
  271. serverAddr.sin_addr.S_addr:= inet_addr(PAnsiChar(ARemoteIP));
  272. WinSock2.connect(FListenSocket, @serverAddr, SizeOf(serverAddr));
  273. lastError := WSAGetLastError();
  274. if lastError <> 0 then
  275. FConnectted := False
  276. else
  277. FConnectted := (Proxy.ProxyType = ptNone);
  278. end;
  279. //------------------------------------------------------------------------------
  280. procedure TBlockingUDPSocket.SendBufferToConnectted(var Buf;
  281. Size: Integer);
  282. //var
  283. // SendBytes,
  284. // ErrorCode:Integer;
  285. begin
  286. //SendBytes:=
  287. send(FListenSocket, Buf, Size, 0);
  288. {if SendBytes = SOCKET_ERROR then
  289. begin
  290. ErrorCode := GetLastError;
  291. if ErrorCode <> WSAEWOULDBLOCK then
  292. begin
  293. if ErrorCode <> 0 then Disconnect;
  294. end;
  295. end;}
  296. end;
  297. //------------------------------------------------------------------------------
  298. procedure TBlockingUDPSocket.SendBuffer(var Buf;
  299. Size: Integer;
  300. RemoteAddress: String;
  301. RemotePort: Integer);
  302. var
  303. ret, ErrorCode: Integer;
  304. saRemote: TSockAddrIn;
  305. ARemoteIP: String;
  306. begin
  307. if not HostToIP(RemoteAddress, ARemoteIP) then ARemoteIP := RemoteAddress;
  308. saRemote.sin_family:= AF_INET;
  309. saRemote.sin_port:= htons(RemotePort);
  310. saRemote.sin_addr.S_addr:= inet_addr(PChar(ARemoteIP));
  311. if saRemote.sin_addr.S_addr = INADDR_NONE then
  312. raise TSocketException.Create('无效的远程主机地址!');
  313. if Proxy.ProxyType = ptSocks5 then
  314. ret:= SendByProxy(Buf, Size, RemoteAddress, RemotePort)
  315. else
  316. ret:= sendto(FListenSocket, Buf, Size, 0, saRemote, SizeOf(saRemote));
  317. if ret = SOCKET_ERROR then
  318. begin
  319. ErrorCode := GetLastError;
  320. if ErrorCode <> WSAEWOULDBLOCK then
  321. begin
  322. {
  323. if ErrorCode <> 0 then
  324. raise TSocketException.CreateFmt('发送数据时出错。错误码是%d', [ErrorCode]);
  325. }
  326. end;
  327. end;
  328. end;
  329. //------------------------------------------------------------------------------
  330. procedure TBlockingUDPSocket.StartListen(AListenPort: Integer);
  331. var
  332. localAddr: TSockAddrIn;
  333. length: Integer;
  334. lastError: Integer;
  335. begin
  336. if (AListenPort < 0) or (AListenPort > 65535) then
  337. raise TSocketException.Create('端口号必须为0-65535之间的数值');
  338. FListenPort := AListenPort;
  339. if FListening then StopListen;
  340. FListenSocket := Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  341. if FListenSocket = INVALID_SOCKET then raise TSocketException.CreateFmt('创建套接字失败,错误代码:%d',[WSAGetLastError]);
  342. //设置缓冲区大小
  343. RecvBufSize := FRecvBufSize;
  344. SendBufSize := FSendBufSize;
  345. {
  346. //设置在TIME_WAIT状态下可以再次在相同的端口上监听
  347. bReLinten:= True;
  348. setsockopt(FListenSocket, SOL_SOCKET, SO_REUSEADDR, @bReLinten, SizeOf(bReLinten));
  349. }
  350. localAddr.sin_family:= AF_INET;
  351. localAddr.sin_port:= htons(FListenPort);
  352. localAddr.sin_addr.S_addr:= htonl(INADDR_ANY);
  353. WinSock2.bind(FListenSocket, @localAddr, SizeOf(localAddr));
  354. lastError := WSAGetLastError();
  355. if lastError <> 0 then raise TBindUDPPortFailException.CreateFmt('绑定本地UDP端口 %d 失败,错误代码:%d',[FListenPort, lastError]);
  356. length := SizeOf(localAddr);
  357. getSockname(FListenSocket, localAddr, length);
  358. FListenPort := ntohs(localAddr.sin_port);
  359. case FProxy.ProxyType of
  360. ptSocks5:
  361. begin
  362. if FProxy.Address = '' then raise TSocketException.Create('必须先设置代理服务器地址');
  363. if FProxy.Port = 0 then raise TSocketException.Create('必须先设置代理服务器端口');
  364. FProxySocket := ConnectToSocks5Proxy('', FListenPort,
  365. FProxy.Address, FProxy.Port,
  366. FProxy.Username, FProxy.Password,
  367. ppUDP, FBindProxyAddr);
  368. end;
  369. ptHttp:
  370. begin
  371. raise TSocketException.Create('当前版本不支持UDP协议的HTTP代理');
  372. end;
  373. end;
  374. FRecvThread := TBlockingUDPSocketRecvThread.Create(Self);
  375. FListening := True;
  376. DoStartListen;
  377. end;
  378. //------------------------------------------------------------------------------
  379. procedure TBlockingUDPSocket.StopListen;
  380. begin
  381. try
  382. if FRecvThread <> nil then FRecvThread.FBlockingUDPSocket := nil;
  383. closeSocket(FListenSocket);
  384. FListenSocket := INVALID_SOCKET;
  385. try
  386. shutdown(FProxySocket, SD_BOTH);
  387. finally
  388. closeSocket(FProxySocket);
  389. FProxySocket:= INVALID_SOCKET;
  390. end;
  391. finally
  392. FListening := False;
  393. DoStopListen;
  394. end;
  395. end;
  396. //------------------------------------------------------------------------------
  397. procedure TBlockingUDPSocket.DoReceivedData(RecvBuf: TBlockingUDPSocketRevcBuf;
  398. RecvBytes: Integer;
  399. RemoteAddress: String;
  400. RemotePort: Integer);
  401. begin
  402. if Assigned(FOnReceivedData) then FOnReceivedData(Self, RecvBuf, RecvBytes, RemoteAddress, RemotePort);
  403. end;
  404. //------------------------------------------------------------------------------
  405. procedure TBlockingUDPSocket.DoStartListen;
  406. begin
  407. if Assigned(FOnStartListen) then FOnStartListen(Self);
  408. end;
  409. //------------------------------------------------------------------------------
  410. procedure TBlockingUDPSocket.DoStopListen;
  411. begin
  412. if Assigned(FOnStopListen) then FOnStopListen(Self);
  413. end;
  414. //------------------------------------------------------------------------------
  415. constructor TBlockingUDPSocket.Create;
  416. begin
  417. inherited Create;
  418. FConnectted := False;
  419. FCallSynchronize := True;
  420. FListening := False;
  421. FListenSocket := INVALID_SOCKET;
  422. FListenSocket := 0;
  423. FProxySocket := INVALID_SOCKET;
  424. FProxy := TProxy.Create();
  425. FSendBufSize := DEFAULT_SENDBUF_SIZE;
  426. FRecvBufSize := DEFAULT_RECVBUF_SIZE;
  427. end;
  428. //------------------------------------------------------------------------------
  429. destructor TBlockingUDPSocket.Destroy;
  430. begin
  431. if FListening then StopListen;
  432. FProxy.Free;
  433. inherited Destroy;
  434. end;
  435. end.