2013-03-27 3 views
0

Поскольку я изучаю Java-сокет, я создал функциональный, но очень простой чат в двух кадрах: один клиент и один сервер. Приложение работает только локально и работает. Но это не останавливается, когда я закрываю окна (хотя я добавил WindowListener в оба класса кадров): что не так?Почему эта клиент-серверная программа не останавливается?

Не забудьте сначала запустить сервер, а затем клиент: оба кадра будут видимыми только после подключения клиента.

Вот SimpleSocketFrame.java (базовый класс для обоих кадров):

package com.loloof64.java_se.simple_socket; 

import java.awt.GridLayout; 
import java.awt.event.KeyAdapter; 
import java.awt.event.KeyEvent; 

import javax.swing.JFrame; 
import javax.swing.JTextArea; 
import javax.swing.JTextField; 

public abstract class SimpleSocketFrame extends JFrame { 

    public SimpleSocketFrame() { 
     setTitle(getFrameTitle()); 
     setLayout(new GridLayout(0, 1)); 

     messageToAddField = new JTextField(30); 
    messageToAddField.addKeyListener(new KeyAdapter() { 

      @Override 
      public void keyReleased(KeyEvent e) { 
       if (e.getKeyCode() == KeyEvent.VK_ENTER){ 
        if (! messageToAddField.getText().isEmpty()) { 
         addMessage(messageToAddField.getText()); 
         messageToAddField.setText(""); 
        } 
       } 
      } 

    }); 

     printedMessagesArea = new JTextArea(10,30); 
     printedMessagesArea.setEditable(false); 

     add(messageToAddField); 
     add(printedMessagesArea); 

    pack(); 
} 

protected abstract void addMessage(String s); 
protected abstract String getFrameTitle(); 

private static final long serialVersionUID = -5861605385948623162L; 
protected JTextArea printedMessagesArea; 
private JTextField messageToAddField; 

} 

Вот класс сервера приложений: SimpleSocketServer.java

package com.loloof64.java_se.simple_socket; 

import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintStream; 
import java.net.ServerSocket; 
import java.net.Socket; 

import javax.swing.JOptionPane; 

public class SimpleSocketServer extends SimpleSocketFrame { 

    public SimpleSocketServer(){ 
     super(); 
     setTitle("Simple Server"); 
     try { 
      serverSocket = new ServerSocket(12345); 
      relatedSocket = serverSocket.accept(); 

      outStream = new PrintStream(relatedSocket.getOutputStream()); 
      isr = new InputStreamReader(relatedSocket.getInputStream()); 
      inStream = new BufferedReader(isr); 

      final InStreamRunnable inStreamRunnable = new InStreamRunnable(); 
      Thread inStreamReaderThread = new Thread(inStreamRunnable); 



      addWindowListener(new WindowAdapter() { 

       @Override 
       public void windowClosing(WindowEvent evt) { 
        try { 
         inStream.close(); 
        } catch (IOException e) { 
         e.printStackTrace(); 
        } 
        try { 
        isr.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
        outStream.close(); 
       try { 
        relatedSocket.close(); 
        } catch (IOException e) { 
         e.printStackTrace(); 
        } 
       try { 
        serverSocket.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
        inStreamRunnable.stop(); 
        System.exit(0); 
       } 

      }); 

      inStreamReaderThread.start(); 
     } catch (IOException e) { 
      JOptionPane.showMessageDialog(this, "Could not create the server !!!", 
        "Fatal error", JOptionPane.ERROR_MESSAGE); 
      System.exit(1); 
      e.printStackTrace(); 
     } 
    } 

    private class InStreamRunnable implements Runnable { 

     @Override 
     public void run() { 
      while (weMustGoOn){ 
       String line; 
       try { 
        line = inStream.readLine(); 
            printedMessagesArea.setText(printedMessagesArea.getText()+line+"\n"); 
       } catch (IOException e) { 

       } 
      } 
    } 

     public void stop(){ 
      weMustGoOn = false; 
     } 

     private boolean weMustGoOn = true; 

    } 

    @Override 
    protected void addMessage(String s) { 
     s = "Serveur> "+s; 
     outStream.println(s); 
     printedMessagesArea.setText(printedMessagesArea.getText()+"Client> "+s+"\n"); 
    } 

    @Override 
    protected String getFrameTitle() { 
     return "Simple Server"; 
    } 

    public static void main(String[] args) { 
     new SimpleSocketServer().setVisible(true); 
    } 

    private static final long serialVersionUID = 4288994465786972478L; 
    private Socket relatedSocket; 
    private ServerSocket serverSocket; 
    private PrintStream outStream; 
    private InputStreamReader isr; 
    private BufferedReader inStream; 
} 

Вот класс клиента: SimpleSocketClient.java

package com.loloof64.java_se.simple_socket; 

import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintStream; 
import java.net.InetAddress; 
import java.net.Socket; 

import javax.swing.JOptionPane; 

public class SimpleSocketClient extends SimpleSocketFrame { 

    public SimpleSocketClient(){ 
     try { 
      socket = new Socket(InetAddress.getLocalHost(), 12345); 
      outStream = new PrintStream(socket.getOutputStream()); 

     isr = new InputStreamReader(socket.getInputStream()); 
      inStream = new BufferedReader(isr); 

      final InStreamRunnable inStreamRunnable = new InStreamRunnable(); 
     Thread inStreamReaderThread = new Thread(inStreamRunnable); 

      addWindowListener(new WindowAdapter() { 

       @Override 
       public void windowClosing(WindowEvent evt) { 
       try { 
         inStream.close(); 
       } catch (IOException e2) { 
        e2.printStackTrace(); 
       } 
       try { 
        isr.close(); 
       } catch (IOException e1) { 
         e1.printStackTrace(); 
        } 
       outStream.close(); 
       try { 
        socket.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
       inStreamRunnable.stop(); 
       System.exit(0); 
      } 

     }); 
      inStreamReaderThread.start(); 
    } catch (IOException e) { 
      JOptionPane.showMessageDialog(this, "Could not create the client !!!", 
        "Fatal error", JOptionPane.ERROR_MESSAGE); 
      System.exit(1); 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     new SimpleSocketClient().setVisible(true); 
    } 

private class InStreamRunnable implements Runnable { 

    @Override 
    public void run() { 
      while (weMustGoOn){ 
      String line; 
      try { 
       line = inStream.readLine(); 
         printedMessagesArea.setText(printedMessagesArea.getText()+line+"\n"); 
       } catch (IOException e) { 

       } 
      } 
     } 

    public void stop(){ 
     weMustGoOn = false; 
    } 

    private boolean weMustGoOn = true; 

    } 

@Override 
protected void addMessage(String s) { 
    s = "Client> "+s; 
    outStream.println(s); 
    printedMessagesArea.setText(printedMessagesArea.getText()+s+"\n"); 
} 

@Override 
    protected String getFrameTitle() { 
    return "Simple Client"; 
} 

    private static final long serialVersionUID = 5468568598525947366L; 
    private Socket socket; 
    private PrintStream outStream; 
    private InputStreamReader isr; 
    private BufferedReader inStream; 


} 
+0

Это такая же проблема как для клиента, так и для сервера? – Austin

+0

Да, оба кадра не могут быть закрыты. – loloof64

+0

Система не выйдет, пока есть потоки daemon – MadProgrammer

ответ

2

Программа не останавливается, когда вы пытаетесь закрыть сетевой вход Readers при закрытии JFrame, которые блокируются на неопределенный срок. BufferedReader и InputStreamReader объекты не могут закрыть как клиентский поток блокируется в этом readLine вызова, который ждет ответа от сервера

line = inStream.readLine(); 

закомментировать или удалить:

inStream.close(); 

и

isr.close(); 

Простого закрытия потока Socket будет достаточно.

Помимо этого: При взаимодействии с компонентами Swing для многопоточных сетевых приложений всегда используйте SwingWorker. SwingWorkers предназначены для правильного взаимодействия с компонентами Swing.

+0

Это работа. Кроме того, я заменю простые Threads SwingWorkers. – loloof64

+0

Кроме того, это просто означает, что, когда я вызываю isr.close(), я жду, когда вы получите незащищенную блокировку? – loloof64

+0

Скорее вы ждёте «семафор» блокировки в 'Reader' в' close', который никогда не уведомляется, так как вы ожидаете ответа от клиента/сервера. Настолько эффективно, что это тупиковая ситуация. – Reimeus

2

Следует добавить finally блок после try и catch, чтобы закрыть сокет, и после этого вы можете закрыть свою программу с помощью метода выхода из системы.

+0

Да, вы правы: я добавил, наконец, предложения по коду моего компьютера. – loloof64

Смежные вопросы