`
kayo
  • 浏览: 550849 次
  • 性别: Icon_minigender_1
  • 来自: 安徽
社区版块
存档分类
最新评论

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

阅读更多
可扩展的多线程通用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.
      */ <br
分享到:
评论

相关推荐

    bl-api-cloud,可扩展轻云服务端框架-易语言

    为轻量级可扩展的API服务端框架,主要用于响应http请求,开发者可通过开发自己的功能插件(.dll)进行加载以达到扩展。 丨前言 之前发过帖子《【框架】bl-api-cloud,高性能可扩展的API服务器》...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    Java的产生与流行是当今Internet发展的客观要求,Java是一门各方面性能都很好的编程语言,它的基本特点是简单、面向对象、分布式、解释的、健壮的、安全的、结构中立的、可移植的、性能很优异的、多线程的、动态的,...

    linux programming instances网络编程教程 附源代码

    全书由13章组成,内容涉及到Lindx系统编程基础、TCP/UDP协议、套接字编程概念及I/O模型、高级编程中需要用到的进程问通信同步、多路复用、多线程编程和一些高级套接字控制方法、IPv6介绍以及网络安全等。...

    C#程序设计(最新的培训教材)

    .NET框架是建立、配置和运行Web服务以及应用程序的多语言环境,是Microsoft的新一代Web应用程序开发平台,是.NET平台最关键的部分。它包含以下两个主要部分。 (1)通用语言运行库(Common Language Runtime—CLR) ...

    Embedded Appweb移植 安装 应用

    * Multi-threaded, event-driven core:Appweb 服务器采用多线程、事件驱动的核心架构,提供了高性能、高可靠性的 Web 服务。 * Dynamically loadable modules:Appweb 服务器支持动态加载模块,提供了高度的可扩展...

    asp.net知识库

    将 ASP.NET 2.0 应用程序服务配置为使用 SQL Server 2000 或 SQL Server 2005 ASP.NET 2.0 中的数据源控件 使用 ASP.NET 2.0 ObjectDataSource 控件 ASP.NET 2.0 的内部变化 使用SQL Cache Dependency 代替 ...

    JAVA上百实例源码以及开源项目源代码

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    JAVA上百实例源码以及开源项目

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    python入门到高级全栈工程师培训 第3期 附课件代码

    07 通过form向server端发送数据 08 form表单之select标签 09 table标签 第38章 01 css的四种引入方式 02 css的四种基本选择器 03 css的组合选择器 04 css的属性选择器 05 css的伪类 06 css的选择器优先级 07 css的...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    JAVA_API1.6文档(中文)

    javax.security.auth.login 此包提供可插入的验证框架。 javax.security.auth.spi 此包提供用于实现可插入验证模块的接口。 javax.security.auth.x500 此包包含应该用来在 Subject 中存储 X500 Principal 和 X500 ...

    java面试题

    答:Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式允许其service方法,一个实例可以服务于多个请求,并且其实例一般不会被销毁,而CGI对每个请求都产生新的进程,服务完后就销毁,所以效率上...

    【RT-Thread作品秀】基于rt-thread的小型蚯蚓养殖监控系统-电路方案

    软件框架说明多个采集线程实时读取环境中的温湿度,发送到stream消息队列;Onenet上传线程接收消息并上传到相应stream。当云端下发命令,触发Onenet_cmd_rsp_cb,在里面发送接收的控制命令到命令缓存邮箱,控制线程...

    分布式数据层TDDL.zip

    淘宝根据自己的业务特点开发了TDDL(Taobao Distributed Data Layer 外号:头都大了 ©_Ob)框架,主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制,它是一个基于集中式配置的 jdbc datasource实现,...

    超级有影响力霸气的Java面试题大全文档

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    java-servlet-api.doc

    在多线程的环境下,Servlet必须能处理许多同时发生的请求。例外的情况是这个Servlet执行了SingleThreadModel接口,如果是那样的话,Servlet只能同时处理一个请求。 Servlet依照Servlet引擎的映射来响应客户端的请求...

    Java 1.6 API 中文 New

    javax.security.auth.login 此包提供可插入的验证框架。 javax.security.auth.spi 此包提供用于实现可插入验证模块的接口。 javax.security.auth.x500 此包包含应该用来在 Subject 中存储 X500 Principal 和 X500 ...

Global site tag (gtag.js) - Google Analytics