Client/Server Com TIDTCP Server
04/11/2005
0
Estou desenvolvendo um programa client/server usando a biblioteca Indy 10 para comunicação entre si em diferentes máquinas da rede.
Criei uma aplicação servidora e acrescentei um TIDTCPServer e TIDServerIOHandlerStack.
Na aplicação cliente coloquei um TIDTCPClient e um TIDIOHandlerStack.
Consigo conectar com o client normalmente no servidor, fazer uma verificação de login e tudo.
Estou com dois problemas.
O primeiro é que algumas vezes o Server precisa enviar alguma informação para o client através desta conexão, mas com esses componentes do Indy, só consigo enviar informações ao client quando o mesmo solicitar alguma coisa, usando a variável AContext passada como parametro no OnExecute do IDTCPServer. E como a conexão é bloqueante, quando o Client solicita alguma informação do Server, ele fica aguardando essa resposta. Mesmo se eu conseguisse enviar alguma coisa direta do Server sem a solicitação do Client, o mesmo não estaria aguardando por esta informação e o programa não funcionaria como o esperado.
Antes de migrar para Indy, eu usava Sockets direto (TClientSocket e TServerSocket) e conseguia fazer essa comunicação Server->client normalmente.
Alguém tem alguam dica?
Bom, eu pensei em uma maneira mais simples de resolver este problema. Estou tentando fazer o seguinte:
Acrescentei um TIDTCPServer no meu Client e um TIDTCPClient no Server (fiz o inverso) e quando o cliente faz a conexão com o server, envia uma porta e o IP para que o server por sua vez faça uma conexão no programa cliente usando o seu IDTcpServer.
Só que naum estou gostando muito dessa solução. Pra mim está parecendo meio que gambiarra... até porque se eu tiver 1 client conectado tudo bem, mas e quando existirem 50, 100 clientes conectados? Vai pesar muito por causa dessas duas conexões efetuadas??
Alguma sugestão??
Se essa realmente for uma boa alternativa, caio em outro problema, como posso fazer um teste para verificar se essa porta já está sendo usada? Estou usando portas altas, acima de 15.000 mas pode acontecer de já estar sendo usada.
Muito obrigado pela atenção.
Prgdelphi
Posts
04/11/2005
Massuda
A outra solução seria, no programa cliente, usar uma thread para fazer leitura dos dados que chegam do servidor; essa thread tem que ter uma máquina de estados finito (ou qualquer estrutura de controle que você ache conveniente) de modo que você saiba distinguir uma resposta do servidor a um comando enviado pelo cliente de uma mensagem/comando do serivdor para o cliente. Geralmente isso é fácil de implementar para programas simples (chat por exemplo), mas pode ficar muito complexo dependendo do seu protocolo de comunicação. Ficaria algo assim (beeem simplificado)...
type TReadingThread = class(TIdThread) protected FConn: TIdTCPConnection; procedure Run; override; public constructor Create(AConn: TIdTCPConnection); reitroduce; end; .... constructor TReadingThread.Create(AConn: TIdTCPConnection); begin FConn := AConn; inherited Create(False); end; procedure TReadingThread.Run; var Command, Data: String; begin Command := FConn.ReadLn; if Command = ´MESSAGE´ then Data := FConn.ReadLn; ... end; ... type TSeuForm = class... IdTCPClient1: TIdTCPClient; private Reader: TReadingThread; end; procedure TSeuForm.IdTCPClient1Connected(Sender: TObject); begin Reader := TReadingThread.Create(IdTCPClient1); end; procedure TFrmClient.IdTCPClient1Disconnected(Sender: TObject); begin if Reader <> nil then begin Reader.TerminateAndWaitFor; FreeAndNil(Reader); end; end;
Os gurus em Indy recomendam a segunda alternativa, mas pessoalmente prefiro a primeira (a que você adotou) por que ela é mais fácil de manter.
No caso do Indy 10, li que existe um componente chamado TIdCmdTCPClient, que tem uma propriedade CommandHandlers parecido com a que tem no TIdTCPServer.
04/11/2005
Prgdelphi
O TIdCmdServer e TIDCmdClient existem sim no Indy 10, mas também tive alguns problemas com os dois e por isso optei pelo TIDTCPServer e client, usando eles eu primeiro envio o tamanho do Texto ou stream que vou enviar e em seguida envio o mesmo. E o outro lado fica aguardando o término. Até hoje não tive mais problema usando esses componentes, o único inconveniente é este, de naum poder enviar dados do server sem a solicitação do client.
O problema da porta vai ocorrer dos dois lados, pois se o Server vai instanciar um novo componente TIDTCPClient para cada cliente conectado, ele também deverá verificar se a porta não está em uso por outro client.
Ou então uma outra alternativa que eu poderia usar seria instanciar um só TIDTCPClient e usar uma porta única e quando o server precisar enviar alguma informação pro client, ele conecta, envia e desconecta.
O que você acha?
Obrigado.
04/11/2005
Massuda
Se dois clientes em IPs diferentes usarem a mesma porta, não há problema, pois os IPs são diferentes. Se estiverem no mesmo IP, os clientes não poderão usar a mesma porta, já que eles não seriam capazes de ativar seus TIdTCPServer na mesma porta (um deles vai falhar, mas você deveria tratar isso como comentei no final do meu post anterior).
O que eu geralmente faço é ativar sempre o TIdTCPServer do programa cliente quando inicio a comunicação com o servidor, assim posso passar ao servidor qual porta o TIdTCPServer está utilizando de fato.
Clique aqui para fazer login e interagir na Comunidade :)