2017-04-04 154 views
0

我正在学习套接字以及服务器/客户端如何通信。Java如何通过网络通过UI按钮发送数据

到目前为止,我有我的协议想通了,并成功地模拟合成的TCP三次握手:

  1. 客户端成功连接到服务器并发送SYN。
  2. 服务器收到SYN并向客户端回复SYNACK。
  3. 客户端收到SYNACK,并将ACK回复给服务器。

就是这样,所有这一切都是在新的线程的Runnable客户端服务的while(true)循环下的Run()方法中线性执行的。我不知道GUI类中的Button如何告诉我的客户端服务发送特定的数据包,例如,当服务在另一个线程中运行时,按下了用户界面中的特定按钮。

我有一个想法,但纠正我在这,不知怎地,我的服务类,而不是GUI界面里面添加的ActionListeners所有按钮..

感谢。

PS。我正在使用DataInputStream和DataOutputStream从/向套接字流读取/写入数据。

ClientService.java:

public class ClientService implements Runnable, GameProtocol { 
    private Socket socket; 
    private int clientNumber; 
    private GameClient client; 
    private JTextArea clientConsole; 
    private DataInputStream fromServer; 
    private DataOutputStream toServer; 

    public ClientService(Socket aSocket, GameClient aClient, JTextArea textArea) { 
     this.socket = aSocket; 
     this.client = aClient; 
     this.clientConsole = textArea; 
    } 
    private void buildStreams() throws Exception { 
     this.fromServer = new DataInputStream(this.socket.getInputStream()); 
     this.toServer = new DataOutputStream(this.socket.getOutputStream()); 
    } 
    public void sendPacket(int data) { 
     try  { 
      this.toServer.writeInt(data); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return; 
    } 
    public void flushPacket() { 
     try  { 
      this.toServer.flush(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return; 
    } 

    public void run() { 
     try { 
      try { 
       buildStreams(); 
       toServer.writeInt(PLAYER_SYN); // start synthetic three way handshake 
       toServer.flush(); 
       if(fromServer.readInt() == SERVER_SYNACK) { 
        this.clientNumber = fromServer.readInt(); 
        clientConsole.append("Client number from Server: " + clientNumber + "\n"); 
        toServer.writeInt(PLAYER_ACK); 
        toServer.writeInt(this.clientNumber); 
        toServer.flush(); 
       } 
       else { 
        clientConsole.append("Client -> Server Sync failed. Can't proceed.\n"); 
        toServer.writeInt(PLAYER_QUIT); 
        toServer.flush(); 
        socket.close(); 
        System.exit(-1); 
       } 
       executeCommand(); 
      } finally { 
       socket.close(); 
      } 
     } catch (Exception e) { 
      System.out.println("Client Service: " + e.getMessage()); 
     } 
    } 
    private void executeCommand() throws Exception { 
     boolean quit = false; 
     while(!quit) { 
      int command = -14324; 
      if(fromServer.available() > 0) { 
       command = fromServer.readInt(); 
      } 
      switch (command) { 
       case WINNER: 
        clientConsole.append("You Win.\n"); 
        break; 
       case LOSER: 
        clientConsole.append("You Lost.\n"); 
        break; 
       case ENABLE_TURN: 
        client.enableTurn(); 
        break; 
       case DISABLE_TURN: 
        client.disableTurn(); 
        break; 
      } 
     } 
    } 
} 

GameClient.java:

public class GameClient extends JFrame implements GameProtocol { 
/** 
* TextArea size. 
*/ 
    private final int 
     TEXTAREA_ROWS = 5, 
     TEXTAREA_COLS = 40; 
/** 
* Client window size, unadjustable. 
*/ 
    private final int 
     FRAME_W = 535, 
     FRAME_H = 600; 

    // for Assignment 10 
    private Socket socket; 
    private DataOutputStream toServer;   
    private DataInputStream fromServer; 
/** 
* ArrayList of type Cards, by default, holds 20 cards. 
*/ 
    private ArrayList<Card> cards; 
/** 
* Console for debugging. 
*/ 
    private JTextArea gameConsole; 
/** 
* Button to terminate game 
*/ 
    private JButton quitButton; 
/** 
* Main Frame panels, cardsPanel and consolePanel are sub-panels of mainPanel. 
*/ 
    private JPanel mainPanel, cardsPanel, consolePanel; 
/** 
* Builds packets to be sent to the server 
*/ 
    private Packet packet; 
/** 
* players points, accumulated. 
*/ 
    private int points = 0; 
/** 
* card choice 1 
*/ 
    private int choice1 = -1; 
/** 
* card choice 2 
*/ 
    private int choice2 = -1; 
/** 
* for building packet arguments 
*/ 
    private static int packetBuilderCount = 0; 
/** 
* Counter for outbound and inbound packets 
*/ 
    private static int packetNumber = 1; 
/** 
* for timing out cards when recieving no match 
*/ 
    private Timer timer; 
/** 
* true when user is allowed to pick cards. 
* false when another user is true 
*/ 
    private boolean isTurn = true; 
    // service 
    private ClientService service; 
/** 
* default contrustor. Builds JFrame and all components. 
*/ 
    public static void main(String[] args) { 
     new GameClient(); 
    } 
    public GameClient() { 
     packet = new Packet(); 
     buildButton(); 
     buildCards(); 
     addEventToCards();  // adds actionlistener to each card. 
     buildPanel(); 
     buildTimer(); 
     buildFrame(); 
     buildConnection(); // for assignment 10    
    } 
    private void buildConnection() { 
     try { 
      this.socket = new Socket(HOST, PORT); 
//   this.toServer = new DataOutputStream(socket.getOutputStream()); 
//   this.fromServer = new DataInputStream(socket.getInputStream()); 
      gameConsole.append("Socket Connected\n"); 
      service = new ClientService(socket, this, this.gameConsole); 
      new Thread(service).start(); 

     } catch (SecurityException ex) { 
      System.out.println("Check your firewall or antivirus. Unable to establish connection to server."); 
     } catch (UnknownHostException ex) { 
      System.out.println("Host can't be found. Unable to establish connection to server."); 
     } catch (ConnectException ex) { 
      System.out.println("Connection refused. What now?"); 
      ex.printStackTrace(); 
//   ex.printStackTrace(); 
     } catch (IOException ex) { 
      System.out.println("fuck......"); 
     } 
    } 
/** 
* initializes the timer. 
*/ 
    private void buildTimer() { 
     int delay = 3000; // wait for 3000ms 
     timer = new Timer(delay, new AbstractAction() { 
/** 
* event handler for timer. Flips the cards back to face down position. 
*/ 
      @Override 
      public void actionPerformed(ActionEvent ae) { 
       flipCard(choice1, 10);  
       flipCard(choice2, 10); 
      } 
     }); 
    } 
/** 
* Initializes JFrame 
*/ 
    private void buildFrame() { 
     this.setSize(FRAME_W, FRAME_H); 
     setResizable(false); 
     this.add(mainPanel); 
     this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     this.setVisible(true); 
    } 
/** 
* Initializes and compiles panels together. Adds Cards to panels 
*/ 
    private void buildPanel() { 
     gameConsole = new JTextArea(TEXTAREA_ROWS, TEXTAREA_COLS); 
     JScrollPane scrollConsole = new JScrollPane(gameConsole); 
     scrollConsole.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER); 
     gameConsole.setEditable(false); 
     mainPanel = new JPanel(); 
     mainPanel.setPreferredSize(new Dimension(535, 475)); 
     mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); 
     cardsPanel = new JPanel(new GridLayout(4, 5, 20, 20)); 
     for(int i = 0; i < 20; i++) { 
      cardsPanel.add(this.cards.get(i)); 
     } 
     consolePanel = new JPanel(); 
     consolePanel.add(scrollConsole); 
     consolePanel.add(quitButton); 
     mainPanel.add(cardsPanel); 
     mainPanel.add(consolePanel); 
    } 
/** 
* builds quit button, adds action listener to handle click 
*/ 
    private void buildButton() { 
     quitButton = new JButton("Quit"); 
     quitButton.addActionListener(e -> { 
      buildQuitPacket(); 
     }); 
    } 
/** 
* builds 20 Card object. 
*/ 
    private void buildCards() { 
     cards = new ArrayList<Card>(); 
     Card temp; 
     for(int i = 0; i < 20; i++) { 
      temp = new Card(); 
      cards.add(temp); 
     } 
    } 
/** 
* adds actionlistener to all 20 card objects. 
*/ 
    private void addEventToCards() { 
     for(int i = 0; i < 20; i++) { 
      final int INDEX = i; 
      cards.get(i).addActionListener(e -> buildButtonPacket(this.cards.get(INDEX).getId())); 
     } 
    } 
/** 
* Builds and sends a Packet when a card is chosen 
* @param value The card ID which was chosen. 
*/ 
    private void buildButtonPacket(int value) { 
     if(!isTurn) { 
      this.gameConsole.append("Its not you're turn.\n"); 
      return; 
     } 
     if(packetBuilderCount == 0) { 
      packet.writeCommandToPacket(PICKED_CARDS); 
      packet.writeValueToPacket(value); 
      packetBuilderCount++; 
      choice1 = value; 
     } 
     else if(packetBuilderCount == 1) { 
      packet.writeValueToPacket(value); 
      packetBuilderCount = 0; 
      writeToConsoleOutBound(packet.toString()); 
      choice2 = value; 
//   try{ 
//    out.print(this.packet.getPacket()); 
//    out.flush(); 
//   } catch (Exception e) { 
//    gameConsole.append("could not send packet.\n"); 
//   } 
      packet.clearPacket(); 
     } 
    } 
/** 
* For handling commands from recieved Packet 
* @param recieved the Packet object recieved. Gets and handles command and possible arguments that follows. 
* See GameProtocol class for packet command definitions. 
*/ 
    public void handlePacket(Packet recieved) { 
     int cmd = recieved.getCommand(); 
     writeToConsoleInBound(recieved.toString()); 
     int arg1 = -99; 
     int arg2 = -99; 
     switch (cmd) { 
      case SERVER_SYNACK: 
       arg1 = recieved.getFirstArg(); 
       if(arg1 == 1) { 
        this.isTurn = true; 
       } 
       else if (arg1 == 0) { 
        this.isTurn = false; 
       } 
       else { 
        writeToConsoleError("Server sync failed. Terminating.."); 
        try { 
         Thread.sleep(3000); 
        } catch (InterruptedException ex) { 
         writeToConsoleError(ex.getMessage()); 
        } finally { 
         System.exit(-1); 
        } 
       } 
       break; 
      case CARDMATCH: 
       arg1 = recieved.getFirstArg(); 
       arg2 = recieved.getSecondArg(); 
       if(arg1 > 9) { 
        arg1 = 9; 
       } 
       if(arg2 > 9) { 
        arg2 = 9; 
       } 
       flipCard(choice1, arg1); 
       flipCard(choice2, arg2); 
       break; 
      case CARDNOMATCH: 
//    System.out.println("CLIENT: im going to sleep"); 
       arg1 = recieved.getFirstArg(); 
       arg2 = recieved.getSecondArg(); 
       if(arg1 > 9) { 
        arg1 = 9; 
       } 
       if(arg2 > 9) { 
        arg2 = 9; 
       } 
       flipCard(choice1, arg1); 
       flipCard(choice2, arg2); 
       this.cardsPanel.revalidate(); 
       this.cardsPanel.repaint(); 
       this.consolePanel.revalidate(); 
       this.consolePanel.repaint(); 
       timer.setRepeats(false); //the timer should only go off once 
       timer.start(); 
       break; 
      case SHOW_CARD: 
       arg1 = recieved.getFirstArg(); 
       arg2 = recieved.getSecondArg(); 
       if(arg1 < 0 || arg1 > 19) { 
        writeToConsoleError(" BAD PACKET ARGUMENT 1, \nCard index out of bounds"); 
       } 
       else if(arg2 < 0 || arg2 > 10) { 
        writeToConsoleError(" BAD PACKET ARGUMENT 2, \nImage index out of bounds"); 
       } 
       else { 
        flipCard(arg1, arg2); 
       } 
       break; 
      case ENABLE_TURN: 
       this.isTurn = true; 
       break; 
      case DISABLE_TURN: 
       this.isTurn = false; 
       break; 
      case WINNER: 
       this.gameConsole.append("You win!\n"); 
       break; 
      case LOSER: 
       this.gameConsole.append("You lost :(\n"); 
       break; 
      case ADD_POINTS: 
       arg1 = recieved.getFirstArg(); 
       this.points += arg1; 
       this.gameConsole.append("** Gained " + Integer.toString(arg1) + ", you now have " + Integer.toString(this.points) + " points.\n"); 
       break; 
      case OTHER_QUIT: 
       this.gameConsole.append("Other player forfeited. You win!\n"); 
       break; 
     } 
    } 
/** 
* Builds a packet containing QUIT when quit button is pressed 
* Only available when isTurn == true. 
*/ 
    private void buildQuitPacket() { 
     if(this.isTurn == false) { 
      this.gameConsole.append("You can only quit when it is your turn."); 
      return; 
     } 
     service.sendPacket(PLAYER_QUIT); 
     service.flushPacket(); 
//  this.service.sendPacket(PLAYER_QUIT); 
//  this.service.flushPacket(); 
     System.exit(0); 
    } 
/** 
* Writes the outgoing packet to the game console. 
* @param msg the actual packet message. 
*/ 
    public void writeToConsoleOutBound(String msg) { 
     this.gameConsole.append(Integer.toString(packetNumber++) + ": SENDING: " + msg + "\n"); 
    } 
/** 
* Writes the incoming packet to the game console. 
* @param msg the actual packet message. 
*/ 
    public void writeToConsoleInBound(String msg) { 
     this.gameConsole.append(Integer.toString(packetNumber++) + ": RECEIVING: " + msg + "\n"); 
    } 
/** 
* Writes an error to the game console. 
* @param msg the error message. 
*/ 
    public void writeToConsoleError(String msg) { 
     this.gameConsole.append("Error handling packet number " + Integer.toString(packetNumber) + "." + msg + "\n"); 
    } 
/** 
* Flips a Card object and show an image 
* @param whichCard the card ID which will be flipped. 
* @param imgID the image which will be shown on the flipped card. 
*/ 
    public void flipCard(int whichCard, int imgID) { 
     this.cards.get(whichCard).flipCard(imgID); 
    } 
/** 
* Accumulates points. 
* @param amt the amount to be added. 
*/ 
    public void gainPoints(int amt) { 
     this.points += amt; 
    } 
    public void enableTurn() { 
     this.isTurn = true; 
    } 
    public void disableTurn() { 
     this.isTurn = false; 
    } 
} 
+1

按钮需要一个饲料后台线程代表UI的执行网络I/O或任何长时间运行操作的监听器。从那里你可以根据需要混合搭配 – efekctive

+0

@efekctive当你说“喂入后台线程”时,你是否暗示Service线程是GUI类的一个实例? – noobcoder

+1

否。从ui进行套接字连接的那一刻,它将冻结UI。它是一个可运行/线程。相反,当你想将信息传递回UI时,你需要调用UI所具有的任何异步方法 – efekctive

回答

0

你到一个套接字连接工程,因为它是在构造函数中,没有调用修改渲染元素已作出(说更新一个按钮的文字)。您的客户端服务需要一个循环来等待UI所发出的请求,否则您需要创建一个线程来处理UI所发出的每个请求。它将成为瓶颈,特别是在游戏中,当一个用户动作可以触发多个UI更新时。看代码贴出你应该遵循或多或少这种模式

package so; 

import java.awt.GridLayout; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.util.concurrent.ConcurrentLinkedQueue; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingUtilities; 

class ClientService implements Runnable{ 
    JLabel label; 
    ConcurrentLinkedQueue<Long> work; 

    @Override 
    public void run() { 
     Long i = null; 
     try{ 
      while(true){ 
       if ((i = work.poll()) != null){ 
        final long ii = i; 
        SwingUtilities.invokeLater(new Runnable() { 
         public void run() { 
          label.setText(ii + ""); 
         } 
         }); 
       } 
       Thread.sleep(10); 
      } 
     } 
     catch(Exception x){ 

     } 
    } 

} 

class GameClient extends JFrame{ 

    Thread worker; 
    ConcurrentLinkedQueue<Long> work; 
    private JLabel label; 
    private JButton button; 

    GameClient(){ 
     setLayout(new GridLayout(2, 1)); 
     label = new JLabel("Label"); 
     button = new JButton("Button"); 
     button.addMouseListener(new MouseListener(){ 

      @Override 
      public void mouseClicked(MouseEvent e) { 
       work.add(System.currentTimeMillis()); 
      } 

      @Override 
      public void mousePressed(MouseEvent e) { 
       // TODO Auto-generated method stub 

      } 

      @Override 
      public void mouseReleased(MouseEvent e) { 
       // TODO Auto-generated method stub 

      } 

      @Override 
      public void mouseEntered(MouseEvent e) { 
       // TODO Auto-generated method stub 

      } 

      @Override 
      public void mouseExited(MouseEvent e) { 
       // TODO Auto-generated method stub 

      } 

     }); 
     this.add(label); 
     this.add(button); 
     work = new ConcurrentLinkedQueue<Long>(); 
     ClientService cs = new ClientService(); 
     cs.label = this.label; 
     cs.work = this.work; 
     this.worker = new Thread(cs); 
     this.worker.start(); 
    } 
} 

class SOCLass { 

    public static void main(String[] args){ 
     SOCLass m = new SOCLass(); 
     GameClient frame = new GameClient(); 
     frame.addWindowListener(new WindowAdapter() { 
      public void windowClosing(WindowEvent e) { 
       frame.worker.interrupt(); 
       System.exit(0); 
      } 
     }); 
     frame.pack(); 
     frame.setVisible(true); 
    } 
}