GARANTIR DESCONTO

Fórum Chat Client-Server com Indy: Envio de texto com várias linha #322607

31/05/2006

0

Olá pessoal!

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

Danilo Christiano

Responder

Posts

31/05/2006

Danilo Christiano

Olá amigos!

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!


Responder

Gostei + 0

31/05/2006

Massuda

Acho que o mais simples é você usar a própria estrutura que o XML impõe a sua mensagem... transmita o XML usando WirteLn e leia tudo que estiver entre <mensagem> e </mensagem>. Ficaria algo assim...
// 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; 
...não é exatamente o código ideal, mas dá para dar uma idéia do que deve ser feito. Parece que você usa Indy 10, como não tenho experiencia com essa versão, pode ser que o código não funcione.

Uma coisa importante é que, quando você usa multithread, você deve usar Synchronize para uma thread poder interagir com o Windows.


Responder

Gostei + 0

01/06/2006

Danilo Christiano

Massuda,

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.
ReadStrings calls ReadLn for the number of iterations indicated in AReadLinesCount...


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!


Responder

Gostei + 0

01/06/2006

Massuda

[quote:fbb13b5eb4=´Danilo Christiano´]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?[/quote:fbb13b5eb4]Você precisa usar TThread.Synchronize sempre que uma thread precisa interagir com a VCL, o que geralmente pode ser traduzido como sempre que você precisa alterar algum elemento visual do programa a partir de uma thread que não seja a thread principal; todo programa tem inicialmente uma thread (a thread principal) que é criada para executar o próprio programa.

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.


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar