As with Intercom 1, the first item of business in Intercom 2 is to create its window, which you see in Figure 9.2. Here's how the code does itnote in particular the Connect button, which Intercom 1 (the server) doesn't have: import java.awt.*; import java.awt.event.*; class Intercom2 extends Frame implements Runnable, ActionListener { private Button button1, button2; private TextArea textarea1, textarea2; private TextField textfield1; private Label label1, label2, label3; public static void main(String[] args) { new Intercom2(); } public Intercom2() { setLayout(null); label1 = new Label("Server IP address:"); label1.setBounds(35, 80, 105, 20); add(label1); textfield1 = new TextField("127.0.0.1"); textfield1.setBounds(145, 80, 100, 20); add(textfield1); button1 = new Button("Connect"); button1.setBounds(255, 80, 80, 20); add(button1); button1.addActionListener(this); button2 = new Button("Send"); button2.setBounds(160, 390, 60, 20); add(button2); button2.addActionListener(this); textarea1 = new TextArea("", 7, 45, TextArea.SCROLLBARS_VERTICAL_ONLY); textarea1.setBounds(20, 110, 340, 120); add(textarea1); label2 = new Label(); label2.setBounds(20, 240, 100, 20); label2.setText("Type here:"); add(label2); textarea2 = new TextArea("", 7, 45, TextArea.SCROLLBARS_VERTICAL_ONLY); textarea2.setBounds(20, 260, 340, 120); add(textarea2); label3 = new Label("Intercom 2"); label3.setFont(new Font("Times New Roman", Font.BOLD, 36)); label3.setBounds(100, 35, 200, 30); add(label3); setSize(400, 430); setTitle("Intercom 2"); setVisible(true); textarea2.requestFocus(); this.addWindowListener(new WindowAdapter(){ public void windowClosing( WindowEvent e){ System.exit(0); } } ); } That's all you need for the window, which you saw earlier in Figure 9.2. Creating that window is the easy part. Now how about connecting to Intercom 1? Connecting to Intercom 1To connect to Intercom 1, the user enters Intercom 1's IP address in Intercom 2's text field (you can leave it as the default 127.0.0.1 if you're testing the intercoms on the same machine) and then clicks the Connect button. This calls the actionPerformed method connected to the Connect button, which uses the Socket class's constructor to connect to that IP address on the port Intercom 1 is listening on, port 8765. The code also displays the message "Connecting…." in the text field, as you see here: public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ try{ socket = new Socket(textfield1.getText(), 8765); textfield1.setText("Connecting...."); . . . } If the connection is made, the code uses the getInputStream and getOutputStream methods to get an object named in to handle incoming text from Intercom 1 and an object named out to handle outgoing text to Intercom 1: public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ try{ socket = new Socket(textfield1.getText(), 8765); textfield1.setText("Connecting...."); in = socket.getInputStream(); out = socket.getOutputStream(); } . . . } The in object is an object of the InputStream class, and the out object is an object of the OutputStream class. You can find the significant methods of the InputStream class in Table 9.6 and the significant methods of the OutputStream class in Table 9.7.
All that's left is to start the worker thread that Intercom 2 uses to handle the incoming text from Intercom 1: public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ try{ socket = new Socket(textfield1.getText(), 8765); textfield1.setText("Connecting...."); in = socket.getInputStream(); out = socket.getOutputStream(); thread = new Thread(this); thread.start(); } . . . } If there is an IOException, the most likely cause is that Intercom 1 wasn't set up and running. Here's how the code displays a message to that effect: public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ try{ socket = new Socket(textfield1.getText(), 8765); textfield1.setText("Connecting...."); in = socket.getInputStream(); out = socket.getOutputStream(); thread = new Thread(this); thread.start(); } catch (IOException ioe){ textarea1.setText("Intercom 1 must be running and\n" + "accessible before running Intercom 2."); textfield1.setText("Not connected"); } . . . } If there is any other exception, you can display the message in a general catch statement. If the Socket object is not null and its isConnected method returns TRue, on the other hand, the intercoms are connected, and the code displays the message "Connected" in the text field: public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ try{ socket = new Socket(textfield1.getText(), 8765); textfield1.setText("Connecting...."); in = socket.getInputStream(); out = socket.getOutputStream(); thread = new Thread(this); thread.start(); } catch (IOException ioe){ textarea1.setText("Intercom 1 must be running and\n" + "accessible before running Intercom 2."); textfield1.setText("Not connected"); } catch (Exception e){ textarea1.setText(e.getMessage()); } if(socket != null && socket.isConnected()){ textfield1.setText("Connected"); } } . . . } Okay, we're connected. What about sending text to Intercom 1 when the user enters some? Sending Text to Intercom 1When the user enters text in the bottom text area in Intercom 2, textarea2, and clicks the Send button, that text is supposed to be sent to Intercom 1. Sending that text isn't difficult; you can simply use the write method of the out object created when the connection was established: public void actionPerformed(ActionEvent event) { . . . if(event.getSource() == button2){ try{ String str = textarea2.getText() + "\n"; byte buffer[] = str.getBytes(); out.write(buffer); textarea2.setText(""); textarea2.requestFocus(); } catch(Exception ex) {textarea1.setText(ex.getMessage());} } } } And that's all you need; now the text in the bottom text area is sent to Intercom 1. What about reading any text sent to Intercom 2 from Intercom 1? Reading Text from Intercom 1Intercom 2 uses a worker thread to read text from Intercom 1. The idea of using a new thread, as in Intercom 1, is to let both text reading and sending happen at the same time. Intercom 2 simply uses the InputStream class's read method to read characters, one by one, this way: public void run() { try{ while ((character = in.read()) != -1) { . . . } } } This lets you read from Intercom 1, character by character. To make that kind of input into a string that you can append to the top text area's text, you can use the String class's constructor, but note that that constructor won't take char variables. To fix that, you can simply store the just-read character in a char array and convert that into a string, which is then appended to the text area's text this way: public void run() { try{ while ((character = in.read()) != -1) { chars[0] = (char)character; textarea1.append(new String(chars)); } } } And you can handle exceptions in a catch statement that displays the exception's message: public void run() { try{ while ((character = in.read()) != -1) { chars[0] = (char)character; textarea1.append(new String(chars)); } } catch(Exception ex) {textarea1.setText(ex.getMessage());} } That completes Intercom 2, the client application. And that's all you need. To get everything started, you run Intercom 1, then Intercom 2, and you use Intercom 2 to connect to Intercom 1. After being connected, both parties can type back and forth freely. Very cool. NOTE Download the complete source code for the Intercom project, Intercom1.java and Intercom2.java, at the Sams website. After compiling these files, run Intercom1.java first, followed by Intercom2.java, as detailed in this chapter, and get started writing back and forth. |