olá pessoal, estou com um probleminha…tenho uma aplicacao para pockets pc que utiliza muito comunicacao tcp com um servidor. Tudo dentro de uma rede wireless…
O problema é que a comunicacao estah demorando demais…coisa de 5 segundos pra informacao ir e voltar. Alguem pode me dizer alguma forma de tornar essa comunicacao mais rapida ???
desde jah agradeço
a) Isso é problema de seu protocolo, que deveria ter sido definido para mandar menos mensagens, mas mensagens maiores. Um protocolo do tipo:
Cliente: Oi, tudo bem?
Servidor: Tudo bem, e você?
Cliente: Muito bem. Como vai a família?
Servidor: Mais ou menos. E a sua?
Cliente: Estou sem sorte hoje.
Servidor: Boa sorte então.
Cliente: Você pode me fazer um favor?
Servidor: É pra já!
Cliente: bla bla bla
... etc....
depois de 30 trocas de mensagens...
... etc....
Servidor: OK, então a gente se fala outro dia.
Cliente: Passe outro dia lá em casa!
que funciona razoavelmente bem quando você tem dois computadores potentes em rede local não funciona com redes com alta latência, como redes wireless. Você deveria ter algo como:
Cliente: Quero um Big Mac, com fritas, e salada, para viagem. Vou pagar R$ 10,45 pelo sanduíche, e estou anexando já o dinheiro trocado.
Servidor: Aqui está seu Big Mac prontinho.
Ou seja, algo com o mínimo de trocas de mensagens possível.
b) A opção TCP_NODELAY (não sei exatamente qual é o nome certo que tem de ser usado nas opções de socket) também ajuda, e é a primeira que tem de ser usada para comunicação via rede. É que se você não usar essa opção, o TCP só manda dados pelo socket se passar um timeout, ou se o buffer encher (isso é conhecido como “Nagling”)
off… essa abordagem client/server foi muito boa :lol:
entao, o problema eh que minha aplicacao exige que muitas mensagens sejam trocadas. Nao tem como eu mandar tudo de uma vez pois eh um sistema dinamico…
Alguem sabe de um modo que posso deixar o protocolo TCP do java mais rapido !?!?
sei q existe algumas prorpriedades a serem setadas como:
conexao.setPerformancePreferences(x,y,z);
conexao.setSoLinger()
conexao.setSoTimeout()
conexao.setTcpNoDelay()
jah tentei utilizar todos eles mas nao deu resultado positivo na velocidade de transmissao…tenho quase certeza que nao utilizei da maneira correta…
desde ja agradeço
se ajudar eu to postando meu codigo aqui:
//cliente
public class AcaoProtocolClient {
private ObjectOutputStream output;
private ObjectInputStream input;
private String endereco="localhost";
private Socket cliente;
private int porta = 5000;
public AcaoProtocolClient(String endereco, String porta){
this.endereco = endereco;
this.porta = Integer.parseInt(porta);
}
public void OpenSocket() throws IOException,ClassNotFoundException{
cliente = new Socket(InetAddress.getByName(endereco),porta);
//****************************************************************************
output = new ObjectOutputStream(cliente.getOutputStream() );
input = new ObjectInputStream(cliente.getInputStream() );
//***************************************************************************
}
public void CloseSocket() throws IOException,ClassNotFoundException{
output.close();
cliente.close();
}
public boolean EnviaObjeto(Object object) throws IOException,ClassNotFoundException {
//envia objeto
output.writeObject(object);
output.flush();
return true;
}
public Object RecebeObjeto() throws IOException,ClassNotFoundException {
//Recebe Objeto
Object object;
object = input.readObject();
//retorna objeto recebido
return object;
}
}
//servidor
public class AcaoProtocolServer {
private ObjectOutputStream output;
private ObjectInputStream input;
private ServerSocket server;
private Socket conexao;
private int porta = 5000;
public AcaoProtocolServer(String porta) {
this.porta = Integer.parseInt(porta);
conexao = new Socket();
try {
server = new ServerSocket( this.porta, 30 );
} catch (IOException ex) {ex.printStackTrace();}
}
public void conecta() throws IOException{
//Bloqueante
conexao = server.accept();
//****************************************************************************
//inicializa objetos de fluxo
output = new ObjectOutputStream(conexao.getOutputStream());
input = new ObjectInputStream(conexao.getInputStream());
//***************************************************************************
}
public String returnConectionName(){
return conexao.getInetAddress().getHostName();
}
public boolean EnviaObjeto(Object object) throws IOException,ClassNotFoundException {
//envia objeto
output.writeObject(object);
output.flush();
return true;
}
public Object RecebeObjeto() throws IOException,ClassNotFoundException {
//Recebe Objeto
Object object;
object = input.readObject();
//retorna objeto recebido
return object;
}
public void closeConection() throws IOException{
output.close();
input.close();
conexao.close();
}
}
Não tem mágica. Não adianta mexer só em parametros de rede, se a rede em si é lenta/demorada. O que o thingol falou está 100% correto: para redes com alta latencia, só mudando a arquitetura de comunicação: grandes blocos de dados de transmissão. Outra alternativa seria mudar para um modelo assincrono de comunicação, principalmente se a questão for percepção para o usuário, mas isso pode complicar bastante o seu sistema (mensagens fora de ordem, requests sem response, etc).
Um bom exemplo disto é por exemplo tentar usar Ajax em rede via satelite: a latencia vai “matar” a interatividade do site.
Bem, para o seu caso, acho que vale colocar algum “analisador de rede” (dependo por software ou por hardware) e tentar levantar gargalos, trafego e delays.
ObjectInputStream e ObjectOutputStream não são recomendadas para uso via sockets. Se você não souber o que está fazendo, depois de algum tempo o servidor vai estourar a memória
Isso vale pra qualquer tipo de Stream, não?
Não; esse problema de estourar a memória é com ObjectInputStream/ObjectOutputStream.
Se você olhar o código-fonte, vai entender o que estou falando.
Olá
[quote=thingol]Não; esse problema de estourar a memória é com ObjectInputStream/ObjectOutputStream.
Se você olhar o código-fonte, vai entender o que estou falando.
[/quote]
Thingol, qual é exatamente o problema e o que se pode fazer para contornar?
[]s
Luca
Quando se usa ObjectOutputStream, writeObject deve serializar o objeto, mas não de forma cega: se esse objeto faz referências a outros objetos que já foram serializados e enviados antes, então writeObject deve enviar apenas uma referência ao objeto que já foi antes serializado, para que no outro lado, com ObjectInputStream, seja possível reconstruir o objeto com as referências corretas.
Dessa forma, posso até enviar um objeto com uma referência circular que ObjectOutputStream não vai “entrar em loop”.
Só que para fazer isso tanto ObjectOutputStream quanto ObjectInputStream devem ter uma tabela que associa referências de objetos com objetos enviados (no caso de OOS) ou recebidos (no caso de OIS).
Se essa tabela não for “resetada” de vez em quando, ela sempre crescerá.
Você pode resetar a tal tabela se não tiver objetos complexos (por exemplo, um TO clássico, que contém apenas tipos primitivos e strings, não precisaria ser cadastrado nessa tal tabela.)
(Acho que o método se chama “reset”). Quando você reseta a tabela no lado OOS, ele envia uma pequena mensagem que força o “reset” da tabela no lado OIS.
Um colega nosso teve grandes problemas com esse comportamento de OOS/OIS porque a aplicação caía após enviadas algumas centenas de megabytes de objetos.
De qualquer maneira, o correto em protocolos de comunicação normalmente é:
- Use uma stream menos complicada (como DataInput/DataOutputStream), e trabalhe sempre com mensagens com cabeçalho (contendo o comprimento da mensagem e alguma outra informação que quiser pôr, tal como o tipo da mensagem).
O benefício adicional de mandar o comprimento é que você não precisa ficar efetuando o “parse” da mensagem para ver onde ela termina (imagine, por exemplo, que eu mandasse um XML sem indicação de quantos bytes ele tem. Pode-se até saber onde termina o XML, mas a lógica é bastante complexa.) - Não se esqueça de dar “flush” quando enviar uma mensagem.
- Não se esqueça: arrays de bytes não são strings. Trabalhe sempre com arrays de bytes. Se precisar mandar uma string, converta-a para um array de bytes antes, e depois a desconverta, mas sempre levando em conta que o encoding deve ser explicitamente passado como parâmetro (já que em sistemas operacionais diferentes o encoding padrão é diferente no caso do Java).
muito obrigado de ante mao galera !!
algumas dicas serao uteis para evitar problemas futuros mas resolvi meu problema de outra forma…simplesmente nao fecho o socket em todos os protocolos, somente no login e no logout e deixo o canal aberto durante toda minha aplicacao. A velocidade ficou excelente e o usuario nem percebe que existe uma comunicacao com um servidor acontecendo…vou tentar utilizar das funcoes de “tuning” do TCP para ver se o desempenho melhora mais ainda e depois posto dizendo uke aconteceu…
Ahh…testei em rede windows e linux cabeada e windows wireless, em todas o resultado foi satisfatorio !!!
abracos galera e valeu mesmo !