Manter um socket aberto enviando mensagem

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?

Desde já grato pela atenção.

Sim, é possível.

Como você está fazendo a leitura do socket?

é 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 ?

Segue o código:

Servidor

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();
        }    
    }
}

Desde já agradeço a atenção de todos.

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]

Mesmo o HTTP, que é um protocolo baseado em texto, também trabalha com tamanhos de mensagens (o tal do Content-Length).

[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 =(

Abraços!

lol, ao postar aqui o GUJ lançou um SocketException hehehehehe

[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.

Desde já agradeço a atenção

Você leu o que escrevi? Vou repetir.

Em caso de dúvida:

http://articles.techrepublic.com.com/5100-10878_11-1050878.html

[quote=thingol][quote=Thingol]

  • 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]

Você leu o que escrevi? Vou repetir.

Em caso de dúvida:

http://articles.techrepublic.com.com/5100-10878_11-1050878.html[/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.

Desde já agrdeço pela atenção.

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.