?? abstractserver.java
字號(hào):
// This file contains material supporting section 3.8 of the textbook:// "Object Oriented Software Engineering" and is issued under the open-source// license found at www.lloseng.com package ocsf.server;import java.net.*;import java.io.*;/*** The <code> AbstractServer </code> class maintains a thread that waits* for connection attempts from clients. When a connection attempt occurs* it creates a new <code> ConnectionToClient </code> instance which* runs as a thread. When a client is thus connected to the* server, the two programs can then exchange <code> Object </code>* instances.<p>** Method <code> handleMessageFromClient </code> must be defined by* a concrete subclass. Several other hook methods may also be* overriden.<p>** Several public service methods are provided to applications that use* this framework, and several hook methods are also available<p>** Project Name: OCSF (Object Client-Server Framework)<p>** @author Dr Robert Laganière* @author Dr Timothy C. Lethbridge* @author François Bélanger* @author Paul Holden* @version February 2001 (2.12)* @see ocsf.server.ConnectionToClient*/public abstract class AbstractServer implements Runnable{ // INSTANCE VARIABLES ********************************************* /** * The server socket: listens for clients who want to connect. */ private ServerSocket serverSocket = null; /** * The connection listener thread. */ private Thread connectionListener; /** * The port number */ private int port; /** * The server timeout while for accepting connections. * After timing out, the server will check to see if a command to * stop the server has been issued; it not it will resume accepting * connections. * Set to half a second by default. */ private int timeout = 500; /** * The maximum queue length; i.e. the maximum number of clients that * can be waiting to connect. * Set to 10 by default. */ private int backlog = 10; /** * The thread group associated with client threads. Each member of the * thread group is a <code> ConnectionToClient </code>. */ private ThreadGroup clientThreadGroup; /** * Indicates if the listening thread is ready to stop. Set to * false by default. */ private boolean readyToStop = false;// CONSTRUCTOR ****************************************************** /** * Constructs a new server. * * @param port the port number on which to listen. */ public AbstractServer(int port) { this.port = port; this.clientThreadGroup = new ThreadGroup("ConnectionToClient threads") { // All uncaught exceptions in connection threads will // be sent to the clientException callback method. public void uncaughtException( Thread thread, Throwable exception) { clientException((ConnectionToClient)thread, exception); } }; }// INSTANCE METHODS ************************************************* /** * Begins the thread that waits for new clients. * If the server is already in listening mode, this * call has no effect. * * @exception IOException if an I/O error occurs * when creating the server socket. */ final public void listen() throws IOException { if (!isListening()) { if (serverSocket == null) { serverSocket = new ServerSocket(getPort(), backlog); } serverSocket.setSoTimeout(timeout); readyToStop = false; connectionListener = new Thread(this); connectionListener.start(); } } /** * Causes the server to stop accepting new connections. */ final public void stopListening() { readyToStop = true; } /** * Closes the server socket and the connections with all clients. * Any exception thrown while closing a client is ignored. * If one wishes to catch these exceptions, then clients * should be individually closed before calling this method. * The method also stops listening if this thread is running. * If the server is already closed, this * call has no effect. * * @exception IOException if an I/O error occurs while * closing the server socket. */ final synchronized public void close() throws IOException { if (serverSocket == null) return; stopListening(); try { serverSocket.close(); } finally { // Close the client sockets of the already connected clients Thread[] clientThreadList = getClientConnections(); for (int i=0; i<clientThreadList.length; i++) { try { ((ConnectionToClient)clientThreadList[i]).close(); } // Ignore all exceptions when closing clients. catch(Exception ex) {} } serverSocket = null; serverClosed(); } } /** * Sends a message to every client connected to the server. * This is merely a utility; a subclass may want to do some checks * before actually sending messages to all clients. This method * can be overriden, but if so it should still perform the general * function of sending to all clients, perhaps after some kind * of filtering is done. Any exception thrown while * sending the message to a particular client is ignored. * * @param msg Object The message to be sent */ public void sendToAllClients(Object msg) { Thread[] clientThreadList = getClientConnections(); for (int i=0; i<clientThreadList.length; i++) { try { ((ConnectionToClient)clientThreadList[i]).sendToClient(msg); } catch (Exception ex) {} } }// ACCESSING METHODS ------------------------------------------------ /** * Returns true if the server is ready to accept new clients. * * @return true if the server is listening. */ final public boolean isListening() { return (connectionListener != null); } /** * Returns an array containing the existing * client connections. This can be used by * concrete subclasses to implement messages that do something with * each connection (e.g. kill it, send a message to it etc.). * Remember that after this array is obtained, some clients * in this migth disconnect. New clients can also connect, * these later will not appear in the array. * * @return an array of <code>Thread</code> containing * <code>ConnectionToClient</code> instances. */ synchronized final public Thread[] getClientConnections() { Thread[] clientThreadList = new Thread[clientThreadGroup.activeCount()]; clientThreadGroup.enumerate(clientThreadList); return clientThreadList; } /** * Counts the number of clients currently connected. * * @return the number of clients currently connected. */ final public int getNumberOfClients() { return clientThreadGroup.activeCount(); } /** * Returns the port number. * * @return the port number. */ final public int getPort() { return port; } /** * Sets the port number for the next connection. * The server must be closed and restarted for the port * change to be in effect. * * @param port the port number. */ final public void setPort(int port) { this.port = port; } /** * Sets the timeout time when accepting connections. * The default is half a second. This means that stopping the * server may take up to timeout duration to actually stop. * The server must be stopped and restarted for the timeout * change to be effective. * * @param timeout the timeout time in ms. */ final public void setTimeout(int timeout) { this.timeout = timeout; } /** * Sets the maximum number of waiting connections accepted by the * operating system. The default is 20. * The server must be closed and restarted for the backlog * change to be in effect. * * @param backlog the maximum number of connections. */ final public void setBacklog(int backlog) { this.backlog = backlog; }// RUN METHOD ------------------------------------------------------- /** * Runs the listening thread that allows clients to connect. * Not to be called. */ final public void run() { // call the hook method to notify that the server is starting serverStarted(); try { // Repeatedly waits for a new client connection, accepts it, and // starts a new thread to handle data exchange. while(!readyToStop) { try { // Wait here for new connection attempts, or a timeout Socket clientSocket = serverSocket.accept(); // When a client is accepted, create a thread to handle // the data exchange, then add it to thread group synchronized(this) { ConnectionToClient c = new ConnectionToClient( this.clientThreadGroup, clientSocket, this); } } catch (InterruptedIOException exception) { // This will be thrown when a timeout occurs. // The server will continue to listen if not ready to stop. } } // call the hook method to notify that the server has stopped serverStopped(); } catch (IOException exception) { if (!readyToStop) { // Closing the socket must have thrown a SocketException listeningException(exception); } else { serverStopped(); } } finally { readyToStop = true; connectionListener = null; } }// METHODS DESIGNED TO BE OVERRIDDEN BY CONCRETE SUBCLASSES --------- /** * Hook method called each time a new client connection is * accepted. The default implementation does nothing. * @param client the connection connected to the client. */ protected void clientConnected(ConnectionToClient client) {} /** * Hook method called each time a client disconnects. * The default implementation does nothing. The method * may be overridden by subclasses but should remains synchronized. * * @param client the connection with the client. */ synchronized protected void clientDisconnected( ConnectionToClient client) {} /** * Hook method called each time an exception is thrown in a * ConnectionToClient thread. * The method may be overridden by subclasses but should remains * synchronized. * * @param client the client that raised the exception. * @param Throwable the exception thrown. */ synchronized protected void clientException( ConnectionToClient client, Throwable exception) {} /** * Hook method called when the server stops accepting * connections because an exception has been raised. * The default implementation does nothing. * This method may be overriden by subclasses. * * @param exception the exception raised. */ protected void listeningException(Throwable exception) {} /** * Hook method called when the server starts listening for * connections. The default implementation does nothing. * The method may be overridden by subclasses. */ protected void serverStarted() {} /** * Hook method called when the server stops accepting * connections. The default implementation * does nothing. This method may be overriden by subclasses. */ protected void serverStopped() {} /** * Hook method called when the server is clased. * The default implementation does nothing. This method may be * overriden by subclasses. When the server is closed while still * listening, serverStopped() will also be called. */ protected void serverClosed() {} /** * Handles a command sent from one client to the server. * This MUST be implemented by subclasses, who should respond to * messages. * This method is called by a synchronized method so it is also * implcitly synchronized. * * @param msg the message sent. * @param client the connection connected to the client that * sent the message. */ protected abstract void handleMessageFromClient( Object msg, ConnectionToClient client);// METHODS TO BE USED FROM WITHIN THE FRAMEWORK ONLY ---------------- /** * Receives a command sent from the client to the server. * Called by the run method of <code>ConnectionToClient</code> * instances that are watching for messages coming from the server * This method is synchronized to ensure that whatever effects it has * do not conflict with work being done by other threads. The method * simply calls the <code>handleMessageFromClient</code> slot method. * * @param msg the message sent. * @param client the connection connected to the client that * sent the message. */ final synchronized void receiveMessageFromClient( Object msg, ConnectionToClient client) { this.handleMessageFromClient(msg, client); }}// End of AbstractServer Class
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -