330 likes | 475 Views
Servers and Sockets. Carol Wolf Computer Science. Java network programming classes . SocketImpl – abstract class and superclass of the rest Four fields: host, file descriptor, local port, host computer port Host – url such as www.pace.edu Local port – matches the host computer port
E N D
Servers and Sockets Carol Wolf Computer Science
Java network programming classes • SocketImpl – abstract class and superclass of the rest • Four fields: host, file descriptor, local port, host computer port • Host – url such as www.pace.edu • Local port – matches the host computer port • Host computer port • port 80 for web pages • port 25 for email • port 13 for time • localhost or local loop - 127.0.0.1 • Socket class – subclass of SocketImpl class • Used by client • SocketServer class – also subclass of SocketImpl • Used by server to wait for client request
Protocols • Sockets in Java use TCP/IP • Transmission Control Protocol (TCP) /Internet Protocol (IP) • Another protocol is User Datagram Protocol (UDP) • It is simpler than TCP/IP with less security • Hypertext Transfer Protocol (HTTP) sits on top of TCP/IP. • It is the primary world wide web protocol. • HTTP is stateless – it drops the connection after the response. • Cookies are commonly used to remember clients • Web browsers are configured for http. They interpret the url and open a client socket. • Response web page are interpreted by the browser using html, hypertext markup language
Program to access the NIST web site • The National Institute of Standards and Technology (NIST) maintains a number of very accurate clocks • These are accessible from a program you can download from their web site • A Java program to access the site takes only a few lines. • The one below uses the site in Colorado with the simple URL, time.nist.gov. • A new instance of a socket is associated with an I/O stream • To access this stream we use getInputStream () and getOutputStream (). • We also need a BufferedReader and a PrintWriter.
Java program NIST import java.io.*; import java.net.*; public class NIST { public static void main (String [] args) { try { // Create an instance of a stream socket connected to NIST on port 13. Socket socket = new Socket ("time.nist.gov", 13); // Get a BufferedReader to read data from the socket’s InputStream. BufferedReader reader = new BufferedReader (new InputStreamReadersocket.getInputStream ())); // Read two lines from the BufferedReader and display them on the console. for (int count = 0; count < 2; count++) { String time = reader.readLine (); System.out.println (time); } } catch (IOException e) {System.out.println ("Network error." + e);} } } // NIST
NIST program • There are two exceptions to be caught, IOException and an UnknownHostException. • But UnknownHostException is a subclass of IOException. • The Socket class has two parameters, the URL and the port. • We could read in the URL. NIST hosts seven sites with slightly different addresses. • The port for the time is set at 13. • The response comes in two lines with the first one empty.
Simple server – written by Cathy Zura • The server opens a server socket and waits for a client request. • It waits in a ‘while (true)’ loop. To get out of the loop, you have to stop the server. • The way the program is written, the user has to decide on the server’s port. This could be hard-coded as 8080. • When it gets a client request, it starts a thread. The tread class is called Server. • Theoretically it could host a number of requests, each in a separate thread.
/** The Web Server opens a port and gets a new ServerSocket. When a web page client opens a socket on the same port, it accepts the connection and creates a thread to handle it. It also keeps a count of the number of threads created. **/ import java.io.*; import java.net.*; // The Socket classes are in the java.net package. Import server.Server; // The Server class is in the package called server. public class WebServer { public static void main (String [] args) { final int DefaultPort = 8080;
try { // Set the port that the server will listen on. BufferedReader stdin = new BufferedReader (new InputStreamReader (System.in)); System.out.print ("Port: "); String portStr = stdin.readLine (); int port; // Use the default port. if (portStr.equals ("")) port = DefaultPort; // Use a different port. else port = Integer.parseInt (portStr);
int count = 1; // Track the number of clients. ServerSocket serverSocket = new ServerSocket (port); while (true) { // Respond to the client. Socket clientSocket = serverSocket.accept (); System.out.println ("Client " + count + " starting:"); new Server (clientSocket).start (); count ++; } } catch (IOException e) {System.out.println ("IO Exception");} catch (NumberFormatException e) {System.out.println ("Number error");} } //main } // WebServer
Threads • Threads are used for concurrent execution - multiprocessing • Thread class - can extend • Runnable interface - can implement • A thread class or one that implements Runnable must have a run method. • The run method contains the code to be executed by the thread • The run method is begun by a start method. • It is possible to set a priority for each thread. • Otherwise the thread inherits the priority of the thread that starts it. • Each program has at least one thread, that executes main. • Threads have wait and notify methods - inherited from Object • These stop the thread until it is notified that it can continue. At that time it competes as before for cpu time
More about threads • Threads can be put to sleep for a set amount of time • sleep (long milliseconds) • Put in a try - catch (InterruptedException e) block • Depends upon actual execution time, not number of CPU cycles • Should execute the same on all computers, independent of CPU speed • Daemon threads run in the background. • The garbage collector is an example • A thread may be marked as a daemon thread. It will then run when the processor isn't otherwise busy. • A daemon thread provides services to other threads • Methods that are run by threads can be synchronized. • Used to avoid race conditions • This is particularly important when the method accesses shared data • ATM example - one account accessed simultaneously from two ATMs • Change to one could upset balance
The Server Thread /** The Server class is a thread. It reads the URL string from the client's socket. It then gets a StringTokenizer for the string and uses the tokenizer to parse it. The first two tokens in the string are the method (get or post) and the name of the class that is to process the request. The remainder of the tokens is sent to the Request class for further processing. The process method in the processor class is then started. **/ class Server extends Thread { WebRequestProcessor processor; Socket clientSocket; public Server (Socket clientSocket) {this.clientSocket = clientSocket;}
public void run () { String urlString, method, processName; try { // Get an input stream for the client’s socket. InputStreaminStream = clientSocket.getInputStream (); BufferedReader in = new BufferedReader (new InputStreamReader (clientSocket.getInputStream ())); // Read the URL string and tokenize it. urlString = in.readLine(); System.out.println (urlString); StringTokenizertokenizer = new StringTokenizer(urlString, "/?&= "); // Get the first two tokens. Send the rest of the tokens to the Request class. method = tokenizer.nextToken(); System.out.println (method); processName = tokenizer.nextToken(); System.out.println (processName);
// Set up the Request and Response clases. Request request = new Request (tokenizer, method); OutputStreamoutStream = clientSocket.getOutputStream (); Response response = new Response (outStream); // Get a new instance of the processor class and start processing. System.out.println ("Processing: " + processName); processor = (WebRequestProcessor) Class.forName (processName).newInstance (); processor.process (request, response); clientSocket.close (); // Close the client's socket. } catch (IOException e) {System.out.println ("Processing Exception");} catch (ClassNotFoundExceptionce) {System.out.println("Class not found");} catch (InstantiationExceptionie) {System.out.println ("Instantiation exception");} catch (IllegalAccessExceptionie) {System.out.println ("Illegal Access Exception");} System.out.println("Client at "+ clientSocket.getInetAddress() + " disconnected"); } // run } // class Server
Clients – Sample web page <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title>E-Mail Form</title></head> <body> <h3>Enter your name and e-mail address. <br />Then click the Send button to send the data to the server.</h3> <form method = "get" action="http://localhost:8080/EmailProcessor"> <p><input type = "text" name = "name" value = "" size = 30 /> Name </p> <p><input type = "text" name = "email" value = "" size = 30 /> E-Mail Address </p> <p><input type="submit" value="Send" /></p> </form> </body> </html>
Serving the request • The Server class receives the URL string • GET /EmailProcessor?name=Alice+Lee&email=alee%40aol.com HTTP/1.1. • It uses a StringTokenizer in the Request class to break out the separate parts. • The method – GET • The name of the processor – EmailProcessor • The parameters – Alice Lee and alee@aol.com • Next it creates the Response class to send back to the client. • This is tricky. It uses the following code: • processor = (WebRequestProcessor) Class.forName (processName).newInstance (); • newInstance is in Class, a subclass of Object. • forName is a static method that returns the Class object associated with the class or interface with the given string name.
WebRequestProcessor class package server; /* An abstract class that defines a set of processing classes. All processor classes must extend this class. Without it, the server could not find the class to respond to the request. */ public abstract class WebRequestProcessor { // An abstract method that processes a request. public abstract void process (Request request, Response response); } // WebRequestProcessor
The Page class // Class with static methods that add standard lines to the html output page. class Page { public static void createHeader (PrintWriter out, String title) { out.println ("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'>"); out.println ("<html>"); out.println ("<head>"); out.println ("<title>" + title + "</title>"); out.println ("</head>"); out.println ("<body>"); } // createHeader public static void createFooter (PrintWriter out) {out.println ("</body></html>");} } // class Page
The request processor /* EmailProcessor processes a request from a client’s web page. It responds to the request by sending back a second web page echoing the name and email address. */ import java.io.*; // The Request, Response and WebRequestProcessor classes are in the server package. import server.Request; import server.Response; import server.WebRequestProcessor; public class EmailProcessor extends WebRequestProcessor {
The rest of EmailProcessor public void process (Request request, Response response) { // Get the request parameters with types name and email. String name = request.getParameter ("name"); String email = request.getParameter ("email"); // Get a PrintWriter object and respond to the request. PrintWriter out = response.getWriter (); Page.createHeader (out, "Test Data"); out.println ("<h3>Hello.</h3>"); out.println ("<h3>Your name is " + name + "</h3>"); out.println ("<h3>Your email address is " + email + "</h3>"); Page.createFooter (out); } } // EmailProcessor
The actual response page <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'> <html> <head> <title>Test Data</title> </head> <body> <h3>Hello.</h3> <h3>Your name is Alice Lee</h3> <h3>Your email address is alee@aol.com</h3> </body></html>
The Request class • The Request class stores the data entered into the form • Its methods that return either a parameter value, given its name, or the entire list of values. • It also changes all plus signs to spaces and hex values to the character given by that value. • And it discards the HTTP/1.1 at the end of the string. • It uses a class called Param to store each parameter. class Param { private String name, value; Param (String n, String v){name = n; value = v;} //constructor protected String getName(){return name;} protected String getValue(){return value;} }//Param
The Request class /* The Request class uses the StringTokenizer created in the WebServer class to create a Vector with the parameters encoded in the URL string. */ public class Request { private Vector parameters; private String method; /* Use the tokenizer to get the name and value parameters and store them in the Properties table. */ public Request (StringTokenizer tokenizer, String method) { String name, value; this.method = method; int size = 0; parameters = new Vector ();
More Request class while (tokenizer.hasMoreTokens()) { name = tokenizer.nextToken (); value = tokenizer.nextToken (); System.out.println(name + " " + value); if (!name.equals ("HTTP")) { value = replaceHexCode (value); Param param = new Param (name, value); parameters.addElement (param); size++; } } } // constructor
/* Some characters are sent in hex by the URL string. They are preceded by a '%' sign. The following method replaces them with the corresponding characters. It also replaces the '+' sign in the string with a space. */ private String replaceHexCode (String value) { value = value.replace ('+', ' '); int index = value.indexOf ('%'); // If no such index occurs in the string, the method returns -1. while (index >= 0) { try { // Get the hex code and covert it to decimal. String hex = value.substring (index+1, index+3); int decimal = Integer.parseInt (hex, 16);
/* Get the character with the decimal code, change the characters '<' to <, and '>' to >. Include the resulting string in the value parameter. */ char ch = (char) decimal; String code; if (ch == '<') code = "<"; else if (ch == '>') code = ">"; else code = String.valueOf (ch); value = value.substring (0, index) + code + value.substring (index+3); } catch (NumberFormatException e) {} index = value.indexOf ('%', index+1); } return value; } // replaceHexCode
// Given a name, return the corresponding value. public String getParameter (String name) { int index = 0; boolean found = false; Param param = null; while (index < parameters.size () && !found) { param = (Param) parameters.elementAt (index); if (param.getName ().equals (name)) found = true; else index ++; } if (found) return param.getValue (); else return null; } // getParameter
// Return an array containing just the parameter values, not the names. public String [] getParameterValues (String name) { String [] values = new String [10]; int index = 0; Param param; for (int count = 0; count < parameters.size (); count ++) { param = (Param) parameters.elementAt (count); if (param.getName ().equals (name)) values [index] = param.getValue (); index++; } return values; } // getParameterValues
package server; import java.io.*; /* The Response class uses the OutputStream sent by the Server to get a PrintWriter object. */ public class Response { private PrintWriter writer; public Response (OutputStream output) {/* The ‘true’ in the PrintWriter constructor indicates whether or not to use autoflush. If it is omitted, the buffer is not automatically emptied at the end of each line. */ writer = new PrintWriter (new OutputStreamWriter (output), true); } // constructor public PrintWriter getWriter () {return writer;} } // Response