Как говорили другие в ответ на ваш вопрос, TCP - это не протокол, ориентированный на сообщения, а потоковый. Я покажу вам, как писать и читать на очень простом эхо-сервере (это слегка измененная версия сервера, который я сделал на этой неделе, чтобы ответить на другой вопрос):
Метод 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;
Все, что пользователь записывает в форму, будет отправлено на сервер, а затем прочитано оттуда и добавлено в memo1. Если фокус ввода уже находится в Memo1, вы увидите каждый символ дважды, один с клавиатуры, а другой с сервера.
Итак, чтобы написать простой клиент, который получает информацию с сервера, вы должны знать, чего ожидать от сервера. Это строка? несколько строк? Integer? массив? бинарный файл? закодированный файл? Есть ли метка для конца соединения? Обычно это определяется протоколом или вами, если вы создаете пользовательскую пару сервер / клиент.
Написать общий TCP без предварительного знания того, что получить от сервера, возможно, но сложно из-за того, что в протоколе нет общей абстракции сообщений на этом уровне.
Не смущайтесь тем фактом, что транспортных сообщений , но один ответ сервера можно разбить на несколько транспортных сообщений , а затем пересобрать клиентскую часть вашего приложения. не контролируйте это. С точки зрения приложения, сокет - это поток (поток) входящих байтов. То, как вы интерпретируете это как сообщение, команду или любой ответ от сервера, зависит от вас. То же самое относится и к стороне сервера ... например, событие onExecute - это белый лист, где у вас тоже нет абстракции сообщения.
Возможно, вы смешиваете абстракцию сообщений с абстракцией команды ... в протоколе, основанном на командах, клиент отправляет строки, содержащие команды, а сервер отвечает строками, содержащими ответы (тогда, вероятно, больше данных). Посмотрите на компоненты TIdCmdTCPServer / Client.
EDIT
В комментариях ОП заявляет, что он / она хочет сделать эту работу в потоке, я не уверен в том, что проблема, с которой он / она сталкивается с этим, но я добавляю пример потока. Сервер такой же, как показано выше, только клиентская часть для этого простого сервера:
Во-первых, класс потока, который я использую:
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;