Многоклиентский сервер сокетов Java - голодание

Рассмотрим следующий сервер:

public class TestServer {


public static void main(String[] args) {


    String ksName = "/some/path/keystore-server.jks";
    char ksPass[] = "password".toCharArray();
    char ctPass[] = "pswd".toCharArray();

    KeyStore ks;
    try {
        ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream(ksName), ksPass);
        KeyManagerFactory kmf = 
        KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, ctPass);
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(kmf.getKeyManagers(), null, null);
        SSLServerSocketFactory ssf = sc.getServerSocketFactory();
        SSLServerSocket s   = (SSLServerSocket) ssf.createServerSocket(SERVER_PORT);                

        while(true){                
            SSLSocket sslsocket = (SSLSocket) s.accept();
            System.out.println("New Client accepted");
            TestThread t = new TestThread(sslsocket);
            t.run();      
        }

    } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException ex) {
        Logger.getLogger(TotalControlServer.class.getName()).log(Level.SEVERE, null, ex);
    }        
} 

}

Выше показан простой сервер, который принимает несколько клиентов: сразу после принятия нового соединения отправляется новый поток (TestThread) для обработки входящих запросов клиента. Вот код для TestThread:

public class TestThread implements Runnable {

SSLSocket sslsocket;

public TestThread(SSLSocket sslsocket) {
    this.sslsocket = sslsocket;
}

@Override
public void run() {

    ObjectInputStream is = null;
    ObjectOutputStream os = null;
    try {
        is = new ObjectInputStream(sslsocket.getInputStream());
        os = new ObjectOutputStream(sslsocket.getOutputStream());

        while(true){
            String p = (String) is.readObject();
            System.out.println("We got: " + p);

            os.writeObject(p.concat(p));
            os.flush();

        }//while         

    } catch (IOException ex) {
        Logger.getLogger(CmdLineService.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ClassNotFoundException ex) {
        Logger.getLogger(CmdLineService.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
        try {
            is.close();
            os.close();
            this.sslsocket.close();
        } catch (IOException ex) {
            Logger.getLogger(CmdLineService.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}//run

}

Достаточно просто, он просто бесконечно читает строки и отправляет обратно свою конкатенацию.

Клиент тоже довольно простой:

public class TestClient {


  public static void main(String[] args) {

    System.setProperty("javax.net.ssl.trustStore" , "/path/keystore-client.jks")  ;
    ObjectOutputStream os = null;
    ObjectInputStream is = null;
    SSLSocket sslsocket = null;

    try {
        SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory.getDefault();
        sslsocket = (SSLSocket) f.createSocket("localhost", SERVER_PORT);

        sslsocket.startHandshake();
        System.out.println("Authentication done");

        os = new ObjectOutputStream(sslsocket.getOutputStream());  
        is = new ObjectInputStream(sslsocket.getInputStream());


        BufferedReader b = new BufferedReader(new InputStreamReader(System.in)); 
        boolean exit = false;
        while(!exit){
            System.out.print("> ");
            String line = b.readLine();
            os.writeObject(line);
            os.flush();

            String s = (String) is.readObject();
            System.out.println(s);

        }//while

    } //main
    catch (IOException ex) {
        Logger.getLogger(TotalControlCmdClient.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ClassNotFoundException ex) {
          Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex);
      } finally {

        try {
            is.close();  
            sslsocket.close();
        } catch (IOException ex) {
            Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex);
        }
    }       
}

Просто зацикливается на неопределенный срок, отправляя строки и читая строку.

Это отлично работает для одного клиента. Однако, если я запускаю другой клиент, он не подключается! Кажется, я столкнулся с какой-то проблемой голодания потока.

Как я могу это исправить?


person nmpg    schedule 26.06.2015    source источник
comment
Не вызывать 't.run();' очень поможет. Подумайте, что это делает...   -  person Martin James    schedule 26.06.2015
comment
Если вы все еще застряли, то :stackoverflow.com/questions/8579657/   -  person Martin James    schedule 26.06.2015
comment
Да, я это вижу. Спасибо   -  person nmpg    schedule 27.06.2015


Ответы (1)


Причина проста: вы никогда не запускаете второй поток прослушивателя:

   while(true){                
            SSLSocket sslsocket = (SSLSocket) s.accept();
            System.out.println("New Client accepted");
            TestThread t = new TestThread(sslsocket);
            t.run();      
        } 

Вызов run() не запускает поток, это делается с помощью start(). Поэтому основной поток обрабатывает содержимое метода run() и, поскольку он занят, вы не можете подключиться с помощью второго клиента.

Поменяй t.run() на t.start и все будет хорошо.

person Robert    schedule 26.06.2015
comment
Я удивлен, что ОП не ухватился за это, когда его/ее отладчик показал, что выполнение никогда не возвращалось к accept(). - person Martin James; 26.06.2015
comment
Да, после добавления «Thread T = new Thread(t); T.start()', это сработало, как и ожидалось. - person nmpg; 27.06.2015