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 ?
package network;
import util.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
* @author Laudelino Martins Cardoso Neto
*/
public class ServerMultiponto {
private ServerSocket s;
public ServerMultiponto (){
try {
s = new ServerSocket(20000);
processaRequisicao();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
private void processaRequisicao(){
while (true) {
try {
System.out.println("Esperando requisição...");
Socket conexao = s.accept();
new ThreadServidor(conexao).start();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}//Fim da classe ServerMultiponto
package network;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
*
* @author Laudelino Martins Cardoso Neto
*/
public class ThreadServidor extends Thread{
private BufferedReader leitor;
private Socket socket;
public ThreadServidor (Socket socket) {
this.socket = socket;
}
@Override
public void run(){
String mensagem = "";
try {
leitor = new BufferedReader(new InputStreamReader(socket.getInputStream()));
DataInputStream leitor = new DataInputStream(socket.getInputStream());
mensagem = leitor.readLine();
System.out.println("Mensagem chegada no servidor: "+mensagem+"\n");
socket.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
}//Fim da classe ThreadServidor
Cliente
package util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.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
*/
public class ClientSocket {
Socket socket = null;
DataInputStream in = null;
DataOutputStream out = null;
/**
* Contrutor de classe que cria um canal de conexão com o servidor
*/
public ClientSocket() throws Exception{
socket = new Socket("127.0.0.1", 20000);
in = new DataInputStream(socket.getInputStream());//Receptor da resposta do servidor
out = new DataOutputStream(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
*/
public void sendText(String mensagem) throws Exception{
try {
out.write(mensagem.getBytes());
out.flush();
} catch (Exception e) {
System.out.println("Ocorreu um erro no envio da mensagem ao servidor."+e.getMessage());
throw new Exception ("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
*/
public void desconectar(){
try {
in.close();
out.close();
socket.close();
} catch (Exception e) {
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
*/
public void enviaDadoSinalizacao(){
try{
socket.sendUrgentData(1);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
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.
O cabeçalho pode ser bem simples e ter apenas o tamanho dos dados (em 2 bytes).
Por exemplo, para mandar a mensagem “abacaxi” (que tem 7 bytes), você:
a) Converte a mensagem para um array de bytes;
b) Mede o tamanho desse array;
c) Usa DataOutputStream, método writeShort, para gravar o tamanho desse array;
d) Usa DataOutputStream, método write, para mandar os outros bytes.
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.
[quote=thingol]Você pode mudar um pouco seu protocolo? Pela minha experiência, é muito difícil fazer protocolos baseados em texto funcionar.
[/quote]
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.
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 =(
[quote=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![/quote]
É 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 :
[/quote]
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
package net;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
* @author Laudelino Martins Cardoso Neto
*/
public class SocketServidorMultiponto {
private ServerSocket server;
private Socket socket;
public SocketServidorMultiponto() {
try{
server = new ServerSocket(20000);
server.setReuseAddress(true);
System.out.println("Esperando o cliente se conectar");
while (true) {
socket = server.accept();
System.out.println("Um cliente foi aceito");
new ThreadServidor(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}//Fim da classe SocketServidorMultiponto
package net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;
/**
*
* @author Laudelino Martins Cardoso Neto
*/
public class ThreadServidor extends Thread{
private Socket socket;
public ThreadServidor(Socket socket) {
this.socket = socket;
}
@Override
public void run () {
String mensagem = "";
try{
BufferedReader leitor = new BufferedReader(new InputStreamReader(socket.getInputStream()));
mensagem = leitor.readLine();
System.out.println("Mensagem: "+mensagem);
socket.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}//Fim da classe ThreadServidor
Cliente
package net;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/**
*
* @author Laudelino Martins Cardoso Neto
*/
public class SocketCliente {
private Socket socket;
private DataOutputStream saida;
private OutputStream out;
private int i;
public SocketCliente(){
try{
socket = new Socket("127.0.0.1", 20000);
socket.setTcpNoDelay(true);
out = socket.getOutputStream();
saida = new DataOutputStream(out);
pw = new PrintWriter(out, true);
} catch (Exception e) {
e.printStackTrace();
}
}
public void enviaMensagem(){
try{
i++;
String mensagem = "Teste - "+i;
saida.write(mensagem.getBytes());
saida.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
public void desconectar(){
try{
out.close();
saida.close();
socket.close();
} catch (Exception e) {
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.
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.