Fórum Chat Client-Server com Indy: Envio de texto com várias linha #322607
31/05/2006
0
Antes de mais nada, li vários tópicos sobre os componentes IdTcpClient e IdTcpServer, bem como todos os outros objetos referentes à troca de informações entre cliente e servidor TCP utilizando os componentes Indy, mas sempre me deparo com soluções não muito convenientes.
A minha intenção é criar um sistema de comunicação interna entre os meus usuários, como o msn, jabber, icq e afins, onde o usuário deve visualizar os contatos online, saber se as mensagens foram entregues e outras funcionalidades que não precisam ser discutidas, diminuindo um pouco o tópico.
Depois de analizar algumas soluções teóricas, decidi optar por mensagens no formato xml por sua facilidade de manipulação, onde a mesma pode ser demonstrada assim:
<mensagem> <id></id> <tipo></tipo> <origem></origem> <destino></destino> <texto></texto> </mensagem>
onde a id é um vetor aleatório e o texto é o conteúdo de um memo, podendo conter várias linhas.
No envio de uma única linha, de um edit qualquer por exemplo, tudo funciona bem. Neste caso onde o memo pode conter uma ou mais e o seu tamanho é variável, o servidor lê cada linha separadamente - dificultando assim a leitura, no lado server, do xml.
Alguém poderia me dar uma luz sobre qual seria a melhor solução a tomar e, se possível, um pequeno exemplo ilustrativo? Abaixo também seguem os códigos (incompletos) do client e server para possíveis correções.
*** CLIENTE
TThreadLeitura = class(TThread)
protected
FConn: TIdTCPConnection;
procedure Execute; override;
public
constructor Create(AConn: TIdTCPConnection); reintroduce;
end;
var
Form2: TForm2;
tl: TThreadLeitura = nil;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
XMLDoc1.Active := True;
XMLDoc1.DocumentElement.ChildValues[´texto´] := Memo2.Lines.Text;
XMLDoc1.DocumentElement.ChildValues[´origem´] := ´origem´;
XMLDoc1.DocumentElement.ChildValues[´destino´] := ´destino´;
IDTCPC1.IOHandler.Write(XMLDoc1.XML);
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
IDTCPC1.Connect;
end;
procedure TForm2.IDTCPC1Connected(Sender: TObject);
begin
tl := TThreadLeitura.Create(IDTCPC1);
end;
constructor TThreadLeitura.Create(AConn: TIdTCPConnection);
begin
FConn := AConn;
inherited Create(False);
end;
procedure TThreadLeitura.Execute;
begin
while not Terminated and FConn.Connected do
begin
Form2.Memo1.Lines.add(FConn.IOHandler.ReadLn);
end;
end;
*** SERVER procedure TForm1.IDTCPS1Execute(AContext: TIdContext); var linha: string; begin linha := AContext.Connection.IOHandler.ReadLn; Memo1.Lines.add(linha + ´ *** unica linha´); //para identificar as quebras AContext.Connection.IOHandler.WriteLn(linha); end;
Até mais!
Danilo Christiano
Curtir tópico
+ 0Posts
31/05/2006
Danilo Christiano
No primeiro post do tópico, depois de uma releitura, percebi que não fui claro o suficiente.
Minha questão é relativamente simples, mas como eu nao conheço bem os componentes que utilizo, travo em algumas linhas de código.
1 - Quero enviar do cliente para o servidor uma mensagem xml, contendo alguns campos com mais de uma linha, o que dificulta a utilização do WriteLn.
2 - Quero receber o xml para no próprio servidor manipular o conteúdo de acordo com minhas necessidades.
Creio que o WriteLn poderia ser utilizado, mas tenho receio de que as quebras de linha dentro do evento OnExecute possam desfigurar o xml. Fiz vários testes enviando e recebendo streams e buffer, mas não obtive sucesso por não conhecer nenhum exemplo prático.
Caso alguém tenha a reposta, agradeceria qualquer post!
Até mais!
Gostei + 0
31/05/2006
Massuda
// cliente type TThreadLeitura = class(TThread) private FString: string; // a linha recebida procedure AtualizarMemo; ... end; procedure TForm2.Button1Click(Sender: TObject); var I: Integer; begin XMLDoc1..... for I := 0 to XMLDoc1.XML.Count - 1 do begin IDTCPC1.IOHandler.WriteLn(XMLDoc1.XML[I]); end; end; procedure TThreadLeituraAtualizarMemo; begin Form2.Memo1.Lines.add(FString); end; procedure TThreadLeitura.Execute; var LendoMensagem: Boolean; S: string; begin LendoMensagem := False; while not Terminated and FConn.Connected do begin S := FConn.IOHandler.ReadLn; if not LendoMensagem then begin LendoMensagem := S = ´<mensagem>´ end; if LendoMensagem then begin FString := S; Synchronize(AtualizaMemo); LendoMensagem := S <> ´</mensagem>´ end; end; end; // servidor procedure TForm1.IDTCPS1Execute(AContext: TIdContext); begin ... o código é parecido com TThreadLeitura.Execute ... end;
Uma coisa importante é que, quando você usa multithread, você deve usar Synchronize para uma thread poder interagir com o Windows.
Gostei + 0
01/06/2006
Danilo Christiano
Obrigado pela dica, mas cheguei ao seguinte código para ler o xml e acredito ser um pouco mais prático:
*** CLIENTE procedure TForm2.Button1Click(Sender: TObject); begin XMLDoc1.Active := True; XMLDoc1.DocumentElement.ChildValues[´texto´] := Memo2.Lines.Text; XMLDoc1.DocumentElement.ChildValues[´origem´] := ´origem´; XMLDoc1.DocumentElement.ChildValues[´destino´] := ´destino´; IDTCPC1.IOHandler.Write(XMLDoc1.XML, true); end;
*** SERVIDOR procedure TForm1.IDTCPS1Execute(AContext: TIdContext); var Linhas: TStrings; begin Linhas := TStringList.Create; AContext.Connection.IOHandler.ReadStrings(Linhas); XMLDoc1.XML := Linhas; XMLDoc1.Active := True; Memo1.Lines.Add(´De: ´+ XMLDoc1.DocumentElement.ChildValues[´origem´]); Memo1.Lines.Add(´Para: ´+ XMLDoc1.DocumentElement.ChildValues[´destino´]); Memo1.Lines.Add(´Mensagem: ´+ XMLDoc1.DocumentElement.ChildValues[´texto´]); end;
O que faz este código funcionar bem no meu caso são as funções:
Write(AValue: TIdStrings;AWriteLinesCount: Boolean);
e
ReadStrings(ADest: TIdStrings;AReadLinesCount: Integer);
onde eu informo o número de linhas quando envio e leio o número de linhas quando recebo.
De qualquer forma, estou ainda com algumas dúvidas:
1 - Utilizando o TString para armazenar o xml lido, todas as quebras de linha da mensagem são mostradas no memo e em qualquer lugar com um caractere e não há quebra de fato. Acredito que exista alguma função que faça algum encode/decode, mas não sei qual é.
2 - Quanto à thread para atualizar/sincronizar as mensagens e a interface, vi alguns exemplos, mas quase todos incompletos. Alguém possui um código eficiente e saiba onde colocar as funções de sincronia?
Até mais pessoal!
Gostei + 0
01/06/2006
Massuda
No código que postei, a procedure TThreadLeitura.AtualizarMemo (percebi agora que faltou um ponto no exemplo e ficou tudo junto... TThreadLeituraAtualizarMemo) é executada de modo sincronizado.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)