Download Sockets

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Sockets
Client-server applications were covered in our discussion of applets. How do client and
server programs communicate over the Internet? They use a connected pair of abstract
communication channels called sockets. A socket provides an input stream for receiving
data, and an output stream for sending data. The input stream of one socket is connected
to the output stream of the socket it is connected to:
:Client
:Socket
(client-side)
:InputStream
:OutputStream
machine/network boundary
:InputStream
:OutputStream
:Socket
(server-side)
connection
:Server
How are connected sockets created? A server socket is an abstract listening device
associated with an IP address and a port number. (The IP address is associated with the
computer that runs the server. Most computers provide 64,000 port numbers to choose
from.) Here is how a server socket is created by the server:
try
{
ServerSocket mySocket = new ServerSocket(4444); // port # = 4444
System.out.println(mySocket.getInetAddress()); prints IP address
// etc.
}
catch(IOException ioe) { ... }
A server socket provides a blocking accept() method that returns a server-side socket
when a client tries to connect with the server. This socket has associated input and output
streams, which can be used to exchange data between client and server:
// server control loop:
try
{
// ...
while(true)
{
Socket request = mySocket.accept();
BufferedReader in = Tools.makeReader(request.getInputStream());
PrintWriter out = Tools.makeWriter(request.getOutputStream());
// handle request
}
}
catch(IOException ioe) { ... }
The client-side socket is created by the constructor from the Internet address and port
number of the server socket. The constructor blocks until the connection is accepted by
the server:
Socket sock = new Socket("jupiter.sjsu.edu", 4444);
BufferedReader in = Tools.makeReader(sock.getInputStream()));
PrintWriter out = Tools.makeWriter(sock.getOutputStream());
// communicate with server
A Client-Server Framework
Java now provides a web-based, client-server framework where applets are the clients
and servlets are the servers. An applet is run by a Java virtual machine embedded in a
browser. A servlet is run by a servlet engine embedded in a web server. Before the servlet
framework was available, I used the server framework described below.
A Generic Server
A generic server instantiates the Master-Slave pattern. The server (i.e., the master)
perpetually listens at a server socket for an incoming client request. When a request
arrives, the server constructs and starts a handler thread (i.e. the slave) to service the
request. Each slave is provided with a unique identification number for debugging and a
socket connection to the client. The server constructs the handler using a handler factory
object, which is passed to the server's constructor. Thus, creating a specific type of server
requires programmers to write a handler and a handler factory object.
ServerSocket
1
Server
1
Thread
listen()
1
1
1
"HandlerFactory"
*
"RequestHandler"
creates
1
1
Socket
run()
processRequest()
makeRequestHandler()
framework
customization
ConcreteHandlerFactory
makeRequestHandler()
creates
ConcreteHandler
processRequest()
Notice that the attributes of Server are protected so they can be freely accessed by
concrete server class extensions.
class Server
{
public Server(int port, HandlerFactory h)
{
try
{
myPort = port;
mySocket = new ServerSocket(myPort);
myHandlerFactory = h;
myAddr = mySocket.getInetAddress();
} // try
catch(IOException ioe)
{
System.err.println("Failed to create socket; " + ioe);
System.exit(1);
} // catch
} // Server Constructor
// listens for a request, then creates & starts a handler
public void listen()
{
int id = 0;
try
{
while(true)
{
System.out.println("Server: listening");
Socket request = mySocket.accept(); // blocks
RequestHandler handler = myHandlerFactory(request,
nextID++);
handler.start();
} // while
System.out.println("Server: halting");
} // try
catch(IOException ioe)
{
System.err.println("Failed to accept socket, " + ioe);
System.exit(1);
} // catch
} // listen
protected
protected
protected
protected
protected
ServerSocket mySocket;
int myPort;
InetAddress myAddr;
HandlerFactory myHandlerFactory;
static int nextID = 0;
} // Server
Request Handler
A request handler instantiates a class derived from the abstract RequestHandler class,
which is derived from the Thread class. An abstract request handler creates reader and
writer character streams from the input and output byte streams associated with the client
socket (this seems to be what JDK 1.1 wants us to do, although data streams would be
another logical candidate). Notice that these character streams, together with the client
socket and the id number, are declared protected, hence are available to the concrete
handler.
The run() method of the abstract request handler calls an abstract processRequest()
method. This must be defined in the concrete request handler.
class RequestHandler extends Thread
{
public RequestHandler(Socket s, int i)
{
request = s;
myId = i;
try
{
in = Tools.makeReader(request.getInputStream());
out = Tools.makeWriter(request.getOutputStream());
} // try
catch(IOException ioe)
{
System.err.println("failed to create streams; " + ioe);
System.exit(1);
} // catch
}
public void run()
{
boolean more = true;
while(more) more = processRequest();
}
public abstract boolean processRequest();
protected
protected
protected
protected
}
Socket request;
BufferedReader in;
PrintWriter out;
int myId;
// RequestHandler
Server Factories
The only requirement of a handler factory is that it provide a method called
makeHandler(), which expects a socket and id number as input, and returns a request
handler as output. We can enforce this with a handler factory interface:
interface HandlerFactory
{
public RequestHandler makeHandler(Socket s, int id);
} // HandlerFactory
Example: Reflectors
Applets can only communicate with their host servers. Reflectors are a Java idiom for
working around this restriction. A reflector is a server running on the host server and
listening to a socket for messages coming from applets. When a message arrives, the
reflector forwards the message to a fixed destination object.
Web Client
Applet
Web Server
Reflector
Remote Computer
Remote
To use the server framework defined above, we need to extend the RequestHandler class
with a reflector handler class that provides a processRequest() method. In this case
processRequest() reads an applet message (a String) on its inherited BufferedReader, in,
then writes the message to a PrintWriter connected by a socket to the destination object.
The IP address and port number of the destination object is specified in the reflector
handler's constructor:
class ReflectorHandler extends RequestHandler
{
public ReflectorHandler(Socket s, String loc, int port)
{
super(s, 0);
Socket dest = new Socket(loc, port);
destIn = Tools.makeReader(dest.getInputStream()));
destOut = Tools.makeWriter(sock.getOutputStream());
}
public boolean processRequest()
{
try
{
String msg = in.readLine();
destOut.println(cmmd);
}
catch(IOException ioe)
{
System.err.println("Request read failure; " + ioe.toString());
stop();
} // catch
}
return false;
// processRequest
private BufferedReader destIn;
private PrintWriter destOut;
} // ReflectorHandler
We also need to specify a reflector handler factory, which is used by the server (the
master) to generate request handlers (slaves). The makeHandler() factor method (C++
programmers call factories virtual constructors) ignores the id number and uses the
ReflectorHandler constructor to create a new reflector handler, which, as required by the
framework, is returned as a RequestHandler instance. The IP location and port number of
the destination object are also parameters to the reflector handler's constructor:
class ReflectorFactory implements HandlerFactory
{
private String destLoc;
private int destPort;
public ReflectorFactory(String loc, int port)
{
destLoc = loc;
destPort = port;
}
public RequestHandler makeHandler(Socket s, int id)
{
return new ReflectorHandler(s, destLoc, destPort);
}
} // ReflectorFactory
A reflector is simply an instance of the Server class:
ReflectorHandlerFactory factory =
new ReflectorFactory("jupiter.sjsu.edu", 42);
Server reflector = new Server(4444, factory);
Example: A Database Server
A database server is a special type of server that provides database access to clients. The
handler for a database server (called an SQL handler) reads an SQL command from the
client, executes the command using the execute method provided by Database instances,
then sends a formatted string representing the result back to the client:
class SQLHandler extends RequestHandler
{
private Database myDbase;
public SQLHandler(Socket s, int id, Database db)
{
super(s, id);
myDbase = db;
}
public boolean processRequest()
{
try
{
String sql = in.readLine();
String result = myDbase.execute(sql);
out.writeBytes(result);
request.close();
} // try
catch(IOException ioe) {} // catch
return false;
} // processRequest
} // SQLHandler
The SQL handler factory creates the database object, then passes it to the SQL handlers it
creates:
class SQLHandlerFactory implements HandlerFactory
{
Database myDbase; // see Interfacing with Databases section
public SQLHandlerFactory(String dm, String url,
String user, String pswd)
{
myDbase = new Database(dm, url, user, pswd);
}
public RequestHandler makeHandler(Socket s, int id)
{
return new SQLHandler(s, id, myDbase);
}
} // SQLHandlerFactory
The following instructions create and start a database server at port 2222 on
jupiter.sjsu.edu:
HandlerFactory factory =
new SQLHandlerFactory("sun.jdbc.odbc.JdbcOdbcDriver",
"jdbc:odbc:ODBC_Addresses",
"smith",
"foobar");
Server dbaseServer = new Server("jupiter.sjsu.edu", 2222, factory);