Dúvidas com Java - serverSocket e método .accept

7 respostas
G

Pessoal, boa tarde.

Estou usando o ServerScket para fazer uma conexão que não pode ficar esperando para sempre que o cliente envie alguma requisição.

Na verdade, o que tem que ser feito é criar um botão e quando alguém clicar, se o servidor estiver esperando (o método .accept bloqueia o fluxo da aplicação), ele deve desistir de ficar aguardando, passando para a próxima linha de código. Isto é, ele deve seguir em frente.

Como posso fazer isso? Tem como conseguir algo desse tipo?

Socket socket = server.accept();

  // Seguir em frente caso o usuário desista de aguardar a conexão.

7 Respostas

V

Você pode configurar o setSoTimeout do servidor, para que o accept fique parado só por um período de tempo.

Se quiser não bloquear o código no accept, deve utiliza-lo a partir de uma thread secundária.

G

Oi, Godoy. Foi o que pensei. Só que haverá um outro botão, para “parar” o servidor. Só que como ele está esperando conexão (no método “accept”), qual é a melhor forma de poder, a qualquer momento, parar um servidor assim, que está rodando numa thread? Algo desse tipo que envio abaixo (acho que essa forma que eu fiz é a pior de todas no mundo…)?

Da forma que eu vejo, a porta e os streams ficariam delegados à thread. A thread, por sua vez, é que vai ficar esperando a conexão, com o método accept. Dessa forma, agora ela é que ficará esperando a conexão e a aplicação pode continuar. Mas e quando eu quiser terminar, “desligar” essa servidor que está rodando na thread? Como faço? Termino a thread?

/*
 * Como iniciar e parar um servidor socket
 */
package testes;
import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;
import java.lang.Thread;


public class Testes {


    public static void main(String[] args) {
    
        
        /*
         * Criar uma thread para rodar o primeiro servidor
         */
        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    System.out.println("Iniciando servidor...");

                    ServerSocket serverSocket = new ServerSocket(9090);
                    Socket socket = serverSocket.accept();

                    System.out.println("finalizando servidor...");
                    serverSocket.close();
                    
                    
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(null, "Erro no servidor " + e.getMessage());
                } 
            }
        };
        thread.start();
        
        // tenta parar o primeiro servidor - Existe forma melhor de realizar o procedimento?
        System.out.println("Parando a thread do servidor agora...");
        thread.stop();
        System.out.println("Thread parada, iniciando novo servidor...");
        
        // Tenta subir o segundo servidor
        try {
            ServerSocket serverSocket = new ServerSocket(9090);
            Socket socket = serverSocket.accept();
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, "Erro no segundo servidor");
        }
    }
}
V

Não, você deve usar o método accept por um curto período de tempo. Algo assim:

public class ServerService implements Runnable {
    private volatile boolean stopped = false;

    public void stopServer() {
       stopped = true;
    }

    @Override
    public void run() {
        try (ServerSocket ss = ServerSocket(9090)) {
            server.setSoTimeout(1000); //Espera no máx. 1s pelo cliente
            while (!stopped) {
                 try {
                    Socket s = server.accept();
                    processaCliente(s);
                } catch (SocketTimeoutException e) {
                    //Sem problemas, vamos tentar novamente
                    //mas isso irá verificar a condição do while.
                }
            }         
        }
    }
No botão de start:
if (serverService = null) {
    serverService = new ServerService();
    Thread t = new Thread(serverService);
    t.setName("Server service");
    t.start();
}
No botão de stop:
if (serverService == null) return;
serverService.stopServer();
serverService = null;
G

Hum, entendi…

Ficou legal, mesmo. ME tira só uma última dúvida…

Quando criamod um thread inline, tipo

new Thread() { 

   public void run() { 
       // comandos 
   }
 
}.start();

Ao meu ver, isso é um objeto que não tem uma variável de referência “segurando” ele. Objetos assim são coletados pelo Garbage Collector, correto? Objetos não referenciados são coletados por ele. Então aí vem minha dúvida… Corro o risco de mandar esse objeto fazer alguma coisa e de uma hora para outra, ele pode ser coletado pelo GB? Afinal de contas, não se tem controle sobre o GB, então isso poderia acontecer?

V

No caso, a classe Thread está referenciando esse objeto.
Quando a thread é disparada, ela passa a referenciar o runnable dela também.

Então, não corre o risco de haver coleta de lixo nesse runnable, até que a Thread pare.

Quanto à sua solução antiga. Esqueci de comentar mas o método stop() é deprecated. Isso porque ele “aborta” a thread, sem liberar recursos. Por exemplo, no seu caso, o sistema operacional ainda poderia ficar esperando a conexão Socket, e você não conseguiria ligar o ServerSocket nessa porta novamente.

G

Eu tinha visto que esse método estava depreciado.

Quando você diz que a “classe thread está referenciando esse objeto”, como isso funciona? Através do “this” não pode ser, não é? pois todos os objetos se auto-referenciam. Tem alguma documentação que você conheça que eu possa ler para saber mais sobre esse pequeno detalhe? Pensei nisso essa semana e gostaria de entender melhor, só por curiosidade, mesmo. Como esses objetos inline se comportam frente ao GB. Eles ficam meio que sendo referenciados pela classe, enquanto estão processando algo e depois são liberados?

Caso vocÊ tenha algum material sobre isso, por favor, me aponte, para que eu possa ler mais a respeito!

As dúvidas foram sanadas, obrigado, Vini! Foi uma solução fácil, mas que eu realmente não estou acostumado a mexer com servidores, então apesar de já conhecer os métodos que foram usados, fiquei meio que perdido em como implementar.

Obrigado!!!

V

Desculpe, me confundi. Ali pensei que você estava criando um Runnable no construtor da Thread.
Mas, no caso, você está fazendo da forma menos elegante, criando um filho da classe Thread.

Nesse caso, a thread após disparada, não é coletada, pq a VM mantém uma referência implícita para ela.

É possível, inclusive, exibir a lista de todas as threads em execução:

Criado 18 de julho de 2012
Ultima resposta 23 de jul. de 2012
Respostas 7
Participantes 2