可扩展的多线程通用Server框架

本文介绍了一个可扩展的多线程通用Server框架的设计与实现。该框架能够监听多个端口,接收连接请求,并通过多线程方式为客户端提供服务。支持并发连接限制及日志记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可扩展的多线程通用Server框架 打印

/*
  * Copyright (c) 2000 David Flanagan.  All rights reserved.
  * This code is from the book Java Examples in a Nutshell, 2nd Edition.
  * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
  * You may study, use, and modify it for any non-commercial purpose.
  * You may distribute it non-commercially as long as you retain this notice.
  * For a commercial use license, or to purchase the book (recommended),
  * visit http://www.davidflanagan.com/javaexamples2.
  */

 

import  java.io.BufferedReader;
import  java.io.BufferedWriter;
import  java.io.IOException;
import  java.io.InputStream;
import  java.io.InputStreamReader;
import  java.io.InterruptedIOException;
import  java.io.OutputStream;
import  java.io.OutputStreamWriter;
import  java.io.PrintWriter;
import  java.net.ServerSocket;
import  java.net.Socket;
import  java.util.Date;
import  java.util.HashMap;
import  java.util.HashSet;
import  java.util.Iterator;
import  java.util.Map;
import  java.util.Set;
import  java.util.StringTokenizer;

/**
  * This class is a generic framework for a flexible, multi-threaded server. It
  * listens on any number of specified ports, and, when it receives a connection
  * on a port, passes input and output streams to a specified Service object
  * which provides the actual service. It can limit the number of concurrent
  * connections, and logs activity to a specified stream.
  */
public class  Server  {
   /**
    * A main() method for running the server as a standalone program. The
    * command-line arguments to the program should be pairs of servicenames and
    * port numbers. For each pair, the program will dynamically load the named
    * Service class, instantiate it, and tell the server to provide that
    * Service on the specified port. The special -control argument should be
    * followed by a password and port, and will start special server control
    * service running on the specified port, protected by the specified
    * password.
    */
   public static  void  main ( String []  args ) {
     try  {
       if  ( args.length <  2 // Check number of arguments
         throw new  IllegalArgumentException ( "Must specify a service" ) ;

       // Create a Server object that uses standard out as its log and
       // has a limit of ten concurrent connections at once.
       Server s =  new  Server ( System.out,  10 ) ;

       // Parse the argument list
       int  i =  0 ;
       while  ( i < args.length ) {
         if  ( args [ i ] .equals ( "-control" )) {  // Handle the -control arg
           i++;
           String password = args [ i++ ] ;
           int  port = Integer.parseInt ( args [ i++ ]) ;
           // add control service
           s.addService ( new  Control ( s, password ) , port ) ;
         else  {
           // Otherwise start a named service on the specified port.
           // Dynamically load and instantiate a Service class
           String serviceName = args [ i++ ] ;
           Class  serviceClass =  Class .forName ( serviceName ) ;
           Service service =  ( Service serviceClass.newInstance () ;
           int  port = Integer.parseInt ( args [ i++ ]) ;
           s.addService ( service, port ) ;
         }
       }
     catch  ( Exception e ) {  // Display a message if anything goes wrong
       System.err.println ( "Server: "  + e ) ;
       System.err.println ( "Usage: java Server "
           "[-control <password> <port>] "
           "[<servicename> <port> ... ]" ) ;
       System.exit ( 1 ) ;
     }
   }

   // This is the state for the server
   Map services;  // Hashtable mapping ports to Listeners

   Set connections;  // The set of current connections

   int  maxConnections;  // The concurrent connection limit

   ThreadGroup threadGroup;  // The threadgroup for all our threads

   PrintWriter logStream;  // Where we send our logging output to

   /**
    * This is the Server() constructor. It must be passed a stream to send log
    * output to (may be null), and the limit on the number of concurrent
    * connections.
    */
   public  Server ( OutputStream logStream,  int  maxConnections ) {
     setLogStream ( logStream ) ;
     log ( "Starting server" ) ;
     threadGroup =  new  ThreadGroup ( Server. class .getName ()) ;
     this .maxConnections = maxConnections;
     services =  new  HashMap () ;
     connections =  new  HashSet ( maxConnections ) ;
   }

   /**
    * A public method to set the current logging stream. Pass null to turn
    * logging off
    */
   public synchronized  void  setLogStream ( OutputStream out ) {
     if  ( out !=  null )
       logStream =  new  PrintWriter ( out ) ;
     else
       logStream =  null ;
   }

   /** Write the specified string to the log */
   protected synchronized  void  log ( String s ) {
     if  ( logStream !=  null ) {
       logStream.println ( "["  new  Date ()  "] "  + s ) ;
       logStream.flush () ;
     }
   }

   /** Write the specified object to the log */
   protected  void  log ( Object o ) {
     log ( o.toString ()) ;
   }

   /**
    * This method makes the server start providing a new service. It runs the
    * specified Service object on the specified port.
    */
   public synchronized  void  addService ( Service service,  int  port )
       throws  IOException  {
     Integer key =  new  Integer ( port ) // the hashtable key
     // Check whether a service is already on that port
     if  ( services.get ( key !=  null )
       throw new  IllegalArgumentException ( "Port "  + port
           " already in use." ) ;
     // Create a Listener object to listen for connections on the port
     Listener listener =  new  Listener ( threadGroup, port, service ) ;
     // Store it in the hashtable
     services.put ( key, listener ) ;
     // Log it
     log ( "Starting service "  + service.getClass () .getName ()  " on port "
         + port ) ;
     // Start the listener running.
     listener.start () ;
   }

   /**
    * This method makes the server stop providing a service on a port. It does
    * not terminate any pending connections to that service, merely causes the
    * server to stop accepting new connections
    */
   public synchronized  void  removeService ( int  port ) {
     Integer key =  new  Integer ( port ) // hashtable key
     // Look up the Listener object for the port in the hashtable
     final  Listener listener =  ( Listener services.get ( key ) ;
     if  ( listener ==  null )
       return ;
     // Ask the listener to stop
     listener.pleaseStop () ;
     // Remove it from the hashtable
     services.remove ( key ) ;
     // And log it.
     log ( "Stopping service "  + listener.service.getClass () .getName ()
         " on port "  + port ) ;
   }

   /**
    * This nested Thread subclass is a "listener". It listens for connections
    * on a specified port (using a ServerSocket) and when it gets a connection
    * request, it calls the servers addConnection() method to accept (or
    * reject) the connection. There is one Listener for each Service being
    * provided by the Server.
    */
   public class  Listener  extends  Thread  {
     ServerSocket listen_socket;  // The socket to listen for connections

     int  port;  // The port we're listening on

     Service service;  // The service to provide on that port

     volatile  boolean  stop =  false // Whether we've been asked to stop

     /**
      * The Listener constructor creates a thread for itself in the
      * threadgroup. It creates a ServerSocket to listen for connections on
      * the specified port. It arranges for the ServerSocket to be
      * interruptible, so that services can be removed from the server.
      */
     public  Listener ( ThreadGroup group,  int  port, Service service )
         throws  IOException  {
       super ( group,  "Listener:"  + port ) ;
       listen_socket =  new  ServerSocket ( port ) ;
       // give it a non-zero timeout so accept() can be interrupted
       listen_socket.setSoTimeout ( 600000 ) ;
       this .port = port;
       this .service = service;
     }

     /***********************************************************************
      * This is the polite way to get a Listener to stop accepting
      * connections
      **********************************************************************/
     public  void  pleaseStop () {
       this .stop =  true // Set the stop flag
       this .interrupt () // Stop blocking in accept()
       try  {
         listen_socket.close () ;
       // Stop listening.
       catch  ( IOException e ) {
       }
     }

     /**
      * A Listener is a Thread, and this is its body. Wait for connection
      * requests, accept them, and pass the socket on to the addConnection
      * method of the server.
      */
     public  void  run () {
       while  ( !stop ) {  // loop until we're asked to stop.
         try  {
           Socket client = listen_socket.accept () ;
           addConnection ( client, service ) ;
         catch  ( InterruptedIOException e ) {
         catch  ( IOException e ) {
           log ( e ) ;
         }
       }
     }
   }

   /**
    * This is the method that Listener objects call when they accept a
    * connection from a client. It either creates a Connection object for the
    * connection and adds it to the list of current connections, or, if the
    * limit on connections has been reached, it closes the connection.
    */
   protected synchronized  void  addConnection ( Socket s, Service service ) {
     // If the connection limit has been reached
     if  ( connections.size ()  >= maxConnections ) {
       try  {
         // Then tell the client it is being rejected.
         PrintWriter out =  new  PrintWriter ( s.getOutputStream ()) ;
         out.print ( "Connection refused; "
             "the server is busy; please try again later.\n" ) ;
         out.flush () ;
         // And close the connection to the rejected client.
         s.close () ;
         // And log it, of course
         log ( "Connection refused to "
             + s.getInetAddress () .getHostAddress ()  ":"
             + s.getPort ()  ": max connections reached." ) ;
       catch  ( IOException e ) {
         log ( e ) ;
       }
     else  // Otherwise, if the limit has not been reached
       // Create a Connection thread to handle this connection
       Connection c =  new  Connection ( s, service ) ;
       // Add it to the list of current connections
       connections.add ( c ) ;
       // Log this new connection
       log ( "Connected to "  + s.getInetAddress () .getHostAddress ()  ":"
           + s.getPort ()  " on port "  + s.getLocalPort ()
           " for service "  + service.getClass () .getName ()) ;
       // And start the Connection thread to provide the service
       c.start () ;
     }
   }

   /**
    * A Connection thread calls this method just before it exits. It removes
    * the specified Connection from the set of connections.
    */
   protected synchronized  void  endConnection ( Connection c ) {
     connections.remove ( c ) ;
     log ( "Connection to "  + c.client.getInetAddress () .getHostAddress ()  ":"
         + c.client.getPort ()  " closed." ) ;
   }

   /** Change the current connection limit */
   public synchronized  void  setMaxConnections ( int  max ) {
     maxConnections = max;
   }

   /**
    * This method displays status information about the server on the specified
    * stream. It can be used for debugging, and is used by the Control service
    * later in this example.
    */
   public synchronized  void  displayStatus ( PrintWriter out ) {
     // Display a list of all Services that are being provided
     Iterator keys = services.keySet () .iterator () ;
     while  ( keys.hasNext ()) {
       Integer port =  ( Integer keys.next () ;
       Listener listener =  ( Listener services.get ( port ) ;
       out.print ( "SERVICE "  + listener.service.getClass () .getName ()
           " ON PORT "  + port +  "\n" ) ;
     }

     // Display the current connection limit
     out.print ( "MAX CONNECTIONS: "  + maxConnections +  "\n" ) ;

     // Display a list of all current connections
     Iterator conns = connections.iterator () ;
     while  ( conns.hasNext ()) {
       Connection c =  ( Connection conns.next () ;
       out.print ( "CONNECTED TO "
           + c.client.getInetAddress () .getHostAddress ()  ":"
           + c.client.getPort ()  " ON PORT "
           + c.client.getLocalPort ()  " FOR SERVICE "
           + c.service.getClass () .getName ()  "\n" ) ;
     }
   }

   /**
    * This class is a subclass of Thread that handles an individual connection
    * between a client and a Service provided by this server. Because each such
    * connection has a thread of its own, each Service can have multiple
    * connections pending at once. Despite all the other threads in use, this
    * is the key feature that makes this a multi-threaded server
    * implementation.
    */
   public class  Connection  extends  Thread  {
     Socket client;  // The socket to talk to the client through

     Service service;  // The service being provided to that client

     /**
      * This constructor just saves some state and calls the superclass
      * constructor to create a thread to handle the connection. Connection
      * objects are created by Listener threads. These threads are part of
      * the server's ThreadGroup, so all Connection threads are part of that
      * group, too.
      */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值