CnUDP.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnUDP;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:网络通讯组件包
  24. * 单元名称:UDP 通讯单元
  25. * 单元作者:周劲羽 (zjy@cnpack.org)
  26. * 备 注:定义了 TCnUDP,使用非阻塞方式进行 UDP 通讯,支持广播
  27. * 开发平台:PWin2000Pro + Delphi 5.01
  28. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
  29. * 本 地 化:该单元中的字符串均符合本地化处理方式
  30. * 单元标识:$Id$
  31. * 修改记录:2008.11.28 V1.1
  32. * 加入一控制接收缓冲区大小的属性
  33. * 2003.11.21 V1.0
  34. * 创建单元
  35. ================================================================================
  36. |</PRE>}
  37. interface
  38. {$I CnPack.inc}
  39. uses
  40. Windows, Messages, Classes, SysUtils, WinSock, Forms, contnrs;
  41. const
  42. csDefRecvBuffSize = 4096;
  43. csDefUDPSendBuffSize = 256 * 1024;
  44. csDefUDPRecvBuffSize = 256 * 1024;
  45. type
  46. //==============================================================================
  47. // UDP 通讯类
  48. //==============================================================================
  49. { TCnUDP }
  50. TOnReceive = procedure(Sender: TComponent; Buffer: Pointer; Len: Integer;
  51. FromIP: string; Port: Integer) of object;
  52. {* 接收到数据事件
  53. |<PRE>
  54. Sender - TCnUDP 对象
  55. Buffer - 数据缓冲区
  56. Len - 数据缓冲区长度
  57. FromIP - 数据来源 IP
  58. Port - 数据来源端口号
  59. |</PRE>}
  60. TCnUDP = class(TComponent)
  61. {* 使用非阻塞方式进行 UDP 通讯的类。支持广播、数据队列等。}
  62. private
  63. FRemoteHost: string;
  64. FRemotePort: Integer;
  65. FLocalPort: Integer;
  66. FSocketWindow: HWND;
  67. FOnDataReceived: TOnReceive;
  68. FListening: Boolean;
  69. Wait_Flag: Boolean;
  70. RemoteAddress: TSockAddr;
  71. RemoteHostS: PHostEnt;
  72. Succeed: Boolean;
  73. Procing: Boolean;
  74. EventHandle: THandle;
  75. ThisSocket: TSocket;
  76. Queue: TQueue;
  77. FLastError: Integer;
  78. FRecvBufSize: Cardinal;
  79. FRecvBuf: Pointer;
  80. FBindAddr: string;
  81. FSockCount: Integer;
  82. FUDPSendBufSize: Cardinal;
  83. FUDPRecvBufSize: Cardinal;
  84. procedure WndProc(var Message: TMessage);
  85. function ResolveRemoteHost(ARemoteHost: string): Boolean;
  86. procedure SetLocalPort(NewLocalPort: Integer);
  87. procedure ProcessIncomingdata;
  88. procedure ProcessQueue;
  89. procedure FreeQueueItem(P: Pointer);
  90. function GetQueueCount: Integer;
  91. procedure SetupLastError;
  92. function GetLocalHost: string;
  93. procedure UpdateBinding;
  94. procedure SetRecvBufSize(const Value: Cardinal);
  95. procedure SetBindAddr(const Value: string);
  96. function SockStartup: Boolean;
  97. procedure SockCleanup;
  98. procedure SetUDPRecvBufSize(const Value: Cardinal);
  99. procedure SetUDPSendBufSize(const Value: Cardinal);
  100. protected
  101. procedure Wait;
  102. procedure Loaded; override;
  103. public
  104. constructor Create(AOwner: TComponent); override;
  105. destructor Destroy; override;
  106. function SendStream(DataStream: TStream; BroadCast: Boolean = False): Boolean;
  107. {* 发送一个数据流。如果 BroadCase 为真,执行 UDP 广播,否则发送数据到
  108. RomoteHost 的机器上的 RemotePort 端口}
  109. function SendBuffer(Buff: Pointer; Length: Integer; BroadCast:
  110. Boolean = False): Boolean;
  111. {* 发送一个数据块。如果 BroadCase 为真,执行 UDP 广播,否则发送数据到
  112. RomoteHost 的机器上的 RemotePort 端口}
  113. procedure ClearQueue;
  114. {* 清空数据队列。如果用户来不及处理接收到的数据,组件会把新数据包放到数据
  115. 队列中,调用该方法可清空数据队列}
  116. procedure ProcessRecv;
  117. {* 处理该 UDP 接口的接收内容。由于 CnUDP 组件的 OnDataReceived 是在主线程
  118. 消息处理中调用的,如果主线程代码需要等待 UDP 接收而不希望处理所有消息,
  119. 可以调用该函数。}
  120. property LastError: Integer read FLastError;
  121. {* 最后一次错误的错误号,只读属性}
  122. property Listening: Boolean read FListening;
  123. {* 表示当前是否正在监听本地端口,只读属性}
  124. property QueueCount: Integer read GetQueueCount;
  125. {* 当前数据队列的长度,只读属性}
  126. property BindAddr: string read FBindAddr write SetBindAddr;
  127. {* 绑定本地地址}
  128. published
  129. property RemoteHost: string read FRemoteHost write FRemoteHost;
  130. {* 要发送 UDP 数据的目标主机地址}
  131. property RemotePort: Integer read FRemotePort write FRemotePort;
  132. {* 要发送 UDP 数据的目标主机端口号}
  133. property LocalHost: string read GetLocalHost;
  134. {* 返回本机 IP 地址,只读属性}
  135. property LocalPort: Integer read FLocalPort write SetLocalPort;
  136. {* 本地监听的端口号}
  137. property RecvBufSize: Cardinal read FRecvBufSize write SetRecvBufSize default csDefRecvBuffSize;
  138. {* 接收的数据缓冲区大小}
  139. property UDPSendBufSize: Cardinal read FUDPSendBufSize write SetUDPSendBufSize default csDefUDPSendBuffSize;
  140. {* UDP 发送的数据缓冲区大小}
  141. property UDPRecvBufSize: Cardinal read FUDPRecvBufSize write SetUDPRecvBufSize default csDefUDPRecvBuffSize;
  142. {* UDP 接收的数据缓冲区大小}
  143. property OnDataReceived: TOnReceive read FOnDataReceived write
  144. FOnDataReceived;
  145. {* 接收到 UDP 数据包事件}
  146. end;
  147. // 取广播地址
  148. procedure GetBroadCastAddress(sInt: TStrings);
  149. // 取本机IP地址
  150. procedure GetLocalIPAddress(sInt: TStrings);
  151. implementation
  152. {$R-}
  153. //==============================================================================
  154. // 辅助过程
  155. //==============================================================================
  156. // 从Winsock 2.0导入函数WSAIOCtl
  157. function WSAIoctl(s: TSocket; cmd: DWORD; lpInBuffer: PCHAR; dwInBufferLen:
  158. DWORD;
  159. lpOutBuffer: PCHAR; dwOutBufferLen: DWORD;
  160. lpdwOutBytesReturned: LPDWORD;
  161. lpOverLapped: POINTER;
  162. lpOverLappedRoutine: POINTER): Integer; stdcall; external 'WS2_32.DLL';
  163. const
  164. SIO_GET_INTERFACE_LIST = $4004747F;
  165. IFF_UP = $00000001;
  166. IFF_BROADCAST = $00000002;
  167. IFF_LOOPBACK = $00000004;
  168. IFF_POINTTOPOINT = $00000008;
  169. IFF_MULTICAST = $00000010;
  170. type
  171. sockaddr_gen = packed record
  172. AddressIn: sockaddr_in;
  173. filler: packed array[0..7] of AnsiChar;
  174. end;
  175. INTERFACE_INFO = packed record
  176. iiFlags: u_long; // Interface flags
  177. iiAddress: sockaddr_gen; // Interface address
  178. iiBroadcastAddress: sockaddr_gen; // Broadcast address
  179. iiNetmask: sockaddr_gen; // Network mask
  180. end;
  181. // 取广播地址
  182. procedure DoGetIPAddress(sInt: TStrings; IsBroadCast: Boolean);
  183. var
  184. s: TSocket;
  185. wsaD: WSADATA;
  186. NumInterfaces: Integer;
  187. BytesReturned, SetFlags: u_long;
  188. pAddr, pMask, pCast: TInAddr;
  189. pAddrStr: string;
  190. PtrA: pointer;
  191. Buffer: array[0..20] of INTERFACE_INFO;
  192. i: Integer;
  193. begin
  194. WSAStartup($0101, wsaD); // Start WinSock
  195. s := Socket(AF_INET, SOCK_STREAM, 0); // Open a socket
  196. if (s = INVALID_SOCKET) then
  197. exit;
  198. try // Call WSAIoCtl
  199. PtrA := @bytesReturned;
  200. if (WSAIoCtl(s, SIO_GET_INTERFACE_LIST, nil, 0, @Buffer, 1024, PtrA, nil,
  201. nil) <> SOCKET_ERROR) then
  202. begin // If ok, find out how
  203. // many interfaces exist
  204. NumInterfaces := BytesReturned div SizeOf(INTERFACE_INFO);
  205. sInt.Clear;
  206. for i := 0 to NumInterfaces - 1 do // For every interface
  207. begin
  208. SetFlags := Buffer[i].iiFlags;
  209. if (SetFlags and IFF_BROADCAST = IFF_BROADCAST) and not
  210. (SetFlags and IFF_LOOPBACK = IFF_LOOPBACK) then
  211. begin
  212. pAddr := Buffer[i].iiAddress.AddressIn.sin_addr;
  213. pMask := Buffer[i].iiNetmask.AddressIn.sin_addr;
  214. if IsBroadCast then
  215. begin
  216. pCast.S_addr := pAddr.S_addr or not pMask.S_addr;
  217. pAddrStr := string(inet_ntoa(pCast));
  218. end
  219. else
  220. begin
  221. pAddrStr := string(inet_ntoa(pAddr));
  222. end;
  223. if sInt.IndexOf(pAddrStr) < 0 then
  224. sInt.Add(pAddrStr);
  225. end;
  226. end;
  227. end;
  228. except
  229. ;
  230. end;
  231. CloseSocket(s);
  232. WSACleanUp;
  233. end;
  234. // 取本机IP地址
  235. procedure GetLocalIPAddress(sInt: TStrings);
  236. begin
  237. DoGetIPAddress(sInt, False);
  238. end;
  239. // 取广播地址
  240. procedure GetBroadCastAddress(sInt: TStrings);
  241. begin
  242. DoGetIPAddress(sInt, True);
  243. end;
  244. //==============================================================================
  245. // UDP 通讯类
  246. //==============================================================================
  247. { TCnUDP }
  248. const
  249. WM_ASYNCHRONOUSPROCESS = WM_USER + 101;
  250. Const_cmd_true = 'TRUE';
  251. type
  252. PRecvDataRec = ^TRecvDataRec;
  253. TRecvDataRec = record
  254. FromIP: string[128];
  255. FromPort: u_short;
  256. Buff: Pointer;
  257. BuffSize: Integer;
  258. end;
  259. constructor TCnUDP.Create(AOwner: TComponent);
  260. begin
  261. inherited Create(AOwner);
  262. Queue := TQueue.Create;
  263. FListening := False;
  264. Procing := False;
  265. FRecvBufSize := csDefRecvBuffSize;
  266. FUDPSendBufSize := csDefUDPSendBuffSize;
  267. FUDPRecvBufSize := csDefUDPRecvBuffSize;
  268. FBindAddr := '0.0.0.0';
  269. GetMem(RemoteHostS, MAXGETHOSTSTRUCT);
  270. FSocketWindow := AllocateHWND(WndProc);
  271. EventHandle := CreateEvent(nil, True, False, '');
  272. if SockStartup then
  273. begin
  274. ThisSocket := Socket(AF_INET, SOCK_DGRAM, 0);
  275. if ThisSocket = TSocket(INVALID_SOCKET) then
  276. begin
  277. SetupLastError;
  278. SockCleanup;
  279. Exit;
  280. end;
  281. setsockopt(ThisSocket, SOL_SOCKET, SO_DONTLINGER, Const_cmd_true, 4);
  282. setsockopt(ThisSocket, SOL_SOCKET, SO_BROADCAST, Const_cmd_true, 4);
  283. FListening := True;
  284. end;
  285. end;
  286. destructor TCnUDP.Destroy;
  287. begin
  288. if FRecvBuf <> nil then
  289. begin
  290. FreeMem(FRecvBuf);
  291. FRecvBuf := nil;
  292. end;
  293. ClearQueue;
  294. Queue.Free;
  295. FreeMem(RemoteHostS, MAXGETHOSTSTRUCT);
  296. DeallocateHWND(FSocketWindow);
  297. CloseHandle(EventHandle);
  298. if ThisSocket <> 0 then
  299. closesocket(ThisSocket);
  300. if FListening then
  301. SockCleanup;
  302. inherited Destroy;
  303. end;
  304. procedure TCnUDP.UpdateBinding;
  305. var
  306. Data: DWORD;
  307. Addr: TSockAddr;
  308. begin
  309. if not (csDesigning in ComponentState) then
  310. begin
  311. FListening := False;
  312. if ThisSocket <> 0 then
  313. begin
  314. closesocket(ThisSocket);
  315. SockCleanup;
  316. end;
  317. if SockStartup then
  318. begin
  319. ThisSocket := Socket(AF_INET, SOCK_DGRAM, 0);
  320. if ThisSocket = TSocket(INVALID_SOCKET) then
  321. begin
  322. SockCleanup;
  323. SetupLastError;
  324. Exit;
  325. end;
  326. end;
  327. FillChar(Addr, SizeOf(Addr), 0);
  328. Addr.sin_addr.S_addr := Inet_Addr(PAnsiChar(AnsiString(FBindAddr)));
  329. Addr.sin_family := AF_INET;
  330. Addr.sin_port := htons(FLocalPort);
  331. Wait_Flag := False;
  332. if WinSock.Bind(ThisSocket, Addr, SizeOf(Addr)) =
  333. SOCKET_ERROR then
  334. begin
  335. SetupLastError;
  336. SockCleanup;
  337. Exit;
  338. end;
  339. // Allow to send to 255.255.255.255
  340. Data := 1;
  341. WinSock.setsockopt(ThisSocket, SOL_SOCKET, SO_BROADCAST,
  342. PAnsiChar(@Data), SizeOf(Data));
  343. Data := FUDPSendBufSize;
  344. WinSock.setsockopt(ThisSocket, SOL_SOCKET, SO_SNDBUF,
  345. PAnsiChar(@Data), SizeOf(Data));
  346. Data := FUDPRecvBufSize;
  347. WinSock.setsockopt(ThisSocket, SOL_SOCKET, SO_RCVBUF,
  348. PAnsiChar(@Data), SizeOf(Data));
  349. WSAAsyncSelect(ThisSocket, FSocketWindow, WM_ASYNCHRONOUSPROCESS, FD_READ);
  350. FListening := True;
  351. end;
  352. end;
  353. procedure TCnUDP.Loaded;
  354. begin
  355. inherited;
  356. UpdateBinding;
  357. end;
  358. procedure TCnUDP.SetBindAddr(const Value: string);
  359. begin
  360. if Value <> FBindAddr then
  361. begin
  362. FBindAddr := Value;
  363. UpdateBinding;
  364. end;
  365. end;
  366. procedure TCnUDP.SetLocalPort(NewLocalPort: Integer);
  367. begin
  368. if NewLocalPort <> FLocalPort then
  369. begin
  370. FLocalPort := NewLocalPort;
  371. UpdateBinding;
  372. end;
  373. end;
  374. function TCnUDP.ResolveRemoteHost(ARemoteHost: string): Boolean;
  375. var
  376. Buf: array[0..127] of AnsiChar;
  377. begin
  378. Result := False;
  379. if not FListening then Exit;
  380. try
  381. RemoteAddress.sin_addr.S_addr := Inet_Addr(PAnsiChar(StrPCopy(Buf, {$IFDEF UNICODE}AnsiString{$ENDIF}(ARemoteHost))));
  382. if RemoteAddress.sin_addr.S_addr = SOCKET_ERROR then
  383. begin
  384. Wait_Flag := False;
  385. WSAAsyncGetHostByName(FSocketWindow, WM_ASYNCHRONOUSPROCESS, Buf,
  386. PAnsiChar(RemoteHostS), MAXGETHOSTSTRUCT);
  387. repeat
  388. Wait;
  389. until Wait_Flag;
  390. if Succeed then
  391. begin
  392. with RemoteAddress.sin_addr.S_un_b do
  393. begin
  394. s_b1 := remotehostS.h_addr_list^[0];
  395. s_b2 := remotehostS.h_addr_list^[1];
  396. s_b3 := remotehostS.h_addr_list^[2];
  397. s_b4 := remotehostS.h_addr_list^[3];
  398. end;
  399. end;
  400. end;
  401. except
  402. ;
  403. end;
  404. if RemoteAddress.sin_addr.S_addr <> 0 then
  405. Result := True;
  406. if not Result then
  407. SetupLastError;
  408. end;
  409. function TCnUDP.SendStream(DataStream: TStream; BroadCast: Boolean): Boolean;
  410. var
  411. Buff: Pointer;
  412. begin
  413. GetMem(Buff, DataStream.Size);
  414. try
  415. DataStream.Position := 0;
  416. DataStream.Read(Buff^, DataStream.Size);
  417. Result := SendBuffer(Buff, DataStream.Size, BroadCast);
  418. finally
  419. FreeMem(Buff);
  420. end;
  421. end;
  422. function TCnUDP.SendBuffer(Buff: Pointer; Length: Integer;
  423. BroadCast: Boolean): Boolean;
  424. var
  425. Hosts: TStrings;
  426. i: Integer;
  427. function DoSendBuffer(Buff: Pointer; Length: Integer; Host: string): Boolean;
  428. var
  429. i: Integer;
  430. begin
  431. Result := False;
  432. try
  433. if not ResolveRemoteHost(Host) then
  434. Exit;
  435. RemoteAddress.sin_family := AF_INET;
  436. RemoteAddress.sin_port := htons(FRemotePort);
  437. i := SizeOf(RemoteAddress);
  438. if WinSock.sendto(ThisSocket, Buff^, Length, 0, RemoteAddress, i)
  439. <> SOCKET_ERROR then
  440. Result := True
  441. else
  442. SetupLastError;
  443. except
  444. SetupLastError;
  445. end;
  446. end;
  447. begin
  448. if BroadCast then
  449. begin
  450. Result := False;
  451. Hosts := TStringList.Create;
  452. try
  453. GetBroadCastAddress(Hosts);
  454. for i := 0 to Hosts.Count - 1 do
  455. if DoSendBuffer(Buff, Length, Hosts[i]) then
  456. Result := True;
  457. finally
  458. Hosts.Free;
  459. end;
  460. end
  461. else
  462. Result := DoSendBuffer(Buff, Length, FRemoteHost);
  463. end;
  464. function TCnUDP.GetQueueCount: Integer;
  465. begin
  466. Result := Queue.Count;
  467. end;
  468. procedure TCnUDP.FreeQueueItem(P: Pointer);
  469. var
  470. Rec: PRecvDataRec;
  471. begin
  472. Rec := PRecvDataRec(P);
  473. Rec.FromIP := '';
  474. FreeMem(Rec.Buff);
  475. FreeMem(Rec);
  476. end;
  477. procedure TCnUDP.ClearQueue;
  478. var
  479. Rec: PRecvDataRec;
  480. begin
  481. while Queue.Count > 0 do
  482. begin
  483. Rec := Queue.Pop;
  484. FreeQueueItem(Rec);
  485. end;
  486. end;
  487. procedure TCnUDP.ProcessQueue;
  488. var
  489. Rec: PRecvDataRec;
  490. begin
  491. if Procing then Exit;
  492. Procing := True;
  493. try
  494. while Queue.Count > 0 do
  495. begin
  496. Rec := Queue.Pop;
  497. if Assigned(FOnDataReceived) then
  498. FOnDataReceived(Self, Rec.Buff, Rec.BuffSize, string(Rec.FromIP), Rec.FromPort);
  499. FreeQueueItem(Rec);
  500. end;
  501. finally
  502. Procing := False;
  503. end;
  504. end;
  505. procedure TCnUDP.ProcessRecv;
  506. var
  507. Unicode: Boolean;
  508. MsgExists: Boolean;
  509. Msg: TMsg;
  510. begin
  511. Unicode := IsWindowUnicode(FSocketWindow);
  512. if Unicode then
  513. MsgExists := PeekMessageW(Msg, FSocketWindow, 0, 0, PM_REMOVE)
  514. else
  515. MsgExists := PeekMessageA(Msg, FSocketWindow, 0, 0, PM_REMOVE);
  516. if MsgExists then
  517. begin
  518. if Msg.Message <> WM_QUIT then
  519. begin
  520. TranslateMessage(Msg);
  521. if Unicode then
  522. DispatchMessageW(Msg)
  523. else
  524. DispatchMessageA(Msg);
  525. end;
  526. end;
  527. end;
  528. procedure TCnUDP.WndProc(var Message: TMessage);
  529. begin
  530. if FListening then
  531. begin
  532. with Message do
  533. begin
  534. if Msg = WM_ASYNCHRONOUSPROCESS then
  535. begin
  536. if LParamLo = FD_READ then
  537. begin
  538. ProcessIncomingdata;
  539. if not Procing then
  540. ProcessQueue;
  541. end
  542. else
  543. begin
  544. Wait_Flag := True;
  545. if LParamHi > 0 then
  546. Succeed := False
  547. else
  548. Succeed := True;
  549. end;
  550. SetEvent(EventHandle);
  551. end
  552. else
  553. Result := DefWindowProc(FSocketWindow, Msg, WParam, LParam);
  554. end;
  555. end;
  556. end;
  557. procedure TCnUDP.ProcessIncomingdata;
  558. var
  559. from: TSockAddr;
  560. i: Integer;
  561. Rec: PRecvDataRec;
  562. IBuffSize: Integer;
  563. begin
  564. i := SizeOf(from);
  565. if FRecvBuf = nil then
  566. GetMem(FRecvBuf, FRecvBufSize);
  567. IBuffSize := WinSock.recvfrom(ThisSocket, FRecvBuf^, FRecvBufSize, 0, from, i);
  568. if (IBuffSize > 0) and Assigned(FOnDataReceived) then
  569. begin
  570. GetMem(Rec, SizeOf(TRecvDataRec));
  571. ZeroMemory(Rec, SizeOf(TRecvDataRec));
  572. Rec.FromIP := ShortString(Format('%d.%d.%d.%d', [Ord(from.sin_addr.S_un_b.S_b1),
  573. Ord(from.sin_addr.S_un_b.S_b2), Ord(from.sin_addr.S_un_b.S_b3),
  574. Ord(from.sin_addr.S_un_b.S_b4)]));
  575. Rec.FromPort := ntohs(from.sin_port);
  576. GetMem(Rec.Buff, IBuffSize);
  577. Rec.BuffSize := IBuffSize;
  578. CopyMemory(Rec.Buff, FRecvBuf, IBuffSize);
  579. Queue.Push(Rec);
  580. end;
  581. end;
  582. procedure WaitforSync(Handle: THandle);
  583. begin
  584. repeat
  585. if MsgWaitForMultipleObjects(1, Handle, False, INFINITE, QS_ALLINPUT)
  586. = WAIT_OBJECT_0 + 1 then
  587. Application.ProcessMessages
  588. else
  589. Break;
  590. until False;
  591. end;
  592. procedure TCnUDP.Wait;
  593. begin
  594. WaitforSync(EventHandle);
  595. ResetEvent(EventHandle);
  596. end;
  597. procedure TCnUDP.SetupLastError;
  598. begin
  599. FLastError := WSAGetLastError;
  600. end;
  601. procedure TCnUDP.SockCleanup;
  602. begin
  603. if FSockCount > 0 then
  604. begin
  605. Dec(FSockCount);
  606. if FSockCount = 0 then
  607. WSACleanup;
  608. end;
  609. end;
  610. function TCnUDP.SockStartup: Boolean;
  611. var
  612. wsaData: TWSAData;
  613. begin
  614. if FSockCount = 0 then
  615. begin
  616. Result := WSAStartup($0101, wsaData) = 0;
  617. if not Result then
  618. Exit;
  619. end;
  620. Inc(FSockCount);
  621. Result := True;
  622. end;
  623. function TCnUDP.GetLocalHost: string;
  624. var
  625. p: PHostEnt;
  626. s: array[0..256] of AnsiChar;
  627. begin
  628. SockStartup;
  629. try
  630. GetHostName(@s, 256);
  631. p := GetHostByName(@s);
  632. Result := string(inet_ntoa(PInAddr(p^.h_addr_list^)^));
  633. finally
  634. SockCleanup;
  635. end;
  636. end;
  637. procedure TCnUDP.SetRecvBufSize(const Value: Cardinal);
  638. begin
  639. if FRecvBufSize <> Value then
  640. begin
  641. FRecvBufSize := Value;
  642. if FRecvBuf <> nil then
  643. begin
  644. // 释放,等待下次需要时重新分配
  645. FreeMem(FRecvBuf);
  646. FRecvBuf := nil;
  647. end;
  648. end;
  649. end;
  650. procedure TCnUDP.SetUDPRecvBufSize(const Value: Cardinal);
  651. var
  652. Data: DWORD;
  653. begin
  654. FUDPRecvBufSize := Value;
  655. if FListening then
  656. begin
  657. Data := FUDPRecvBufSize;
  658. WinSock.setsockopt(ThisSocket, SOL_SOCKET, SO_RCVBUF,
  659. PAnsiChar(@Data), SizeOf(Data));
  660. end;
  661. end;
  662. procedure TCnUDP.SetUDPSendBufSize(const Value: Cardinal);
  663. var
  664. Data: DWORD;
  665. begin
  666. FUDPSendBufSize := Value;
  667. if FListening then
  668. begin
  669. Data := FUDPSendBufSize;
  670. WinSock.setsockopt(ThisSocket, SOL_SOCKET, SO_SNDBUF,
  671. PAnsiChar(@Data), SizeOf(Data));
  672. end;
  673. end;
  674. end.