BlockingUDPSocket.pas 15 KB

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