2015-11-20 76 views
0

對於需要使用TCP連接構建程序的類。Java - 使用一臺計算機測試兩個TCP客戶端

我們給出了下面的示例代碼,以瞭解如何構建連接。當我只有一個客戶端運行時,服務器和客戶端通信正常。當我試圖啓動客戶端的一個新實例來模擬使用客戶端與一臺服務器的兩個人時,第二個客戶端不能發送或接收,也不會顯示「獲得I/O連接」消息。爲什麼是這樣?

以下是示例代碼。

// Fig. 27.5: Server.java 
// Server portion of a client/server stream-socket connection. 
import java.io.EOFException; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.awt.BorderLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.JTextField; 
import javax.swing.SwingUtilities; 

public class Server extends JFrame 
{ 
    private JTextField enterField; // inputs message from user 
    private JTextArea displayArea; // display information to user 
    private ObjectOutputStream output; // output stream to client 
    private ObjectInputStream input; // input stream from client 
    private ServerSocket server; // server socket 
    private Socket connection; // connection to client 
    private int counter = 1; // counter of number of connections 

    // set up GUI 
    public Server() 
    { 
     super("Server"); 

     enterField = new JTextField(); // create enterField 
     enterField.setEditable(false); 
     enterField.addActionListener(
     new ActionListener() 
     { 
      // send message to client 
      public void actionPerformed(ActionEvent event) 
      { 
       sendData(event.getActionCommand()); 
       enterField.setText(""); 
      } // end method actionPerformed 
     } // end anonymous inner class 
    ); // end call to addActionListener 

     add(enterField, BorderLayout.NORTH); 

     displayArea = new JTextArea(); // create displayArea 
     add(new JScrollPane(displayArea), BorderLayout.CENTER); 

     setSize(300, 150); // set size of window 
     setVisible(true); // show window 
    } // end Server constructor 

    // set up and run server 
    public void runServer() 
    { 
     try // set up server to receive connections; process connections 
     { 
     server = new ServerSocket(12345, 100); // create ServerSocket 

     while (true) 
     { 
      try 
      { 
       waitForConnection(); // wait for a connection 
       getStreams(); // get input & output streams 
       processConnection(); // process connection 
      } // end try 
      catch (EOFException eofException) 
      { 
       displayMessage("\nServer terminated connection"); 
      } // end catch 
      finally 
      { 
       closeConnection(); // close connection 
       ++counter; 
      } // end finally 
     } // end while 
     } // end try 
     catch (IOException ioException) 
     { 
     ioException.printStackTrace(); 
     } // end catch 
    } // end method runServer 

    // wait for connection to arrive, then display connection info 
    private void waitForConnection() throws IOException 
    { 
     displayMessage("Waiting for connection\n"); 
     connection = server.accept(); // allow server to accept connection    
     displayMessage("Connection " + counter + " received from: " + 
     connection.getInetAddress().getHostName()); 
    } // end method waitForConnection 

    // get streams to send and receive data 
    private void getStreams() throws IOException 
    { 
     // set up output stream for objects 
     output = new ObjectOutputStream(connection.getOutputStream()); 
     output.flush(); // flush output buffer to send header information 

     // set up input stream for objects 
     input = new ObjectInputStream(connection.getInputStream()); 

     displayMessage("\nGot I/O streams\n"); 
    } // end method getStreams 

    // process connection with client 
    private void processConnection() throws IOException 
    { 
     String message = "Connection successful"; 
     sendData(message); // send connection successful message 

     // enable enterField so server user can send messages 
     setTextFieldEditable(true); 

     do // process messages sent from client 
     { 
     try // read message and display it 
     { 
      message = (String) input.readObject(); // read new message 
      displayMessage("\n" + message); // display message 
     } // end try 
     catch (ClassNotFoundException classNotFoundException) 
     { 
      displayMessage("\nUnknown object type received"); 
     } // end catch 

     } while (!message.equals("CLIENT>>> TERMINATE")); 
    } // end method processConnection 

    // close streams and socket 
    private void closeConnection() 
    { 
     displayMessage("\nTerminating connection\n"); 
     setTextFieldEditable(false); // disable enterField 

     try 
     { 
     output.close(); // close output stream 
     input.close(); // close input stream 
     connection.close(); // close socket 
     } // end try 
     catch (IOException ioException) 
     { 
     ioException.printStackTrace(); 
     } // end catch 
    } // end method closeConnection 

    // send message to client 
    private void sendData(String message) 
    { 
     try // send object to client 
     { 
     output.writeObject("SERVER>>> " + message); 
     output.flush(); // flush output to client 
     displayMessage("\nSERVER>>> " + message); 
     } // end try 
     catch (IOException ioException) 
     { 
     displayArea.append("\nError writing object"); 
     } // end catch 
    } // end method sendData 

    // manipulates displayArea in the event-dispatch thread 
    private void displayMessage(final String messageToDisplay) 
    { 
     SwingUtilities.invokeLater(
     new Runnable() 
     { 
      public void run() // updates displayArea 
      { 
       displayArea.append(messageToDisplay); // append message 
      } // end method run 
     } // end anonymous inner class 
    ); // end call to SwingUtilities.invokeLater 
    } // end method displayMessage 

    // manipulates enterField in the event-dispatch thread 
    private void setTextFieldEditable(final boolean editable) 
    { 
     SwingUtilities.invokeLater(
     new Runnable() 
     { 
      public void run() // sets enterField's editability 
      { 
       enterField.setEditable(editable); 
      } // end method run 
     } // end inner class 
    ); // end call to SwingUtilities.invokeLater 
    } // end method setTextFieldEditable 
} // end class Server 

// Fig. 27.7: Client.java 
// Client portion of a stream-socket connection between client and server. 
import java.io.EOFException; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.net.InetAddress; 
import java.net.Socket; 
import java.awt.BorderLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.JTextField; 
import javax.swing.SwingUtilities; 

-

public class Client extends JFrame 
{ 
    private JTextField enterField; // enters information from user 
    private JTextArea displayArea; // display information to user 
    private ObjectOutputStream output; // output stream to server 
    private ObjectInputStream input; // input stream from server 
    private String message = ""; // message from server 
    private String chatServer; // host server for this application 
    private Socket client; // socket to communicate with server 

    // initialize chatServer and set up GUI 
    public Client(String host) 
    { 
     super("Client"); 

     chatServer = host; // set server to which this client connects 

     enterField = new JTextField(); // create enterField 
     enterField.setEditable(false); 
     enterField.addActionListener(
     new ActionListener() 
     { 
      // send message to server 
      public void actionPerformed(ActionEvent event) 
      { 
       sendData(event.getActionCommand()); 
       enterField.setText(""); 
      } // end method actionPerformed 
     } // end anonymous inner class 
    ); // end call to addActionListener 

     add(enterField, BorderLayout.NORTH); 

     displayArea = new JTextArea(); // create displayArea 
     add(new JScrollPane(displayArea), BorderLayout.CENTER); 

     setSize(300, 150); // set size of window 
     setVisible(true); // show window 
    } // end Client constructor 

    // connect to server and process messages from server 
    public void runClient() 
    { 
     try // connect to server, get streams, process connection 
     { 
     connectToServer(); // create a Socket to make connection 
     getStreams(); // get the input and output streams 
     processConnection(); // process connection 
     } // end try 
     catch (EOFException eofException) 
     { 
     displayMessage("\nClient terminated connection"); 
     } // end catch 
     catch (IOException ioException) 
     { 
     ioException.printStackTrace(); 
     } // end catch 
     finally 
     { 
     closeConnection(); // close connection 
     } // end finally 
    } // end method runClient 

    // connect to server 
    private void connectToServer() throws IOException 
    {  
     displayMessage("Attempting connection\n"); 

     // create Socket to make connection to server 
     client = new Socket(InetAddress.getByName(chatServer), 12345); 

     // display connection information 
     displayMessage("Connected to: " + 
     client.getInetAddress().getHostName()); 
    } // end method connectToServer 

    // get streams to send and receive data 
    private void getStreams() throws IOException 
    { 
     // set up output stream for objects 
     output = new ObjectOutputStream(client.getOutputStream());  
     output.flush(); // flush output buffer to send header information 

     // set up input stream for objects 
     input = new ObjectInputStream(client.getInputStream()); 

     displayMessage("\nGot I/O streams\n"); 
    } // end method getStreams 

    // process connection with server 
    private void processConnection() throws IOException 
    { 
     // enable enterField so client user can send messages 
     setTextFieldEditable(true); 

     do // process messages sent from server 
     { 
     try // read message and display it 
     { 
      message = (String) input.readObject(); // read new message 
      displayMessage("\n" + message); // display message 
     } // end try 
     catch (ClassNotFoundException classNotFoundException) 
     { 
      displayMessage("\nUnknown object type received"); 
     } // end catch 

     } while (!message.equals("SERVER>>> TERMINATE")); 
    } // end method processConnection 

    // close streams and socket 
    private void closeConnection() 
    { 
     displayMessage("\nClosing connection"); 
     setTextFieldEditable(false); // disable enterField 

     try 
     { 
     output.close(); // close output stream 
     input.close(); // close input stream 
     client.close(); // close socket 
     } // end try 
     catch (IOException ioException) 
     { 
     ioException.printStackTrace(); 
     } // end catch 
    } // end method closeConnection 

    // send message to server 
    private void sendData(String message) 
    { 
     try // send object to server 
     { 
     output.writeObject("CLIENT>>> " + message); 
     output.flush(); // flush data to output 
     displayMessage("\nCLIENT>>> " + message); 
     } // end try 
     catch (IOException ioException) 
     { 
     displayArea.append("\nError writing object"); 
     } // end catch 
    } // end method sendData 

    // manipulates displayArea in the event-dispatch thread 
    private void displayMessage(final String messageToDisplay) 
    { 
     SwingUtilities.invokeLater(
     new Runnable() 
     { 
      public void run() // updates displayArea 
      { 
       displayArea.append(messageToDisplay); 
      } // end method run 
     } // end anonymous inner class 
    ); // end call to SwingUtilities.invokeLater 
    } // end method displayMessage 

    // manipulates enterField in the event-dispatch thread 
    private void setTextFieldEditable(final boolean editable) 
    { 
     SwingUtilities.invokeLater(
     new Runnable() 
     { 
      public void run() // sets enterField's editability 
      { 
       enterField.setEditable(editable); 
      } // end method run 
     } // end anonymous inner class 
    ); // end call to SwingUtilities.invokeLater 
    } // end method setTextFieldEditable 
} // end class Client 

回答

1

問題是服務器類。這個類是單線程的(主線程)。主線程正在用於接受新連接和從套接字讀取。接受連接後的主線程採用processConnection方法,並在接下來的通話中阻止。

message = (String) input.readObject(); // read new message

此調用是在循環終止只有當你從客戶端接收

} while (!message.equals("CLIENT>>> TERMINATE")); 

,直到上面的循環運行的代碼不能回到「RUNSERVER」方法「終止」提出,它可以使用'waitForConnection'來接受連接。

您需要分離線程以接受連接並從連接的套接字讀取。

+0

應該可以使用[Selector](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/Selector.html)對單個線程進行處理,但這是相當可能的先進的,將意味着OP代碼的一些大的變化。 – markspace

+0

同意。對於真實世界的應用程序,我會選擇netty來完成這項工作。 – Peeyush

相關問題