Delphi2009的Indy全接触之UDP篇

首先新建服务端。

如下图所示建立工程:

 

代码如下:

  1. unit ServerUnit;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer, StdCtrls,
  6.   IdSocketHandle, IdGlobal;
  7. type
  8.   TServerForm = class(TForm)
  9.     Label1: TLabel;
  10.     Edit1: TEdit;
  11.     Label2: TLabel;
  12.     Edit2: TEdit;
  13.     Label3: TLabel;
  14.     Edit3: TEdit;
  15.     IdUDPServer1: TIdUDPServer;
  16.     procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; AData: TBytes;
  17.       ABinding: TIdSocketHandle);
  18.     procedure FormCreate(Sender: TObject);
  19.   private
  20.     { Private declarations }
  21.   public
  22.     { Public declarations }
  23.   end;
  24. var
  25.   ServerForm: TServerForm;
  26. implementation
  27. {$R *.dfm}
  28. procedure TServerForm.FormCreate(Sender: TObject);
  29. begin
  30.   IdUDPServer1.DefaultPort := 3030;
  31.   IdUDPServer1.Active := True;
  32.   Edit1.ReadOnly := True;
  33.   Edit2.ReadOnly := True;
  34.   Edit3.ReadOnly := True;
  35. end;
  36. procedure TServerForm.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
  37.   AData: TBytes; ABinding: TIdSocketHandle);
  38. begin
  39.   Edit1.Text := ABinding.PeerIP;
  40.   Edit2.Text := IntToStr(ABinding.PeerPort);
  41.   Edit3.Text := BytesToString(AData);
  42.   IdUDPServer1.Send(ABinding.PeerIP, ABinding.PeerPort, TimeToStr(Time) + ' => Server received the message!');
  43. end;
  44. end.

然后新建客户端如下图所示:

注意:为了让客户端能实时监控服务端发回来的确定消息,使用了TTimer控件,Interval设置为250毫秒。

 

代码如下:

  1. unit ClientUnit;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent, IdUDPBase,
  6.   IdUDPClient;
  7. type
  8.   TClientForm = class(TForm)
  9.     Label1: TLabel;
  10.     Edit1: TEdit;
  11.     Button1: TButton;
  12.     Label2: TLabel;
  13.     Edit2: TEdit;
  14.     IdUDPClient1: TIdUDPClient;
  15.     Timer1: TTimer;
  16.     procedure Button1Click(Sender: TObject);
  17.     procedure FormCreate(Sender: TObject);
  18.     procedure Timer1Timer(Sender: TObject);
  19.   private
  20.     { Private declarations }
  21.   public
  22.     { Public declarations }
  23.   end;
  24. var
  25.   ClientForm: TClientForm;
  26. implementation
  27. {$R *.dfm}
  28. procedure TClientForm.Button1Click(Sender: TObject);
  29. begin
  30.   IdUDPClient1.Broadcast(Edit1.Text, 3030);
  31.   Timer1.Enabled := True;
  32. end;
  33. procedure TClientForm.FormCreate(Sender: TObject);
  34. begin
  35.   Timer1.Enabled := False;
  36.   Edit2.ReadOnly := True;
  37. end;
  38. procedure TClientForm.Timer1Timer(Sender: TObject);
  39. begin
  40.   Edit2.Text := IdUDPClient1.ReceiveString(-1);
  41.   Timer1.Enabled := False;
  42. end;
  43. end.

如此实现简单的UDP协议下局域网通讯。

===================================================

但是很快我又发现,上面的代码在本机虽然能够正常执行,分别位于2台电脑时会发生无法接收服务器响应的现象。

究其原因可能是客户程序用户界面“冻结”的缘故。

于是在服务端和客户端都加上TIdAntiFreeze以解决上面发生的现象(尤其是客户端)。

服务端:

客户端:

事实上,上述客户端的TTimer控件是完全没有必要使用的。

当我们对IdUDPClient使用Broadcast方法后,一旦与服务端通讯成功,IdUDPClient自动会得到来自服务端的返回值。

如果通讯失败,在IdUDPClient.ReceiveTimeout所设定的事件后终止响应,所以我们可以吧延迟事件稍微设长一点。

于是客户端代码可以简单的写成:

  1. unit ClientUnit;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent, IdUDPBase,
  6.   IdUDPClient, IdAntiFreezeBase, IdAntiFreeze;
  7. type
  8.   TClientForm = class(TForm)
  9.     Label1: TLabel;
  10.     Edit1: TEdit;
  11.     Button1: TButton;
  12.     Label2: TLabel;
  13.     Edit2: TEdit;
  14.     IdUDPClient1: TIdUDPClient;
  15.     IdAntiFreeze1: TIdAntiFreeze;
  16.     procedure Button1Click(Sender: TObject);
  17.     procedure FormCreate(Sender: TObject);
  18.   private
  19.     { Private declarations }
  20.   public
  21.     { Public declarations }
  22.   end;
  23. var
  24.   ClientForm: TClientForm;
  25. implementation
  26. {$R *.dfm}
  27. procedure TClientForm.Button1Click(Sender: TObject);
  28. begin
  29.   IdUDPClient1.Broadcast(Edit1.Text, 3030);
  30.   Edit2.Text := IdUDPClient1.ReceiveString(-1);
  31. end;
  32. procedure TClientForm.FormCreate(Sender: TObject);
  33. begin
  34.   IdUDPClient1.ReceiveTimeout := 5000;
  35.   Edit2.ReadOnly := True;
  36. end;
  37. end.

如此即可完成与服务端的通讯。

=======================================================================

关于网络中查找服务器主机的问题,似乎可以使用UDP广播的方式查找。

先看代码:

  1. unit ClientUnit;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent, IdUDPBase,
  6.   IdUDPClient, IdAntiFreezeBase, IdAntiFreeze;
  7. type
  8.   TClientForm = class(TForm)
  9.     Label1: TLabel;
  10.     Edit1: TEdit;
  11.     Button1: TButton;
  12.     Label2: TLabel;
  13.     IdUDPClient1: TIdUDPClient;
  14.     IdAntiFreeze1: TIdAntiFreeze;
  15.     ListBox1: TListBox;
  16.     Timer1: TTimer;
  17.     procedure Button1Click(Sender: TObject);
  18.     procedure FormCreate(Sender: TObject);
  19.     procedure Timer1Timer(Sender: TObject);
  20.   private
  21.     { Private declarations }
  22.     var IpList : TStringList;
  23.   public
  24.     { Public declarations }
  25.   end;
  26. var
  27.   ClientForm: TClientForm;
  28. implementation
  29. {$R *.dfm}
  30. procedure TClientForm.Button1Click(Sender: TObject);
  31. begin
  32.   IdUDPClient1.Broadcast(Edit1.Text, 3030);
  33.   ListBox1.Items.Add(IdUDPClient1.ReceiveString());
  34. end;
  35. procedure TClientForm.FormCreate(Sender: TObject);
  36. begin
  37.   IdUDPClient1.ReceiveTimeout := 10000;
  38.   IpList := TStringList.Create;
  39. end;
  40. procedure TClientForm.Timer1Timer(Sender: TObject);
  41. var ipaddr : string;
  42. var I : Integer;
  43. begin
  44.   IdUDPClient1.BroadcastEnabled := True;
  45.   IdUDPClient1.Broadcast('SearchHost', 3030);
  46.   ipaddr := IdUDPClient1.ReceiveString();
  47.   if IpList.IndexOf(ipaddr) = -1 then
  48.     IpList.Add(ipaddr);
  49.   ListBox1.Clear;
  50.   for I := 0 to IpList.Count - 1 do
  51.     ListBox1.Items.Add(IpList.Strings[I]);
  52. end;
  53. end.

这样在IpList中就会不断的有主机的IP地址被加入进来了。

但是实际情况是这样的:

由于使用了TTimer控件,我这里设置了Interval:5000,如果设置过小会因为线程大量占用而严重影响主程序正常工作,不知道有什么办法来解决这个问题。还有,当网络中没有服务器的任何响应时客户端也会出现假死现象,不知如何解决。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值