Olá pessoal, bem estou implementando um sistema clienteXservidor onde eu quero abrir um socket no cliente e mante-lo aberto durante todo o ciclo de vida do aplicativo cliente apenas mandando mensagens para o servidor. Acontece que eu até consigo manter ele aberto porém tudo que eu mando para o servidor só é enviado de fato mesmo quando eu fecho o socket no cliente, mas ai nesse caso eu perco a conexão. Já tentei mandar um ‘\n’ no fim da mensagem mas nesse caso é a mesma coisa que se eu fechasse o socket, tipo o que eu quero mesmo é que ele fique aberto o tempo todo sendo que eu apenas chamaria um método para mandar a mensagem para o servidor.
Eu fiz uns testes com um servidor feito em delphi e nesse caso quando eu dava um in.write(algum byte) seguido de um in.flush(); o servidor mostrava na tela normalmente a mensagem vinda do cliente, mas quando eu fiz isso em um servidor feito em java a coisa não deu certo.
Enfim era essa a minha pergunta, é possível manter esse socket aberto enviando mensagens?
é possível sim, e não há a necessidade de ficar enviando \n nem fechar a conexão. O que deve estar acontecendo é que o SO ou a VM devem estar ‘bufferizando’ a saída do seu socket. Você está dando flush a cada vez que escreve ?
L
laudenpower
Segue o código:
Servidor
packagenetwork;importutil.*;importjava.io.IOException;importjava.net.ServerSocket;importjava.net.Socket;/** * * @author Laudelino Martins Cardoso Neto */publicclassServerMultiponto{privateServerSockets;publicServerMultiponto(){try{s=newServerSocket(20000);processaRequisicao();}catch(IOExceptionioe){ioe.printStackTrace();}}privatevoidprocessaRequisicao(){while(true){try{System.out.println("Esperando requisição...");Socketconexao=s.accept();newThreadServidor(conexao).start();}catch(IOExceptionioe){ioe.printStackTrace();}catch(Exceptione){e.printStackTrace();}}}}//Fim da classe ServerMultipontopackagenetwork;importjava.io.BufferedReader;importjava.io.BufferedWriter;importjava.io.DataInputStream;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.ObjectInputStream;importjava.io.OutputStreamWriter;importjava.net.Socket;/** * * @author Laudelino Martins Cardoso Neto */publicclassThreadServidorextendsThread{privateBufferedReaderleitor;privateSocketsocket;publicThreadServidor(Socketsocket){this.socket=socket;}@Overridepublicvoidrun(){Stringmensagem="";try{leitor=newBufferedReader(newInputStreamReader(socket.getInputStream()));DataInputStreamleitor=newDataInputStream(socket.getInputStream());mensagem=leitor.readLine();System.out.println("Mensagem chegada no servidor: "+mensagem+"\n");socket.close();}catch(IOExceptionioe){ioe.printStackTrace();}catch(ClassNotFoundExceptioncnfe){cnfe.printStackTrace();}}}//Fim da classe ThreadServidor
Cliente
packageutil;importjava.io.DataInputStream;importjava.io.DataOutputStream;importjava.io.IOException;importjava.net.Socket;/** * Classe que manipula um objeto socket que * terá como função enviar dados para o servidor * do terminal de separação do romaneio. * @author Diego Silva */publicclassClientSocket{Socketsocket=null;DataInputStreamin=null;DataOutputStreamout=null;/** * Contrutor de classe que cria um canal de conexão com o servidor */publicClientSocket()throwsException{socket=newSocket("127.0.0.1",20000);in=newDataInputStream(socket.getInputStream());//Receptor da resposta do servidorout=newDataOutputStream(socket.getOutputStream());//Sender da mensagem para o servidor }/** * Envia uma mensagem para o servidor * dos terminais, sendo que nsse caso é uma mensagem * padrão identificando o terminal * @param mensagem */publicvoidsendText(Stringmensagem)throwsException{try{out.write(mensagem.getBytes());out.flush();}catch(Exceptione){System.out.println("Ocorreu um erro no envio da mensagem ao servidor."+e.getMessage());thrownewException("Ocorreu um erro no envio da mensagem ao servidor."+e.getMessage());}}/** * Método que fecha a conexão com o * servidor sendo que nesse caso * fecha também os canais de envio de dados também */publicvoiddesconectar(){try{in.close();out.close();socket.close();}catch(Exceptione){System.out.println("Ocorreu um erro no fechamento do socket: "+e.getMessage());e.printStackTrace();}}/** * Método que envia um dado de sinalização para * verificar conectividade com o servidor */publicvoidenviaDadoSinalizacao(){try{socket.sendUrgentData(1);}catch(IOExceptionioe){ioe.printStackTrace();}}}
Desde já agradeço a atenção de todos.
T
thingol
Você pode mudar um pouco seu protocolo? Pela minha experiência, é muito difícil fazer protocolos baseados em texto funcionar.
Normalmente é aconselhável:
Modificar uma opção DO SOCKET ( dê uma olhada em setTcpNoDelay - você deve chamar esse método imediatamente após abrir o socket , e não depois, com o parâmetro “true” ) para que ele não bufferize a saída :
Criar um protocolo binário que mande pacotes com cabeçalho e dados.
Para receber a mensagem, você
a) Usa DataInputStream, método readShort, para ler o tamanho do array;
b) Vai chamando repetidamente DataInputStream, método read, até receber todos os dados (de acordo com o tamanho) e acumulando os dados em um ByteArrayOutputStream. Quando receber todos os dados (ou quando read retornar -1, indicando que o socket foi fechado), a mensagem foi lida.
T
thingol
thingol:
Você pode mudar um pouco seu protocolo? Pela minha experiência, é muito difícil fazer protocolos baseados em texto funcionar.
Mesmo o HTTP, que é um protocolo baseado em texto, também trabalha com tamanhos de mensagens (o tal do Content-Length).
L
laudenpower
thingol:
Você pode mudar um pouco seu protocolo? Pela minha experiência, é muito difícil fazer protocolos baseados em texto funcionar.
Tipo nesse caso minha inexperiencia me deixa na mão a principio eu tenho que mandar uma mensagem para o servidor e nada mais (tipo não preciso receber nada do mesmo), mas sendo assim você poderia me mandar um link exemplificando a criação de tal protocolo?
Desde já agradeço muito a todos que dedicaram a sua atenção.
T
Tchello
thingol, um protocolo em xml não seria interessante?
Mesmo que o projeto seja para fins didáticos seria algo que eu mesmo gostaria muito de implementar.
Sim, já fiz um projeto onde havia um protocolo de texto, foi um horror.
Pelo menos o componente responsável em administrar as conexões dos sockets ficou muito satisfatório =D, taí um projeto que eu gostaria de fazer novamente com meus conhecimentos atuais, ficaria muito mais interessante ^^
Desculpem-me pela divagação, é apenas a nostalgia em atacando =(
Abraços!
T
Tchello
lol, ao postar aqui o GUJ lançou um SocketException hehehehehe
L
laudenpower
Tchello:
thingol, um protocolo em xml não seria interessante?
Mesmo que o projeto seja para fins didáticos seria algo que eu mesmo gostaria muito de implementar.
Sim, já fiz um projeto onde havia um protocolo de texto, foi um horror.
Pelo menos o componente responsável em administrar as conexões dos sockets ficou muito satisfatório =D, taí um projeto que eu gostaria de fazer novamente com meus conhecimentos atuais, ficaria muito mais interessante ^^
Desculpem-me pela divagação, é apenas a nostalgia em atacando =(
Abraços!
É que na verdade eu apenas preciso enviar uma string de tamanho 10, nada mais tipo eu estou usando o DataInputStream e o DataOutputStream para enviar as informações do cliente e ler elas no servidor, o que acontece é que não tem jeito do servidor ficar lendo a medida que vou enviando, o servidor processa o que o cliente manda apenas quando o mesmo fecha a conexão.
Você teria algum exemplo de como fazer essa comunicação, mantendo o socket do cliente aberto.
Modificar uma opção DO SOCKET ( dê uma olhada em setTcpNoDelay - você deve chamar esse método imediatamente após abrir o socket , e não depois, com o parâmetro “true” ) para que ele não bufferize a saída :
Nesse caso é após a criação do socket no cliente? Eu li o que foi escrito porém quando eu chamo o método setTcpNoDelay(true); não acontece nada. Li o conceito sobre os método TCP_NODELAY e TCP_CORK, até onde eu pude entender quando eu configuro o meu socket para um desses o mesmo não espera que o cabeçalho do pacote seja enviado antes. Mas nesse caso não estou entendendo como eu tenho que chamar o método imediatamente após abrir o socket, vou postar o código que comecei do zero para os colegas avaliarem melhor.
Servidor
packagenet;importjava.net.ServerSocket;importjava.net.Socket;/** * * @author Laudelino Martins Cardoso Neto */publicclassSocketServidorMultiponto{privateServerSocketserver;privateSocketsocket;publicSocketServidorMultiponto(){try{server=newServerSocket(20000);server.setReuseAddress(true);System.out.println("Esperando o cliente se conectar");while(true){socket=server.accept();System.out.println("Um cliente foi aceito");newThreadServidor(socket).start();}}catch(Exceptione){e.printStackTrace();}}}//Fim da classe SocketServidorMultipontopackagenet;importjava.io.BufferedReader;importjava.io.InputStreamReader;importjava.net.Socket;/** * * @author Laudelino Martins Cardoso Neto */publicclassThreadServidorextendsThread{privateSocketsocket;publicThreadServidor(Socketsocket){this.socket=socket;}@Overridepublicvoidrun(){Stringmensagem="";try{BufferedReaderleitor=newBufferedReader(newInputStreamReader(socket.getInputStream()));mensagem=leitor.readLine();System.out.println("Mensagem: "+mensagem);socket.close();}catch(Exceptione){e.printStackTrace();}}}//Fim da classe ThreadServidor
Cliente
packagenet;importjava.io.DataOutputStream;importjava.io.OutputStream;importjava.io.PrintWriter;importjava.net.Socket;/** * * @author Laudelino Martins Cardoso Neto */publicclassSocketCliente{privateSocketsocket;privateDataOutputStreamsaida;privateOutputStreamout;privateinti;publicSocketCliente(){try{socket=newSocket("127.0.0.1",20000);socket.setTcpNoDelay(true);out=socket.getOutputStream();saida=newDataOutputStream(out);pw=newPrintWriter(out,true);}catch(Exceptione){e.printStackTrace();}}publicvoidenviaMensagem(){try{i++;Stringmensagem="Teste - "+i;saida.write(mensagem.getBytes());saida.flush();}catch(Exceptione){e.printStackTrace();}}publicvoiddesconectar(){try{out.close();saida.close();socket.close();}catch(Exceptione){e.printStackTrace();}}}//Fim da classe SocketCliente
Desculpa se não entendi direito algumas coisas, mas estou acompanhando esse tópico com o máximo de atenção para não disperdiçar o tempo de ninguém.
Desde já agrdeço pela atenção.
F
filipenf
não vejo problema nenhum quanto à usar o protocolo texto. Temos uma aplicação aqui que conecta à um servidor feito em C++ e transfere dados em modo texto sem problema. Existe uma opção para utilizar texto-plano ou criptografado, e nunca tive nenhum problema com isso. Pelo que entendi o problema é buffer, só não sei se no client ou no servidor.
Se você tiver acesso à uma máquina com linux, sugiro você usar o netcat pra emular o seu servidor, desta forma você consegue verificar se o problema é realmente o client. Basta você digitar
netcat -l -p 20000
e depois apontar o seu client para essa máquina. Com o uso do netcat, tudo que entrar por essa porta será impresso na stdout. Se não aparecer nada, o problema está no seu client. Se aparecer, o problema está no seu server.