Cuando envío un mensaje desde TCPClient
a un TCPServer
se manejará usando el evento OnExecute
en el servidor. Ahora quiero manejar los mensajes recibidos en el Cliente pero TCPClient
no tiene ningún evento para esto. Entonces tengo que hacer un hilo para manejarlos manualmente. Cómo puedo hacerlo ?¿Cómo manejar los datos recibidos en TCPClient? (Delphi - Indy)
Respuesta
Como dijeron otros en respuesta a su pregunta, TCP no es un protocolo orientado a mensajes, sino uno de transmisión. Te voy a mostrar cómo escribir y leer a un servidor de eco muy simple (esta es una versión ligeramente modificada de un servidor que hice esta semana para responder a otra pregunta):
El método OnExecute servidor tiene el siguiente aspecto:
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;
Este servidor comienza con un mensaje de bienvenida, luego solo lee el byte de conexión por byte. El servidor responde el mismo byte, hasta que el byte recibido sea 65 (el comando de desconexión) 65 = 0x41 o $ 41. El servidor termina con un mensaje de adiós.
Usted puede hacer esto en un cliente:
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;
El procedimiento siguiente byte puede ser cualquier cosa que desee proporcionar un byte. Por ejemplo, para obtener la entrada del usuario, se puede girar el TeclaDePresentaciónPreliminar de su formulario a la verdadera y escribir un controlador de eventos OnKeyPress y la función NextByte la siguiente manera: Se enviará
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;
Todo lo que el usuario escribe en la forma al servidor y luego leer desde allí y agregar a memo1. Si el foco de entrada ya está en Memo1, verá cada personaje dos veces, uno del teclado y otro del servidor.
Por lo tanto, para escribir un cliente simple que obtiene información de un servidor, debe saber qué esperar del servidor. ¿Es una cadena? múltiples cadenas? ¿Entero? ¿formación? un archivo binario? archivo codificado? ¿Hay una marca para el final de la conexión? Estas cosas generalmente se definen en el protocolo o por usted, si está creando un par personalizado de servidor/cliente.
Escribir un TCP genérico sin saber previamente qué es lo que se obtiene del servidor es posible, pero complejo debido al hecho de que no existe una abstracción de mensaje genérico en este nivel en el protocolo.
No se deje confundir por el hecho de que hay mensajes de transporte, sino una sola respuesta del servidor puede dividirse en varios mensajes de transporte , y el lado del cliente y luego volver a montar, su aplicación no controlan esto. Desde el punto de vista de una aplicación, el socket es un flujo (flujo) de bytes entrantes. La forma de interpretar esto como un mensaje, un comando o cualquier tipo de respuesta del servidor depende de usted. Lo mismo es aplicable al lado del servidor ... por ejemplo, el evento onExecute es una hoja en blanco donde tampoco tiene una abstracción de mensaje.
Quizás esté mezclando la abstracción de mensajes con la abstracción del comando ... en un protocolo basado en comandos, el cliente envía cadenas que contienen comandos y el servidor responde con cadenas que contienen respuestas (entonces probablemente más datos). Eche un vistazo a los componentes TIdCmdTCPServer/Client.
EDITAR
En los comentarios de los Estados OP s/él quiere hacer este trabajo en un hilo, no estoy seguro de lo que es el problema s/él está teniendo con esto, pero estoy añadiendo una ejemplo de hilo El servidor es el mismo que se muestra antes, sólo la parte del cliente de esta simple servidor:
En primer lugar, la clase de hilo que estoy usando:
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;
Entonces, estoy añadiendo estos dos métodos a la formulario:
//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;
Gracias por la respuesta completa. pero quería hacer esto usando Thread. – Kermia
@Kermia, ¿en qué parte de tu pregunta afirmas esto? ¿Qué te hace pensar que no puedes hacer esto en un hilo? – jachguate
Como dije: 'Tengo que hacer un hilo para manejarlos manualmente. ¿Cómo puedo hacerlo? '. porque Indy está trabajando con modo de bloqueo y sin ningún hilo adicional, mi aplicación se congelará. – Kermia
TCP no funciona con mensajes. Esa es una interfaz basada en flujo. En consecuencia, no espere que reciba un "mensaje" en el receptor. En su lugar, lee el flujo de datos entrantes del socket y lo analiza de acuerdo con su protocolo de alto nivel.
Si necesita que el cliente Indy maneje los "mensajes" entrantes (la definición de "mensaje" depende del protocolo utilizado), le recomiendo echar un vistazo a la implementación de TIdTelnet en la unidad protocols \ IdTelnet.
Este componente utiliza un hilo de recepción, basado en un TIdThread, que asíncronamente recibe mensajes del servidor Telnet, y los pasa a una rutina de manejo de mensajes. Si tienes un protocolo similar, este podría ser un buen punto de partida.
Actualización: para ser más específico, el procedure TIdTelnetReadThread.Run;
en IdTelnet.pas es donde ocurre la 'magia' del cliente asíncrono, como se puede ver, utiliza Sincronizar para ejecutar el procesamiento de datos en el hilo principal, pero por supuesto su aplicación podría también haga la gestión de datos en el hilo de recepción, o páselo a un hilo de trabajo para mantener intacto el hilo principal. El procedimiento no utiliza un bucle, porque el bucle/pausa/reinicio se implementa en IdThread.
El título está editado. – Kermia
Aquí está mi código para leer/escribir con Delphi 7. Uso del puerto TCP evento Read.
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.
Agregue TTimer
. Establezca su Interval
en 1
. Escribir en OnTimer
Evento:
procedure TForm1.Timer1Timer(Sender: TObject);
var
s: string;
begin
if not IdTCPClient1.Connected then Exit;
if IdTCPClient1.IOHandler.InputBufferIsEmpty then Exit;
s := IdTCPClient1.IOHandler.InputBufferAsString;
Memo1.Lines.Add('Received: ' + s);
end;
No establezca otra cosa Timer.Interval
1
. Porque, los datos recibidos se eliminan después de unos milisegundos.
- 1. ¿Cómo desconectar TCPClient Totally? (Indy)
- 2. Delphi e Indy con SSL
- 3. Delphi - Indy (IDHTTP) Mantener sesión
- 4. Delphi/Indy IdHttpServer no multiproceso?
- 5. Solicitud de muestra - Amazon S3/Indy/Delphi
- 6. ¿Cómo manejar los datos obsoletos en REST?
- 7. Actualización paso a paso de Indy 10 en Delphi 2009
- 8. error de PHP, pero no los datos recibidos
- 9. Cómo manejar los cambios en los datos duplicados en NoSQL
- 10. Cómo usar correctamente TcpClient ReadTimeout
- 11. Problema con Indy IdHttp Publicar en Delphi 2010
- 12. INDY 10.1.5 - ¿Qué DLL dlls funcionan con Delphi 2006?
- 13. Problemas recibidos en RXTX
- 14. Soporte de SSL para smtp.live.com y TIdSmtp (Indy, Delphi)
- 15. ¿Cómo almacenar en caché los datos recibidos de una llamada Ajax?
- 16. Http Publicar con indy
- 17. WCF: ¿inspeccionar los mensajes enviados/recibidos?
- 18. Cómo usar SSL en la clase TcpClient
- 19. Indy 10 IdTCPClient Leyendo datos usando un hilo separado?
- 20. ¿Es necesario cerrar NetworkStream y TcpClient, o simplemente TcpClient?
- 21. ¿Utiliza los enlaces múltiples de Indy Server como sockets separados?
- 22. Creación de modelo de red troncal utilizando datos JSON recibidos
- 23. Actores: Cómo manejar eficientemente los datos de lectura mayoritaria
- 24. ¿Cómo manejar mejor los archivos de datos con CMake?
- 25. ¿Indy es estable para FreePascal?
- 26. Cómo no manejar datos en JqPlot
- 27. Apache httpcore, servidores simple eco de datos de correos recibidos
- 28. TCPClient vs Socket en C#
- 29. cómo ejecutar javascript en delphi?
- 30. Instale la versión más reciente de Indy 10 en Delphi 2009
¿Has echado un vistazo a los ejemplos de Indy? – BugFinder