架構概覽
- 協定:UDP (無連線、低延遲、支援廣播)。
- 元件:Indy
TIdUDPClient / TIdUDPServer (舊版為 TNMUDP)。
- 通訊埠:雙方同一 Port (預設 8888)。
- 訊息格式:
電腦名: 內容,廣播送給列表所有線上使用者,私訊僅送給選取目標。
關鍵程式碼結構
// 發送訊息 (廣播或私訊)
procedure TForm1.SendChat(const Text: string; Broadcast: Boolean);
var
Stream: TMemoryStream;
i: Integer;
begin
if Text = '' then Exit;
Stream := TMemoryStream.Create;
try
Stream.WriteBuffer(Text[1], Length(Text));
if Broadcast then
begin
for i := 1 to ListBoxUsers.Items.Count - 1 do // 索引 0 為「所有人」
begin
IdUDPClient1.Host := ListBoxUsers.Items[i];
IdUDPClient1.Send(Stream);
end;
end
else
begin
IdUDPClient1.Host := ListBoxUsers.Items[ListBoxUsers.ItemIndex];
IdUDPClient1.Send(Stream);
end;
finally
Stream.Free;
end;
end;
// 接收訊息 (OnUDPRead 事件)
procedure TForm1.IdUDPClient1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
var
S: string;
begin
SetLength(S, AData.Size);
AData.ReadBuffer(S[1], AData.Size);
MemoChat.Lines.Add(S); // 直接顯示「電腦名: 內容」
end;
使用者列表管理
- 啟動時取得本機電腦名稱 (
GetComputerName) 加入列表,並加入「所有人」項目。
- 提供「新增使用者」輸入 IP/電腦名,「移除使用者」從列表刪除 (不可刪除「所有人」)。
完整表單單元 (精簡版)
unit ChatMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdUDPServer;
type
TFormChat = class(TForm)
IdUDPClient1: TIdUDPClient;
IdUDPServer1: TIdUDPServer;
ListBoxUsers: TListBox;
MemoChat: TMemo;
EditMsg: TEdit;
btnSend: TButton;
btnBroadcast: TButton;
btnAddUser: TButton;
btnRemoveUser: TButton;
procedure FormCreate(Sender: TObject);
procedure btnSendClick(Sender: TObject);
procedure btnBroadcastClick(Sender: TObject);
procedure btnAddUserClick(Sender: TObject);
procedure btnRemoveUserClick(Sender: TObject);
procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
private
procedure SendMsg(const Text: string; Broadcast: Boolean);
end;
var
FormChat: TFormChat;
implementation
{$R *.dfm}
procedure TFormChat.FormCreate(Sender: TObject);
var
NameBuf: array[0..255] of Char;
Size: DWORD;
begin
Size := Length(NameBuf);
GetComputerName(NameBuf, Size);
ListBoxUsers.Items.Add('所有人');
ListBoxUsers.Items.Add(StrPas(NameBuf));
ListBoxUsers.ItemIndex := 0;
IdUDPServer1.DefaultPort := 8888;
IdUDPServer1.Active := True;
IdUDPClient1.Port := 8888;
end;
procedure TFormChat.SendMsg(const Text: string; Broadcast: Boolean);
var
Stream: TMemoryStream;
i: Integer;
begin
if Trim(Text) = '' then Exit;
Stream := TMemoryStream.Create;
try
Stream.WriteBuffer(Text[1], Length(Text));
if Broadcast then
begin
for i := 1 to ListBoxUsers.Items.Count - 1 do
begin
IdUDPClient1.Host := ListBoxUsers.Items[i];
IdUDPClient1.Send(Stream);
Stream.Position := 0; // 重置位置供下次發送
end;
end
else if ListBoxUsers.ItemIndex > 0 then
begin
IdUDPClient1.Host := ListBoxUsers.Items[ListBoxUsers.ItemIndex];
IdUDPClient1.Send(Stream);
end;
finally
Stream.Free;
end;
EditMsg.Clear;
EditMsg.SetFocus;
end;
procedure TFormChat.btnSendClick(Sender: TObject);
begin
SendMsg(Format('%s: %s', [ListBoxUsers.Items[1], EditMsg.Text]), False);
end;
procedure TFormChat.btnBroadcastClick(Sender: TObject);
begin
SendMsg(Format('%s: %s', [ListBoxUsers.Items[1], EditMsg.Text]), True);
end;
procedure TFormChat.btnAddUserClick(Sender: TObject);
var
S: string;
begin
S := InputBox('新增使用者', '輸入電腦名或 IP', '');
if S <> '' then ListBoxUsers.Items.Add(S);
end;
procedure TFormChat.btnRemoveUserClick(Sender: TObject);
begin
if ListBoxUsers.ItemIndex > 0 then
ListBoxUsers.Items.Delete(ListBoxUsers.ItemIndex);
end;
procedure TFormChat.IdUDPServer1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
var
S: string;
begin
SetLength(S, AData.Size);
AData.ReadBuffer(S[1], AData.Size);
MemoChat.Lines.Add(S);
end;
end.
部署注意事項
- 所有參與聊天的電腦需在同一區域網路,且防火牆開放 UDP 8888。
- 若跨網段,需路由器開啟 UDP 廣播轉發或改用伺服器轉發架構 (TCP/HTTP)。
- Indy 10+ 版本請使用
TIdUDPClient/TIdUDPServer,舊版 TNMUDP 已棄用。