Java: приложение, разделяющее клиента.- ПРИ чтении потока ObjectInputStream

#java #multithreading #objectinputstream

#java #многопоточность #objectinputstream

Вопрос:

Это должно быть очень просто, но мне, очевидно, не хватает некоторого базового понимания. У меня есть простое клиент-серверное приложение

сервер:

 public class GameServer{
   private Socket clientSocket;
   private ServerSocket serverSocket;
   private ArrayList<ObjectOutputStream> oosPlayers=new ArrayList<ObjectOutputStream>();   

   public static void main(String args[]){
       GameServer server = new GameServer();
       server.startGame();
   }   

   public void startGame(){  
      try{
         serverSocket = new ServerSocket(10008);  
         System.out.println("Server started at:"   serverSocket);
         while(true){
             System.out.println("Server: Waiting for client requests...");
             clientSocket=serverSocket.accept();
             System.out.println("Server: Client connection accepted at:" clientSocket);
             //remember player's output stream
             oosPlayers.add(new ObjectOutputStream(clientSocket.getOutputStream()));
             //read data from clients 
             Thread receiverThread = new Thread(new DataReceiver());
             receiverThread.start();
         }
      }
      catch(IOException ex){
          System.out.println(ex.getMessage());
      }
   }
   public class DataReceiver implements Runnable{
       public void run(){
           try{
               String mess;
               ObjectInputStream ois = new ObjectInputStream(clientSocket.getInputStream());
               while((mess=(String)ois.readObject()) != null){                         
                   System.out.println("Server: " mess);
                   send();
               }
           }
           catch(Exception ex){ System.out.println(ex.getMessage()); }
       }
       public void send(){
           for (ObjectOutputStream oos : oosPlayers){
               try{
                   oos.writeObject(new String("There is " oosPlayers.size() " player(s) at the board"));                   
                   oos.flush();
               }
               catch(Exception ex){ System.out.println(ex.getMessage()); }  
           }
       }
   }        
}
  

клиент:

 public class GameClient implements ActionListener{
   private Socket socket;
   private ObjectInputStream  ois;
   private ObjectOutputStream oos;
   private JButton button;

   public static void main(String args[]){
       GameClient client = new GameClient();  
       client.drawBoard();
   }
   public void drawBoard(){
       JFrame frame = new JFrame("A game");
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       button = new JButton("Send");
       button.addActionListener(this);
       configureConnection();
       Thread receiverThread = new Thread(new DataReceiver());
       receiverThread.start();

       frame.getContentPane().add(new JPanel().add(button));
       frame.setSize(200,200);
       frame.setVisible(true);
   }   
   public void configureConnection(){
       try{
           socket = new Socket("127.0.0.1", 10008);
           ois = new ObjectInputStream(socket.getInputStream());
           oos = new ObjectOutputStream(socket.getOutputStream());
       }
       catch(Exception ex) { System.out.println(ex.getMessage()); }
   }   

   public void actionPerformed(ActionEvent ev){
       if (ev.getSource()==button){
          try{
              oos.writeObject(new String("Some data from client"));
              oos.flush();
          }
          catch(Exception ex){ System.out.println(ex.getMessage()); }         
       }
   }   

   public class DataReceiver implements Runnable{
       public void run(){
           try{
               String mess;
               while((mess=(String)ois.readObject())!= null){                  
                   System.out.println("Client:" mess);
               }
           }
           catch(Exception ex){ System.out.println(ex.getMessage()); }
       }
   }   
}
  

Мой вопрос относится к receiverThread с обеих сторон. Когда я использую WHILE во время чтения из ObjectInputStream (в методе RUN), sever прослушивает запросы клиентов и правильно отправляет их всем потокам ObjectOutputStream. Если я изменю это WHILE на IF данные считываются (и отправляются) с сервера только один раз для каждого клиента. То же самое относится и к WHILE / IF клиента.

Я понимаю, что когда я запускаю receiverThread, он каким-то образом останавливает поток в WHILE и продолжает чтение из входного потока, тогда как использование IF позволяет потоку завершиться (и тем самым останавливает процесс чтения). Как это возможно, что, хотя условие WHILE не выполняется, т.Е. Изначально (ни один клиент еще не подключен), он сохраняет receiverThread в рабочем состоянии. То же самое происходит после того, как receiverThread уже прочитал данные, и в потоке ничего не осталось.

Был бы признателен за некоторые пояснения по этой основной проблеме.

С уважением, Марчин

Ответ №1:

Если вы измените while на if в своем run() методе, вы прочитаете только один объект и завершите работу. Если ваш протокол клиент-сервер очень прост, например, они отправляют только один объект за сеанс, обе версии равны. В противном случае система не будет функционировать должным образом: все остальные объекты не будут доставлены.

Комментарии:

1. Спасибо за ответ. Мне все еще любопытно, как это возможно, что при первом while((mess=(String)ois.readObject())!= null) выполнении на сервере условие выполняется с ошибкой (поток пуст), но он не завершает метод run и продолжает прослушивать входной поток.

2. Он не продолжает прослушивать InputStream. Это прослушивающий сокет , потому что вы его не закрыли. Четкая реализация должна завершать цикл while с помощью блока try / finally и закрывать поток в блоке finally.

3. Это может быть неверно, но я считаю, что это не завершает работу while((mess=(String)ois.readObject())!= null) , потому что ваш сокет находится в режиме блокировки , и поток чтения будет блокировать метод readObject() до тех пор, пока он либо не прочитает данные, либо сокет не будет закрыт (что приводит к возникновению исключения).

4. Ну, буквально я бы прочитал это как «пока есть что-то, что нужно прочитать из входного потока (поскольку сокет все еще открыт), сделайте что-нибудь», но это означало бы «если там ничего не осталось, выйдите из цикла». Согласно вашему предложению, это следует скорее интерпретировать как «пока сокет находится в режиме блокировки, продолжайте чтение из него», тогда как при использовании IF это будет выглядеть как «если сокет находится в режиме блокировки, считывайте из него». Возможно, здесь присутствует путаница, но на данный момент я удовлетворен этим объяснением и буду считать, что это так 🙂 спасибо

5. Не по теме: могу ли я как-нибудь оценить эти ответы