32
Servers and Sockets Carol Wolf Computer Science

Servers and Sockets Carol Wolf Computer Science. Java network programming classes SocketImpl – abstract class and superclass of the rest Four fields:

Embed Size (px)

Citation preview

Servers and Sockets

Carol WolfComputer 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 NISTimport 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 InputStreamReader socket.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.InputStream inStream = clientSocket.getInputStream ();BufferedReader in = new BufferedReader (new

InputStreamReader (clientSocket.getInputStream ()));

 // Read the URL string and tokenize it.urlString = in.readLine();System.out.println (urlString);StringTokenizer tokenizer = 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);OutputStream outStream = 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 (ClassNotFoundException ce) {System.out.println("Class not found");}catch (InstantiationException ie) {System.out.println ("Instantiation exception");}catch (IllegalAccessException ie) {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>

The web page

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 [email protected]

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 [email protected]</h3>

</body></html>

The response page in the browser

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 &lt, and '>' to &gt. Include the resulting string in the value parameter. */

char ch = (char) decimal;

String code;

if (ch == '<') code = "&lt";

else if (ch == '>') code = "&gt";

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