如何处理TCPClient中接收到的数据? (德尔福
当我从TCPClient
向TCPServer
发送消息时,将使用服务器中的OnExecute
事件处理它。 现在我想处理客户端收到的消息,但TCPClient
没有任何事件。 所以我必须做一个线程来手动处理它们。 我该怎么做 ?
正如其他人在回答你的问题时所说的,TCP不是一种面向消息的协议,而是一种流。 我将向您展示如何编写和读取一个非常简单的echo服务器(这是我本周做了一个略微修改的服务器来回答其他问题):
服务器的OnExecute方法如下所示:
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
aByte: Byte;
begin
AContext.Connection.IOHandler.Writeln('Write anything, but A to exit');
repeat
aByte := AContext.Connection.IOHandler.ReadByte;
AContext.Connection.IOHandler.Write(aByte);
until aByte = 65;
AContext.Connection.IOHandler.Writeln('Good Bye');
AContext.Connection.Disconnect;
end;
该服务器以欢迎消息开始,然后只读取每个字节的连接字节。 服务器回复相同的字节,直到接收到的字节为65(断开命令)65 = 0x41或$ 41。 服务器然后以一个很好的再见消息结束。
您可以在客户端执行此操作:
procedure TForm3.Button1Click(Sender: TObject);
var
AByte: Byte;
begin
IdTCPClient1.Connect;
Memo1.Lines.Add(IdTCPClient1.IOHandler.ReadLn); //we know there must be a welcome message!
Memo1.Lines.Add('');// a new line to write in!
AByte := 0;
while (IdTCPClient1.Connected) and (AByte <> 65) do
begin
AByte := NextByte;
IdTCPClient1.IOHandler.Write(AByte);
AByte := IdTCPClient1.IOHandler.ReadByte;
Memo1.Lines[Memo1.Lines.Count - 1] := Memo1.Lines[Memo1.Lines.Count - 1] + Chr(AByte);
end;
Memo1.Lines.Add(IdTCPClient1.IOHandler.ReadLn); //we know there must be a goodbye message!
IdTCPClient1.Disconnect;
end;
下一个字节过程可以是任何你想提供的字节。 例如,要从用户获得输入,可以将表单的KeyPreview设置为true,然后编写OnKeyPress事件处理函数和NextByte函数,如下所示:
procedure TForm3.FormKeyPress(Sender: TObject; var Key: Char);
begin
FCharBuffer := FCharBuffer + Key;
end;
function TForm3.NextByte: Byte;
begin
Application.ProcessMessages;
while FCharBuffer = '' do //if there is no input pending, just waint until the user adds input
begin
Sleep(10);
//this will allow the user to write the next char and the application to notice that
Application.ProcessMessages;
end;
Result := Byte(AnsiString(FCharBuffer[1])[1]); //just a byte, no UnicodeChars support
Delete(FCharBuffer, 1, 1);
end;
用户在表单中写入的任何内容都将被发送到服务器,然后从那里读取并添加到备忘录1中。 如果输入焦点已经在Memo1中,您将看到每个字符两次,一个来自键盘,另一个来自服务器。
所以,为了编写一个从服务器获取信息的简单客户端,你必须知道服务器期望什么。 它是一个字符串吗? 多个字符串? 整数? 阵列? 一个二进制文件? 编码文件? 连接结束有没有标记? 如果你正在创建一个自定义的服务器/客户端对,这些东西通常在协议中或由你来定义。
在没有事先知道从服务器获得什么的情况下编写通用的TCP是可能的,但是由于在协议的这个级别没有通用的消息抽象这一事实而变得复杂。
不要因为传输消息而感到困惑,但是单个服务器响应可以拆分为多个传输消息,然后重新组装客户端,您的应用程序不会控制这个。 从应用程序角度来看,套接字是传入字节的流(流)。 您将此解释为消息的方式,来自服务器的命令或任何类型的响应由您决定。 同样是适用的服务器端...例如,onExecute事件是一个白皮书,您也没有消息抽象。
也许你把消息抽象与命令抽象混合在一起:在基于命令的协议上,客户端发送包含命令的字符串,服务器回复包含响应的字符串(然后可能会有更多数据)。 看看TIdCmdTCPServer / Client组件。
编辑
在评论中,OP表示他/她希望在一个线程中完成这项工作,但我不确定他/她有什么问题,但我添加了一个线程示例。 服务器与前面显示的相同,只是此简单服务器的客户端部分:
首先,我正在使用的线程类:
type
TCommThread = class(TThread)
private
FText: string;
protected
procedure Execute; override;
//this will hold the result of the communication
property Text: string read FText;
end;
procedure TCommThread.Execute;
const
//this is the message to be sent. I removed the A because the server will close
//the connection on the first A sent. I'm adding a final A to close the channel.
Str: AnsiString = 'HELLO, THIS IS _ THRE_DED CLIENT!A';
var
AByte: Byte;
I: Integer;
Client: TIdTCPClient;
Txt: TStringList;
begin
try
Client := TIdTCPClient.Create(nil);
try
Client.Host := 'localhost';
Client.Port := 1025;
Client.Connect;
Txt := TStringList.Create;
try
Txt.Add(Client.IOHandler.ReadLn); //we know there must be a welcome message!
Txt.Add('');// a new line to write in!
AByte := 0;
I := 0;
while (Client.Connected) and (AByte <> 65) do
begin
Inc(I);
AByte := Ord(Str[I]);
Client.IOHandler.Write(AByte);
AByte := Client.IOHandler.ReadByte;
Txt[Txt.Count - 1] := Txt[Txt.Count - 1] + Chr(AByte);
end;
Txt.Add(Client.IOHandler.ReadLn); //we know there must be a goodbye message!
FText := Txt.Text;
finally
Txt.Free;
end;
Client.Disconnect;
finally
Client.Free;
end;
except
on E:Exception do
FText := 'Error! ' + E.ClassName + '||' + E.Message;
end;
end;
然后,我将这两种方法添加到表单中:
//this will collect the result of the thread execution on the Memo1 component.
procedure TForm3.AThreadTerminate(Sender: TObject);
begin
Memo1.Lines.Text := (Sender as TCommThread).Text;
end;
//this will spawn a new thread on a Create and forget basis.
//The OnTerminate event will fire the result collect.
procedure TForm3.Button2Click(Sender: TObject);
var
AThread: TCommThread;
begin
AThread := TCommThread.Create(True);
AThread.FreeOnTerminate := True;
AThread.OnTerminate := AThreadTerminate;
AThread.Start;
end;
TCP不支持消息。 这是基于流的界面。 因此,不要指望你会在接收器上收到“消息”。 而是从套接字读取传入数据流,并根据您的高级协议对其进行解析。
这里是我用Delphi 7读/写的代码。使用Tcp事件读取。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
Button1: TButton;
ListBox1: TListBox;
Edit1: TEdit;
Edit2: TEdit;
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
UsePort: Integer;
UseHost: String;
begin
UseHost := Edit1.Text;
UsePort := STRTOINT(Edit2.Text);
ClientSocket1.Port := UsePort;
ClientSocket1.Host := UseHost;
ClientSocket1.Active := true;
end;
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
begin
ListBox1.Items.Add(ClientSocket1.Socket.ReceiveText);
end;
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode:=0;
ClientSocket1.Active := False;
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
ClientSocket1.Socket.SendText(Edit1.Text);
end;
end.
链接地址: http://www.djcxy.com/p/76469.html
上一篇: How to handle received data in the TCPClient ? (Delphi
下一篇: Connection Closed Gracefully when IdTCPClient.IOHandler.ReadStream()!