[RESOLVIDO] J2ME SocketConnection não mantém conexão

Bom dia pessoal !!
Estou aqui mais uma vez para pedir o auxílio de vocês(Quem sabe um dia se eu ficar bom em Java o quanto eu sou em .Net eu começe a ajudar também) para uma coisa que é mais uma dúvida do que um problema.

Estou fazendo um aplicativo Cliente-Servidor usando Java no Servidor e é óbvio o Cliente é J2ME. Já escrevi muitas aplicações Cliente-Servidor em .Net, e me deparei com uma situação interessante: o J2ME não mantém a conexão aberta para Inputs e OutPuts.

Em todos os exemplos que eu achei, eu tenho que criar a conexão atravéz do Helper Connector, abrir o Input e OutPut, postar o que quiser mandar para o servidor no Output e ficar esperando com um while no Input até que toda a informação vinda do servidor seja descarregada.

Depois disso se eu tentar abrir um novo Input ou OutPut em cima da conexão aberta ele dá erro, dizendo que não há mais Outputs disponíveis. Para funcionar, eu tenho que fechar tudo que foi aberto(Input, Output, Conexão) e abrir tudo de novo. A idéia que segui é básicamente como estas:
http://www.java-samples.com/showtutorial.php?tutorialid=738
http://www.java-samples.com/j2me/socket-connection-free-j2me-sample-program.htm

A minha dúvida é: Eu não consigo manter esse tunel aberto? para cada vez que eu quiser alguma coisa do servidor, o Cliente é que tem que requisitar? Eu sei que dá pra fazer fila de mensagens, e a cada requisição a fila é transferida, etc. mas eu estou achando que isso vai consumir recursos que um aparelho real(descartando o emulador) não possui.

Obrigado pela atenção.

A conexão fica aberta pelo tempo que a operadora quiser ou a situação atual da rede suportar. Ponto.

É lógico que elas não tem interesse que você fique minutos ou horas conectados pois isto gera problema de faixa de IP. Cada vez que um cel se conecta a rede, a ele se atribui temporariamente um IP e lógico, menos 1 IP na faixa de endereço da operadora fica disponível para outros usuários.

Imagina se todo mundo inventa de fazer aplicação de push-email ? Táva tudo mundo ferrado…

Concordo com o que você disse. Realmente é uma questão de restringir o consumo de banda.
Mas a minha dúvida é um pouco mais técnica, ou seja, existe uma forma de abrir o tunel e mantê-lo aberto trafegando de um lado para o outro? porque todos os exemplos que encontrei eles fazem uma unica requisição e aguardam a resposta, e quando esta chega tudo é fechado.

Obrigado pela atenção.

[quote=joaoneto]Concordo com o que você disse. Realmente é uma questão de restringir o consumo de banda.
Mas a minha dúvida é um pouco mais técnica, ou seja, existe uma forma de abrir o tunel e mantê-lo aberto trafegando de um lado para o outro? porque todos os exemplos que encontrei eles fazem uma unica requisição e aguardam a resposta, e quando esta chega tudo é fechado.

Obrigado pela atenção.[/quote]

Você mesmo tem a resposta. A forma é não fechar o fluxo e pronto.
Quando vc trabalha com fluxos, você pode ter o comportamento bloqueante ou não-bloqueante…algo que todo mundo que já estudou socket sabe que é assim.
Diz-se bloqueante o comportamento de um método que quando chamado para te retornar algo do fluxo, só vai retornar o controle a quem chamou após os dados aparecerem.
Diz-se não-bloqueante o comportamento de um método que quando chamado, retorna imediatamente a quem chamou independente se tem ou não dados.

Você vai ter um loop que fica lendo do fluxo e ele só lê quando existe dados, por isto você tem a opção de que a leitura seja bloqueante. Se houver erro no fluxo, graciosamente o loop será encerrado e logo em seguida vc faz as finalizações necessárias (closes…,nulls…,etc).

Mestre boone obrigado pelas respostas.
Acho que vou então tentar encontrar uma forma do fluxo ficar aberto, porque por exemplo, para enviar algo ao servidor, eu tenho que abrir o Output, gravar nele e fechar. Sem fechar ele não envia. Já o caso do Input eu notei que é diferente mesmo, ele não precisa ser fechado para ficar recebendo.

Vou fazer mais alguns experimentos, suas respostas me ajudaram a dar uma “clareada” no que preciso fazer.

Obrigado.

[quote=joaoneto]Mestre boone obrigado pelas respostas.
Acho que vou então tentar encontrar uma forma do fluxo ficar aberto, porque por exemplo, para enviar algo ao servidor, eu tenho que abrir o Output, gravar nele e fechar. Sem fechar ele não envia. Já o caso do Input eu notei que é diferente mesmo, ele não precisa ser fechado para ficar recebendo.

Vou fazer mais alguns experimentos, suas respostas me ajudaram a dar uma “clareada” no que preciso fazer.

Obrigado.[/quote]

Estranho vc precisar fechar para ir enviando…tem certeza que vc está usando o método flush() para forçar o envio sem precisar fechar o fluxo ?

Sim, eu tentei com o Flush() pra ver se funcionava, mas não deu.
Pode ser alguma configuração extra que eu devo fazer. No exemplo que encontrei no site da SUN ele efetua estas:

client.setSocketOption(DELAY, 0); client.setSocketOption(KEEPALIVE, 0);
Mas já descobri que o KEEPALIVE deve ser 1 para manter a conexão até o limite de Timeout.
Para referência segue o link:http://developers.sun.com/mobility/midp/articles/midp2network/

Obrigado.

[quote=joaoneto]Sim, eu tentei com o Flush() pra ver se funcionava, mas não deu.
Pode ser alguma configuração extra que eu devo fazer. No exemplo que encontrei no site da SUN ele efetua estas:

client.setSocketOption(DELAY, 0); client.setSocketOption(KEEPALIVE, 0);
Mas já descobri que o KEEPALIVE deve ser 1 para manter a conexão até o limite de Timeout.
Para referência segue o link:http://developers.sun.com/mobility/midp/articles/midp2network/

Obrigado.[/quote]

Posta o código para darmos uma olhada.
Não tá fazendo muito sentido este problema que você tá passando, pois senão não daria certo usar SOCKET para ficar conversando usando um protocolo X (Ex: POP3) com um servidor, por exemplo. Vários protocolos são baseados em COMANDO->RESPOSTA, e portanto é fundamental poder ir enviando, recebendo, enviando, recebendo e por ai vai.

Segue meu código:

private String URL = "socket://127.0.0.1:6789";
private InputStream  Input;
private OutputStream  Output;
private void OpenConnection() throws Exception{
    client = (SocketConnection) Connector.open(URL,Connector.READ_WRITE);
    client.setSocketOption(SocketConnection.DELAY, 0);
    client.setSocketOption(SocketConnection.KEEPALIVE, 0);
    client.setSocketOption(SocketConnection.SNDBUF, 512);
    client.setSocketOption(SocketConnection.RCVBUF, 512);
    Output = client.openOutputStream();
    Input = client.openInputStream();
}
private void ReceiveMessage(){
        if(client != null){
            try {
                int c = 0;
                StringBuffer sb = new StringBuffer();
                while (((c = Input.read()) != '\n') && (c != -1)) {
                    sb.append((char) c);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
 private void SendMessage(String Message){
        if(client != null){
            try {
                Output.write((Message).getBytes());
                Output.flush();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
public void run() {
            OpenConnection();
        while(true){
            try {
                SendMessage("0:OK");
                ReceiveMessage();
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

Do jeito que está, se eu não fechar Input, Output e Client eu não consigo fazer nada. Ah, o Flush do método SendMessage não funciona.

Olha quando voce tenta ler o Input, enquanto nao tiver algo la ,a execuçao nao sai dessa linha de codigo. Entao eu sugiro que voce crie um form,em vez de InputStream e OutputStream use DataInputStream e DataOutputStream,para enviar use o metodo writeUTF() do Output (Strings Utf8) e para ler use readUTF() do Input detro de uma thread para esperar por mensagens assim o resto do aplicativo vai continuar rodando .Chama o metodo aguardaMensagens somente uma vez assim q abrir o DIS e DOS.

Espero ter ajudado .
Eu acabei de fazer uma chat por bluetooth e descobri que IStream e OStream so funcionam no emulador.

Valeu !! Vou experimentar a sua dica.

EDIT: Deu e não deu… Agora eu consegui ver o que estava acontecendo. Eu mudei o server para que ele receba o socket numa thread também:

[code]import java.io.;
import java.net.
;

public class Main {
public static void main(String[] args) {
try {
ServerSocket welcomeSocket = new ServerSocket(6789);

        System.out.println("Servidor Iniciado na Porta 6789;");
        System.out.println("Iniciando Listener...");
        while (true) {
            Socket connectionSocket = welcomeSocket.accept();
            TcpThread t = new TcpThread(connectionSocket);
            t.start();
        }
    } catch (IOException ex) {
        System.out.println("Ocorreu um Erro: " + ex.getMessage());
    }
}

static class TcpThread extends Thread {
    Socket socket;
    DataInputStream Sinput;
    DataOutputStream Soutput;
    TcpThread(Socket socket) {
            this.socket = socket;
    }
    public void run(){
        while(true){
            try{
                Soutput = new DataOutputStream(socket.getOutputStream());
                Soutput.flush();
                Sinput = new DataInputStream(socket.getInputStream());
            }catch(Exception ex){
                System.out.println("Ocorreu um Erro: " + ex.getMessage());
                return;
            }
            try{
                String Input = Sinput.readUTF();
                if(Input != null){
                    String[] Commands = Input.split(":");
                    int command = Integer.parseInt(Commands[0]);
                    switch(command){
                         case 0://Requisição de ID
                        Soutput.writeUTF("1");
                        Soutput.flush();
                    }
                }
            }catch(Exception ex){
                System.out.println("Ocorreu um Erro: " + ex.getMessage());
                return;
            }finally{
                try {
                    System.out.println("Fechando I/O");
                    Soutput.close();
                    Sinput.close();
                }catch (Exception e) {}
            }
        }
    }
}

}[/code]

Mas agora o Client está dando erro ao Ler a resposta. o erro é neste ponto:

Esse é meu Client:

[code]import java.io.;
import javax.microedition.io.
;
import java.io.IOException;
public class SocketController implements Runnable, ISocketSubject {

private SocketConnection client;
private String URL = "socket://127.0.0.1:6789";
private DataInputStream  Input;
private DataOutputStream  Output;
private String SocketStatus = "";

public SocketController(ISocketObservable observer){
    this.observer = observer;
}

private void SendMessage(String Message){
    if(client != null){
        try {
            Output.writeUTF(Message);
            Output.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
private void StartSocket(){
    try {
        client = (SocketConnection) Connector.open(URL,Connector.READ_WRITE);
        //if(PingServer(true)){
            client.setSocketOption(SocketConnection.DELAY, 0);
            client.setSocketOption(SocketConnection.KEEPALIVE, 1);
            client.setSocketOption(SocketConnection.RCVBUF, 512);
            client.setSocketOption(SocketConnection.SNDBUF, 512);
            client.setSocketOption(SocketConnection.LINGER, 5);
            Output = client.openDataOutputStream();
            Input = client.openDataInputStream();
        //}
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}
private void ReceiveMessage(){
    new Thread(){
        public void run(){
            if(client != null && Input != null){
                try {
                    String receive = Input.readUTF();
                    if(receive != null){
                        NotifyObservers(receive);
                    }
                } catch (IOException ex) {

                }
            }
        }
    }.start();
}

public void run() {
        StartSocket();
        ReceiveMessage();
        SendMessage("0:OK");
    while(true){
        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

private ISocketObservable observer;
public void AttachObserver(ISocketObservable observer) {
    this.observer = observer;
}

public void DettachObserver(ISocketObservable observer) {
    this.observer = null;
}

public void NotifyObservers(String info) {
    this.observer.Notified(info);
}

}[/code]
O erro é esse:

[quote]java.io.EOFException
at java.io.DataInputStream.readFully(), bci=45
at java.io.DataInputStream.readUTF(), bci=25
at java.io.DataInputStream.readUTF(DataInputStream.java:547)
at DeploySocket.SocketController$1.run(SocketController.java:97)[/quote]

Tenta isso no cliente pode ter erros é que eu to com um celular e esse codigo ta no meu computador.

DataInputStream dIS;
/*esse é o metodo runCliente chame ele assim q voce quiser iniciar a conexao*/
public void runClient(){
new Thread(){
public void run(){
conectado=false;
while(!conectado){
try{
SocketConnection conn=(SocketConnection)Connector.open(url);
//Conectado
dIS=conn.OpenDataInputStream();
DataOutputStream dOS=conn.OpenDataOutputStream();
//Conexao aberta
dOS.writeUTF("0:OK");
dOS.flush();
//Chama o metodo q cria a thread q espera pela mensagem
aguardaMsgs();
conectado=true;
//para a execucao da Thread
}catch(Exception ex){
ex.printStackTrace();
}
}.start();
}

public void aguardaMsgs(){
new Thread(){
public void run(){
try{
 String mensagem=dIS.readUTF();
/*faz o que voce quiser com a mensagem e se for receber outra ou mais mensagens coloca esse try dentro de um while*/
}catch(Exception ex){
ex.printStackTrace();
}
}.start();
}

Cara voce chega a conectar o celular ao computador ?.
To querendo testar com dois celulares mais so tenho internet nesse que eu to e no computador.

Eu vou tentar novamente, mas o modelo que você me enviou está bem parecido com o que eu estou fazendo.
Eu ainda não testei com nenhum aparelho, estou no emulador ainda.

Pô joao que escorregada sua hein…fazer comunicação sem estar dentro de Thread !!! Menino feio, muito feio…

Isto é o básico que todo mundo que vai fazer conexões tem que saber. Existe zilhares de posts aqui no GUJ e no Google demonstrando isto…

Putz levei um puxão de orelha do Stallone Cobra (O Algoritimo ruim é uma doença, e eu sou a cura!!) hauhauahuahauhauah !!
Eu peço desculpas pelas “badecagens” como dizemos aqui em minas, mas tá sendo bom, eu to aprendendo bastante.
Meu código funcionou amigos, o problema não eram só os Loops e Threads, achei algumas coisas erradas no server.

Estou fazendo um código mais enxuto aqui e vou postar ele assim que estiver pronto.
Obrigado a todos pela atenção.

PS: Meu código já estava dentro de uma thread, dentro da principal do GameCanvas, por isso ela não apareceu no código que postei. Mas fica a dica, pq funcionou melhor sendo individual.

Quem sou eu para puxar a orelha ?!

Só acho que vc não deu tanta importância aos comentários dos artigos que leu onde citava o uso de Thread.

Quanto a solução, viu que ela é possível ?!!! Nada como ter o esforço recompensado. Vc lutou e conseguiu. Não desanimou. É assim q tem que ser perante as adversidades.

Segue em anexo o Client em JME e o Server em SE.
Agradeço ao auxílio, às discussões e principalmente ao empenho.

Obrigado a todos.