如何使通过网络传递的Java序列化对象通知客户端GUI的变化

时间:2009-12-14 23:21:05

标签: java model-view-controller serialization client-server

我正在制作一个客户端 - 服务器Java应用程序,它将成为MV(C,P)。我当前的问题是,一旦客户端得到一个Player对象,它需要告诉GUI播放器已被更改,它应该更新GUI上的所有内容。 (不太详细,所以整个更新应该不错)

/*
 * ClientGUI.java
 *
 * Created on Dec 10, 2009, 7:51:31 PM
 */

package inequity;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Administrator
 */
public class ClientGUI extends javax.swing.JFrame
{
  private Socket socClient;
  private ObjectOutputStream socketOut;
  private ObjectInputStream socketIn;
  PriorityBlockingQueue<Command> clientQ = null;
  private Player me;

  public ClientGUI()
  {
    initComponents();
    setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    clientQ = new PriorityBlockingQueue<Command>(Server.CLIENT_QUEUE_SIZE, new CompObj());
    me = new Player();
  }
  public void initPlayer(String name, String color)
  {
    me.name = name;
    me.color = color;
  }

  public boolean connect(int port)
  {
    try
    {
      socClient = new Socket("127.0.0.1", port);
      if(socClient.isConnected())
        System.out.println("Connected");

      socketOut = new ObjectOutputStream(socClient.getOutputStream());
      socketIn = new ObjectInputStream(socClient.getInputStream());

      socketOut.writeObject(new PlayerUpdate(me, 2));

      new ClientListener(socketIn).start();

    }
    catch(Exception e)
    {
      try
      {
        if(socketOut != null)
        {
          socketOut.flush();
          socketOut.close();
        }

        if(socketIn != null)
        {
          socketIn.close();
        }

        System.out.println("Unable to connect");
      }
      catch (IOException ex)
      {
        System.out.println("IOException caught");
        return false;
      }
    }
    return true;
  }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
  private void initComponents() {

    txtMsg = new javax.swing.JTextField();
    btnSend = new javax.swing.JButton();
    PlayerName = new javax.swing.JLabel();
    ID = new javax.swing.JLabel();
    chat = new javax.swing.JLabel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    txtMsg.setText("Test");
    txtMsg.setName("txtMsg"); // NOI18N
    txtMsg.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        txtMsgActionPerformed(evt);
      }
    });

    btnSend.setText("Send");
    btnSend.setName("btnSend"); // NOI18N
    btnSend.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        btnSendActionPerformed(evt);
      }
    });

    PlayerName.setText("N/A");
    PlayerName.setName("PlayerName"); // NOI18N

    ID.setText("ID:");
    ID.setName("ID"); // NOI18N

    chat.setText("Chatbox");
    chat.setName("chat"); // NOI18N

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(btnSend, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE)
              .addComponent(txtMsg, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE)
              .addComponent(PlayerName, javax.swing.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE))
            .addContainerGap())
          .addGroup(layout.createSequentialGroup()
            .addComponent(ID)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 62, Short.MAX_VALUE)
            .addComponent(chat)
            .addGap(41, 41, 41))))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(ID)
          .addComponent(chat))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addComponent(PlayerName)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addComponent(txtMsg, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(btnSend)
        .addContainerGap())
    );

    pack();
  }// </editor-fold>                        

    private void txtMsgActionPerformed(java.awt.event.ActionEvent evt) {                                       

    }                                      

    private void btnSendActionPerformed(java.awt.event.ActionEvent evt) {                                        
      try
      {
        if(socketOut != null)
          socketOut.writeObject(new ChatCmd(txtMsg.getText(), 10));

      }
      catch (IOException ex)
      {
        Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
      }

    }                                       

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() 
            {
              String name = "";
              String color = "";

              JoinGUI join = new JoinGUI(name, color);
              ClientGUI client = new ClientGUI();
              client.initPlayer(name, color);

              client.setVisible(join.prompt());
              client.connect(1345);
            }
        });
    }

  // Variables declaration - do not modify                     
  private javax.swing.JLabel ID;
  private javax.swing.JLabel PlayerName;
  private javax.swing.JButton btnSend;
  private javax.swing.JLabel chat;
  private javax.swing.JTextField txtMsg;
  // End of variables declaration                   


  // End of variables declaration

  public class ClientListener extends Thread
  {
    private ObjectInputStream recieving;
    public ClientListener(ObjectInputStream in)
    {
      recieving = in;
    }

    @Override
    public void run()
    {
      Command cmd;
      while(true)
      {
        try
        {
          cmd = (Command) recieving.readObject();
          clientQ.add(cmd);
          //temp code!  Put in clientProxy? Drop to clientQ?
          clientQ.take().exec(me);
          ID.setText("" + me.getID());
          PlayerName.setText("" + me.name);
          chat.setText(me.latestChatMsg);
        }
        catch (InterruptedException ex)
        {
          Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (ClassNotFoundException ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }

      }

    }

  }

}

package inequity;

import java.io.Serializable;
import java.util.ArrayList;

/**
 *
 * @author Administrator
 */

public class Player implements Serializable
{
  public ArrayList openHandShakes;
  public String latestChatMsg;
  private int ID;
  public String name;
  public String color;

  public Player()
  {
    openHandShakes = new ArrayList();
    ID = -1;
    name = "";
    color = "";
  }
  public void setID(int id)
  {
    ID = id;
  }
  public int getID()
  {
    return ID;
  }
  public void openHandShake(int shakeID)
  {
    openHandShakes.add(shakeID);
  }
  public boolean closeHandShake(int shakeID)
  {
    return true;
  }

  public void latestChat(String message)
  {
    latestChatMsg = message;
  }

}

package inequity;

import javax.swing.Box;
import javax.swing.JTextField;

/**
 *
 * @author Administrator
 */
public class JoinGUI extends javax.swing.JFrame
{
  private JTextField nameBox;
  private JTextField colorBox;

  public JoinGUI(String name, String color)
  {
    //give back name and color to clientgui
    nameBox = new JTextField("Enter Name");
    colorBox = new JTextField("Enter Color");
    Box inputMenu  = Box.createVerticalBox();

    inputMenu.add(nameBox);
    inputMenu.add(colorBox);
    add(inputMenu);
  }
  public boolean prompt()
  {
    setVisible(false);
    //wait for enter to be pressed then return true, as clientGUI is waiting to started
    //then give name and color to client
    return true;
  }
}
/*
 * Server.java
 */

package inequity;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import java.net.*;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * The main class of the application.
 */
public class Server extends JFrame
{
  public static final int CLIENT_QUEUE_SIZE = 10;
  public static final int SERVER_QUEUE_SIZE = 10;

  public Server()
  {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  public PlayerProxy wait4client(int playerID, ServerSocket server, PriorityBlockingQueue<Command> srvQ)
  {
    Socket incoming;
    PriorityBlockingQueue<Command> clientQ = null;
    Runnable rIn;
    Runnable rOut;
    Thread tIn = null;
    Thread tOut = null;

    try
    {
      incoming = server.accept();
      clientQ = new PriorityBlockingQueue<Command>(CLIENT_QUEUE_SIZE, new CompObj());

      rIn = new Net2QRunnable(playerID, incoming, srvQ);
      rOut = new Q2NetRunnable(playerID, incoming, clientQ);

      tIn = new Thread(rIn);
      tOut = new Thread(rOut);

      tIn.start();
      tOut.start();      
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    clientQ.add(new HelloMsg());

    return new PlayerProxy(playerID, tIn, tOut, clientQ);
  }

  public static void main(String[] args)
  {
    PriorityBlockingQueue<Command> srvQ = new PriorityBlockingQueue<Command>(SERVER_QUEUE_SIZE, new CompObj());

    try
    {
      Game game = new Game(srvQ);
      Server srv = new Server();
      ServerSocket server = new ServerSocket(1345);

      srv.setVisible(true);

      System.out.println("Lobby");
      while (game.numPlayers() < 2)
      {
        game.addPlayer(srv.wait4client(game.numPlayers(), server, srvQ));
      }

      game.play();

      System.out.println("Game Finished");
    }
    catch (SocketException e)
    {
      System.out.println("Socket already taken, exiting.");
    }
    catch (Exception ex)
    {
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
    }

  }
}

如果您愿意,我可以提供完整的来源,但我认为这就是所需要的。 同样,Player对象被发送到客户端,客户端接受并将其Player对象设置为接收的对象。我希望客户端的播放器说:“嘿GUI!我刚刚修改过.Lemme调用一个提取/解析我所有数据的更新方法!”

任何帮助都将不胜感激。

更新:

  public class ClientListener extends Thread
  {
    private ObjectInputStream recieving;
    public ClientListener(ObjectInputStream in)
    {
      recieving = in;
    }

    @Override
    public void run()
    {
      Command cmd;
      while(true)
      {
        try
        {
          cmd = (Command) recieving.readObject();
          clientQ.add(cmd);

          java.awt.EventQueue.invokeLater(new Runnable()
          {
            public void run()
            {
              try
              {
                clientQ.take().exec(me);
                if(me.changed == true)
                  updateGUI();


              } 
              catch (Exception ex)
              {
                Logger.getLogger(ClientGUI.class.getName()).log(Level.SEVERE, null, ex);
              }
            }
          });
        }
        catch (Exception ex)
        {
          Logger.getLogger(ClientListener.class.getName()).log(Level.SEVERE, null, ex);
        }

      }

    }

  }

似乎它应该是我想要的吗?

2 个答案:

答案 0 :(得分:2)

只需将更新代码放入Runnable实例并将其传递给SwingUtilities.InvokeLater()即可。这会将你的runnable放入事件调度队列

或者你可以调用invokeAndWait来阻止,直到更新完成。

如果您尝试调用更新另一个线程中的Swing元素的代码,则可能导致其余代码不稳定。

答案 1 :(得分:2)

使用SwingUtilities.invokeLater在GUI线程中运行处理程序。