The first thing to do in Intercom 1 is to create the window and controls you see in Figure 9.1. This works by creating a new object of the Intercom1 class in the main method: import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { public static void main(String[] args) { new Intercom1(); } . . . } Next, the code adds the Send button, connects it to an ActionListener, and adds the top text area control (note that the text area controls in this project have only vertical scrollbars so that the user doesn't have to stop to scroll horizontally with the mouse each time long text appears or he's typing something longer than the text area can display on one line): import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Button button1; private TextArea textarea1; public static void main(String[] args) { new Intercom1(); } public Intercom1() { setLayout(null); button1 = new Button("Send"); button1.setBounds(160, 360, 60, 20); add(button1); button1.addActionListener(this); textarea1 = new TextArea("", 7, 45, TextArea.SCROLLBARS_VERTICAL_ONLY); textarea1.setBounds(20, 80, 340, 120); add(textarea1); . . . } You can find the significant methods of the Java AWT TextArea class in Table 9.1.
Next comes the bottom text area control, where you enter text to send to Intercom 2. To make it clear that this text area is for that purpose, the code also adds a label with the text "Type here:" above the second text area control: import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Button button1; private TextArea textarea1, textarea2; private Label label1; public static void main(String[] args) { new Intercom1(); } public Intercom1() { setLayout(null); button1 = new Button("Send"); button1.setBounds(160, 360, 60, 20); add(button1); button1.addActionListener(this); . . . label1 = new Label(); label1.setBounds(20, 210, 100, 20); label1.setText("Type here:"); add(label1); textarea2 = new TextArea("", 7, 45, TextArea.SCROLLBARS_VERTICAL_ONLY); textarea2.setBounds(20, 230, 340, 120); add(textarea2); . . . } All that's left to create the Intercom 1 display is to add the label "Intercom 1" you see in Figure 9.1, to set the window title, and to handle window-closing events, as you see here: import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Button button1; private TextArea textarea1, textarea2; private Label label1, label2; public static void main(String[] args) { new Intercom1(); } public Intercom1() { setLayout(null); . . . label2 = new Label("Intercom 1"); label2.setFont(new Font("Times New Roman", Font.BOLD, 36)); label2.setBounds(100, 35, 200, 30); add(label2); setSize(400, 400); setTitle("Intercom 1"); setVisible(true); textarea2.requestFocus(); this.addWindowListener(new WindowAdapter(){ public void windowClosing( WindowEvent e){ System.exit(0); try{ socket.close(); }catch(Exception ex){} } } ); . . . } That's it for Intercom 1's GUI. The next step is to start the server and wait for a connection from Intercom 2. Intercom 1 acts as the server for the connection, waiting for connections from clients, so it's based on the Java java.net.ServerSocket class; Intercom 1 uses an object of this class to accept connections from Intercom 2. Connecting to Intercom 2ServerSocket connections let you create TCP/IP connections on the Internet, just as you would if you were writing your own web server. In fact, you can write a web server yourself in just a few lines, and you can host it yourself if you have a fixed IP address. Here's how: Just create a new ServerSocket object that will listen on port 80 (the port web browsers use), in a program called, say, Server.java. (More is coming up on how to use ServerSocket and what methods are available when this chapter develops the Intercom code next.) import java.io.*; import java.net.*; public class Server { public static void main(String[] args ) { try { ServerSocket socket = new ServerSocket(80); . . . } catch (Exception e) {} } } You can wait for connections to your new web server with the ServerSocket object's accept method and use a PrintWriter object to write to web browsers: import java.io.*; import java.net.*; public class Server { public static void main(String[] args ) { try { ServerSocket socket = new ServerSocket(80); Socket insocket = socket.accept(); PrintWriter out = new PrintWriter ( insocket.getOutputStream(), true); . . . } catch (Exception e) {} } } After a browser connects to the web server, you can send a web page back to the browser, like this: import java.io.*; import java.net.*; public class Server { public static void main(String[] args ) { try { ServerSocket socket = new ServerSocket(80); Socket insocket = socket.accept(); PrintWriter out = new PrintWriter ( insocket.getOutputStream(), true); out.println("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0 " + "transitional//EN'>"); out.println("<html>"); out.println("<head>"); out.println("<title>"); out.println("A new web page"); out.println("</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>"); out.println("A custom web server! Not bad."); out.println("</h1>"); out.println("</body>"); out.println("</html>"); insocket.close(); } catch (Exception e) {} } } After compiling Server.java, run it to start the server waiting for connections. In a browser on your or any Internet-connected machine, enter http://nnn.nnn.nnn.nnn, where nnn.nnn.nnn.nnn is your fixed IP address, and you'll see the web page Server.java creates, as shown in Figure 9.10. Figure 9.10. A custom web server.Congratulations, you've written your own custom web server, all using the built-in TCP/IP socket support in Java. Intercom 1 centers around a ServerSocket object that Intercom 2 will connect to. Here's how that object is created in Intercom 1note that this application communicates using port 8765, which is very rarely used by other applications (if you run across a conflict with other software, change this port number to some other four-digit number; using a value over 8000 helps avoid conflicts with most other software): import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Button button1; private TextArea textarea1, textarea2; private Label label1, label2; ServerSocket socket; public static void main(String[] args) { new Intercom1(); } public Intercom1() { . . . try { socket = new ServerSocket(8765); . . . } catch (Exception e) { textarea1.setText(e.getMessage()); } } . . . }
You can find the significant methods of the ServerSocket class in Table 9.2.
The ServerSocket object's accept method listens for and accepts connections, which is how you use a server socket. This method returns the Socket object you can use to communicate with Intercom 2. Here's what that looks like in code: import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Button button1; private TextArea textarea1, textarea2; private Label label1, label2; ServerSocket socket; PrintWriter out; Socket insocket; public static void main(String[] args) { new Intercom1(); } public Intercom1() { . . . try { socket = new ServerSocket(8765); insocket = socket.accept(); . . . } catch (Exception e) { textarea1.setText(e.getMessage()); } } . . . } You can find the significant methods of the Socket class in Table 9.3; these are the methods the code will use to communicate with Intercom 2.
Sending Text to Intercom 2Once the connection is made, you can use the Socket object's getOutputStream method to get an OutputStream object and create a PrintWriter object named out, which will let the user type to Intercom 2. How do you handle both incoming and outgoing text in the same application, potentially at the same time? To do that, Intercom 1 is clever and launches a new thread for the incoming text. It creates the PrintWriter object named out first to handle outgoing text, and then it starts the new thread for the incoming text this way: import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class Intercom1 extends Frame implements Runnable, ActionListener { private Thread thread; private Button button1; private TextArea textarea1, textarea2; private Label label1, label2; ServerSocket socket; PrintWriter out; Socket insocket; public static void main(String[] args) { new Intercom1(); } public Intercom1() { . . . try { socket = new ServerSocket(8765); insocket = socket.accept(); out = new PrintWriter(insocket.getOutputStream(), true); thread = new Thread(this); thread.start(); } catch (Exception e) { textarea1.setText(e.getMessage()); } } . . . } You can find the significant methods of the PrintWriter class in Table 9.4; this is the class Intercom 1 will use to actually write text to Intercom 2 across the Internet.
The user enters his text in the bottom text area, textarea2, and clicks the Send button. That executes the actionPerformed method, which simply has to send the text in the text area to the out object, which in turn sends that text to Intercom 2 (note that the code also clears the text area of text): public void actionPerformed(ActionEvent event) { if(event.getSource() == button1){ String text = textarea2.getText(); textarea2.setText(""); out.println(text); textarea2.requestFocus(); } } Reading Text from Intercom 2What about reading text from Intercom 2? Because the code is divided up between threads, Intercom 1 will be able to both read and send text at the same time. The worker thread in Intercom 1 will be run in Intercom 1's run method, and it will listen for text from Intercom 2. To catch text from Intercom 2, you can use the getInputStream method of the insocket object of the Socket class to get an InputStream object. To make working with that input stream easier, the code uses that InputStream object to create a BufferedReader object, which lets you read whole lines of text at a time. Here's how to create the BufferedReader object in the worker thread's run method: public void run() { try { BufferedReader in = new BufferedReader (new InputStreamReader(insocket.getInputStream())); . . . }catch (Exception e) { textarea1.setText(e.getMessage()); } } You can find the significant methods of the BufferedReader class in Table 9.5.
Now all you've got to do to read text from Intercom 2 is to use the BufferedReader object's readLine method, looping over that method repeatedly. Here's what that looks like in the run method, where the code waits until a line of text is ready and then appends it to the text in the top text area, textarea1: public void run() { String instring; try { BufferedReader in = new BufferedReader (new InputStreamReader(insocket.getInputStream())); while((instring = in.readLine()) != null){ textarea1.append(instring + "\n"); } }catch (Exception e) { textarea1.setText(e.getMessage()); } } That completes all the tasks in Intercom 1accepting connections from Intercom 2 on port 8765, reading text from Intercom 2, and sending text to Intercom 2. Now it's time to get Intercom 2 working. NOTE The Intercom project is designed for two users only. Can you convert it into a multiuser chat program like the Chat project earlier? Yes, you can, but it would take some work. Each time a new connection is made, you'd have to store a different Socket object in Intercom 1 and listen for text from all of them. Also, you'd have to echo the text you got from one client to all the clients. In other words, you'd have to modify the server application, Intercom 1, to really act as a server for multiple clients, reading and sending text to all clients. This certainly can be done, but it would take some work to do it. |