Networking: Sockets
Simple one-way text messaging
We will write two simple programs:
- A sender to read text from the screen and send it over
the network to a receiver.
- A receiver to receive the sender's text and print
it to the screen.
- Both programs will run on different machines and communicate
using sockets.
What is a socket?
- A programming abstraction to make inter-program communication easy.
- A socket has two ends:
- You put stuff (bytes, chars, strings) in one end, on one machine.
- It comes out the other end, on another machine.
Let's look at an example. First, the sender:
(source file)
import java.io.*; // For PrintWriter.
import java.net.*; // For Socket's.
import java.util.*; // For the Scanner class.
public class TextSender {
public static void main (String[] argv)
{
try {
// Open a socket to the server: rabbit.seas.gwu.edu, port 9003.
Socket soc = new Socket ("rabbit.cs.gwu.edu", 9003);
// To test on same machine: Socket soc = new Socket ("localhost", 9003);
// Note: server must be fired up first!
InetAddress remoteMachine = soc.getInetAddress();
System.out.println ("Attempting connection to " + remoteMachine);
// Now create the output stream and wrap a PrintWriter around it.
OutputStream outStream = soc.getOutputStream ();
PrintWriter pw = new PrintWriter (outStream);
// Read text from the screen:
Scanner scanner = new Scanner (System.in);
System.out.print ("Enter text: ");
while (scanner.hasNext()) {
// Extract string and send it on the socket.
String line = scanner.next();
pw.println (line);
// Note: must flush() to actually cause line to be sent.
pw.flush();
// Repeat.
System.out.print ("Enter text: ");
}
}
catch (IOException e) {
System.out.println (e);
}
}
}
And now the sender:
(source file)
import java.io.*;
import java.net.*;
import java.util.*;
public class TextReceiver {
public static void main (String[] argv)
{
try {
// Create a listening service for connections at the designated port number.
ServerSocket srv = new ServerSocket (9003);
// When a connection is made, get the socket.
// The method accept() blocks until then.
System.out.println ("Server: waiting for a connection ... ");
Socket soc = srv.accept ();
// At this stage, the connection will have been made.
InetAddress remoteMachine = soc.getInetAddress();
System.out.println ("Accepted a connection from " + remoteMachine);
// We are going to listen, so get an InputStream and wrap a line-reader around it.
InputStream in_stream = soc.getInputStream ();
LineNumberReader lnr = new LineNumberReader (new InputStreamReader (in_stream));
String line = lnr.readLine ();
while (line != null) {
System.out.println ("Received: " + line);
// Repeat.
line = lnr.readLine();
}
}
catch (IOException e) {
System.out.println (e);
}
}
}
In-Class Exercise 1:
Download TextSender.java
to hobbes, change the port number to your assigned
port number, and compile. Likewise download
TextReceiver.java
to your rabbit.cs.gwu.edu account, change the port
number in the code, then compile.
First fire up TextReceiver, then TextSender.
Server vs. client:
- The terms server and client have nothing to
do with "sending" and "receiving".
- The server is the program that starts by listening
(using a ServerSocket).
- The client opens a connection to an existing server
(using a Socket).
In-Class Exercise 2:
Change the sender into a "server". That is, create
SenderAsServer.java on rabbit.cs.gwu.edu so that
the sender (one who prompts for text and sends it) is written
as a server that opens a ServerSocket.
Now write ReceiverAsClient.java on hobbes,
a program that opens a connection to the server, but will actually receive text
and display it.
Handling multiple connections
In-Class Exercise 3:
Go back to Exercise 1 and
run a single TextReceiver (on rabbit.cs.gwu.edu).
Now fire up two TextSender's (on hobbes) and
try to communicate with the TextReceiver. What do you notice?
We will modify the receiver to handle multiple connections:
- Once the receiver gets a connection request, it creates a new
thread to handle the request.
- The receiver then continues executing (in its own thread)
and is able to listen for new connections.
Here's the modified receiver:
(source file)
import java.io.*;
import java.net.*;
import java.util.*;
public class MultiTextReceiver {
public static void main (String[] argv)
{
try {
// Create a listening service for connections at the designated port number.
ServerSocket srv = new ServerSocket (9003);
int numConnections = 0;
while (true) {
// Wait for a connection to come in.
System.out.println ("Server: waiting for a connection ...");
Socket soc = srv.accept ();
// At this stage, the connection will have been made.
InetAddress remoteMachine = soc.getInetAddress();
System.out.println ("Accepted a connection from " + remoteMachine);
// We are going to listen, so get an InputStream
InputStream inStream = soc.getInputStream ();
LineNumberReader lnr = new LineNumberReader (new InputStreamReader (inStream));
numConnections ++;
// Create an instance of a object that handles this connection in a separate thread.
LineReaderObject lineReader = new LineReaderObject (lnr, numConnections);
System.out.println ("Server: created connection ID=" + numConnections);
// Start the thread:
lineReader.start();
} //end-while
}
catch (IOException e) {
System.out.println (e);
}
}
}
class LineReaderObject extends Thread {
LineNumberReader lnr;
int ID;
public LineReaderObject (LineNumberReader lnr, int ID)
{
this.lnr = lnr;
this.ID = ID;
}
// Override the run() method in Thread. This is what is called by the
// thread once it starts.
public void run ()
{
// readLine() needs a try-catch.
try {
String line = lnr.readLine ();
while (line != null) {
System.out.println ("[I# " + ID + "] Received: " + line);
line = lnr.readLine();
}
}
catch (IOException e) {
System.out.println (e);
}
}
}
Talking to a webserver
We will use the HTTP protocol to fetch a page from a webserver:
- The HTTP protocol has two main commands: GET and POST.
- GET is simpler (but restrictive).
Here's an example:
(source file)
import java.net.*;
import java.io.*;
public class WebClient {
public static void main (String[] argv)
{
try {
// Open the connection.
Socket soc = new Socket ("search.yahoo.com", 80);
InetAddress remote_machine = soc.getInetAddress();
System.out.println ("Connected to " + remote_machine);
// Now create the output and input streams
OutputStream outStream = soc.getOutputStream ();
PrintWriter pw = new PrintWriter (outStream);
InputStream inStream = soc.getInputStream();
LineNumberReader lnr = new LineNumberReader (new InputStreamReader (inStream));
// Make the query.
pw.print ("GET /bin/search?p=java\n\n");
pw.flush ();
// Get the results.
String s = lnr.readLine ();
while (s != null) {
System.out.println (s);
s = lnr.readLine ();
}
// Close streams before quitting.
pw.close ();
lnr.close ();
soc.close ();
}
catch (IOException e) {
System.out.println (e);
}
}
}
In-Class Exercise 4:
Modify WebClient.java to
fetch the index.html page that your webserver delivers
on rabbit.cs.gwu.edu. Remember to fire up your webserver
first.