【发布时间】:2018-08-07 21:26:15
【问题描述】:
我需要编写一个简单的聊天程序,供一些客户使用。基本上,有很多客户端连接到服务器并一起聊天。服务器工作:
如果需要,这里的代码:
//CONNECT TO THE SERVER
procedure TFormServer.ButtonStartClick(Sender: TObject);
begin
if not TCPServer.Active then
begin
try
TCPServer.DefaultPort := 8002;
TCPServer.Bindings[0].IP := LIP.Text;
TCPServer.Bindings[0].Port := StrToInt(LPort.Text);
TCPServer.MaxConnections := 5;
TCPServer.Active := true;
Memo1.Lines.Add(TimeNow + 'Server started.');
except
on E: Exception do
Memo1.Lines.Add(sLineBreak + ' ====== INTERNAL ERROR ====== ' +
sLineBreak + ' > ' + E.Message + sLineBreak);
end;
end;
end;
//DISCONNECT
procedure TFormServer.ButtonStopClick(Sender: TObject);
begin
if TCPServer.Active then
begin
TCPServer.Active := false;
Memo1.Lines.Add(TimeNow + 'Server stopped.');
end;
end;
//IF CLOSE THE APP DONT FORGET TO CLOSE SERVER!!
procedure TFormServer.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ButtonStopClick(Self);
end;
procedure TFormServer.FormCreate(Sender: TObject);
begin
FClients := 0;
end;
//When a client connects I write a log
procedure TFormServer.TCPServerConnect(AContext: TIdContext);
begin
Inc(FClients);
TThread.Synchronize(nil, procedure
begin
LabelCount.Text := 'Connected sockets: ' + FClients.ToString;
Memo1.Lines.Add(TimeNow + ' Client connected @ ' + AContext.Binding.IP + ':' + AContext.Binding.Port.ToString);
end);
end;
//Same, when a client disconnects I log it
procedure TFormServer.TCPServerDisconnect(AContext: TIdContext);
begin
Dec(FClients);
TThread.Synchronize(nil, procedure
begin
LabelCount.Text := 'Connected sockets: ' + FClients.ToString;
Memo1.Lines.Add(TimeNow + ' Client disconnected');
end);
end;
//WHAT I DO HERE:
//I receive a message from the client and then I send this message to EVERYONE that is connected here. It is a global chat
procedure TFormServer.TCPServerExecute(AContext: TIdContext);
var
txt: string;
begin
txt := AContext.Connection.IOHandler.ReadLn();
AContext.Connection.IOHandler.WriteLn(txt);
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(TimeNow + txt);
end);
end;
服务器代码非常简单且最少,但它可以满足我的需要。这是客户端:
这里有代码,很简单:
//CONNECT TO THE SERVER
procedure TFormClient.ConnectClick(Sender: TObject);
begin
if Length(Username.Text) < 4 then
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('ERROR: Username must contain at least 4 characters');
Exit;
end;
if not TCPClient.Connected then
begin
try
Username.Enabled := false;
Memo1.Lines.Clear;
TCPClient.Host := '127.0.0.1';
TCPClient.Port := 8002;
TCPClient.ConnectTimeout := 5000;
TCPClient.Connect;
Connect.Text := 'Disconnect';
except
on E: Exception do
Memo1.Lines.Add(' ====== ERROR ======' + sLineBreak +
' > ' + E.Message + sLineBreak);
end;
end
else
begin
TCPClient.Disconnect;
Username.Enabled := true;
Connect.Text := 'Connect';
end;
end;
//IF YOU FORGET TO DISCONNECT WHEN APP IS CLOSED
procedure TFormClient.FormDestroy(Sender: TObject);
begin
if TCPClient.Connected then
TCPClient.Disconnect;
end;
//Here I send a string to the server and it's good
procedure TFormClient.SendClick(Sender: TObject);
begin
if TCPClient.Connected then
begin
TCPClient.IOHandler.WriteLn(Username.Text + ': ' + EditMessage.Text);
EditMessage.Text := '';
end
else
begin
Memo1.Lines.Add('ERROR: You aren''t connected!');
end;
end;
//Problems here
procedure TFormClient.Timer1Timer(Sender: TObject);
begin
Memo1.Lines.Add(TCPClient.IOHandler.ReadLn());
end;
问题始于最后一个过程Timer1Timer。我发现 TCPServer 使用线程,这就是为什么我调用 Synchronize 来更新 UI。相反,TCPClient 不使用线程,我必须手动检查服务器。请看这段代码:
procedure TFormServer.TCPServerExecute(AContext: TIdContext);
var
txt: string;
begin
txt := AContext.Connection.IOHandler.ReadLn();
AContext.Connection.IOHandler.WriteLn(txt);
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(TimeNow + txt);
end);
end;
如您所见,当服务器收到一个字符串时,他立即将其发送回所有客户端。我尝试在这里获取字符串:
procedure TFormClient.Timer1Timer(Sender: TObject);
begin
Memo1.Lines.Add(TCPClient.IOHandler.ReadLn());
end;
怎么了?我在这里看到了一个类似的问题,答案说我必须使用计时器和IOHandler.ReadLn(),这就是我正在做的事情。我认为问题就在这里。如何解决?
还有timer的间隔是200,是不是太短了?
我已阅读 Remy Lebeau 在答案中所说的内容,并生成了以下简单代码:
procedure TFormClient.Timer1Timer(Sender: TObject);
begin
if not(TCPClient.Connected) then
Exit;
if TCPClient.IOHandler.InputBufferIsEmpty then
Exit;
Memo1.Lines.Add(TCPClient.IOHandler.InputBufferAsString());
end;
表单中有一个Timer1 组件。这按我的预期工作,但它仍然可以锁定 UI 吗?
【问题讨论】:
标签: delphi