• 用Java编写你自己的简单HTTP服务器


    来源:http://blog.csdn.net/yanghua_kobe/article/details/7296156

       HTTP是个大协议,完整功能的HTTP服务器必须响应资源请求,将URL转换为本地系统的资源名。响应各种形式的HTTP请求(GET、POST等)。处理不存在的文件请求,返回各种形式的状态码,解析MIME类型等。但许多特定功能的HTTP服务器并不需要所有这些功能。例如,很多网站只是想显示“建设中“的消息。很显然,Apache对于这样的网站是大材小用了。这样的网站完全可以使用只做一件事情的定制服务器。Java网络类库使得编写这样的单任务服务器轻而易举。 定制服务器不只是用于小网站。大流量的网站如Yahoo,也使用定制服务器,因为与一般用途的服务器相比,只做一件事情的服务器通常要快得多。针对某项任务来优化特殊用途的服务器很容易;其结果往往比需要响应很多种请求的一般用途服务器高效得多。例如,对于重复用于多页面或大流量页面中的图标和图片,用一个单独的服务器处理会更好(并且还可以避免在请求时携带不必要的Cookie,因而可以减少请求/响应数据,从而减少下载带宽,提升速度);这个服务器在启动时把所有图片文件读入内存,从RAM中直接提供这些文件,而不是每次请求都从磁盘上读取。此外,如果你不想在包含这些图片的页面请求之外单独记录这些图片,这个单独服务器则会避免在日志记录上浪费时间。

      

     1 import java.io.File;
     2 import java.io.IOException;
     3 import java.net.ServerSocket;
     4 import java.net.Socket;
     5 
     6 /**
     7  * @Title: JHTTP.java 
     8  * @Package  
     9  * @author 任伟
    10  * @date 2014-12-4 下午1:30:07 
    11  * @version V1.0  
    12  */
    13 
    14 /**
    15  * @ClassName: JHTTP
    16  * @Description: JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中
    17  * @author 任伟
    18  * @date 2014-12-4 下午1:30:07
    19  */
    20 public class JHTTP extends Thread {
    21     private File documentRootDirectory;                //文档根目录
    22     private String indexFileName = "index.html";    //引导文件
    23     private ServerSocket server;                    //Server
    24     private int numThreads = 50;                        //线程数量
    25 
    26     public JHTTP(File documentRootDirectory, int port, String indexFileName)
    27             throws IOException {
    28         if (!documentRootDirectory.isDirectory()) {
    29             throw new IOException(documentRootDirectory
    30                     + " does not exist as a directory ");
    31         }
    32         this.documentRootDirectory = documentRootDirectory;
    33         this.indexFileName = indexFileName;
    34         this.server = new ServerSocket(port);
    35     }
    36 
    37     private JHTTP(File documentRootDirectory, int port) throws IOException {
    38         this(documentRootDirectory, port, "index.html");
    39     }
    40 
    41     public void run() {
    42         //新建numThreads个请求处理线程,并开启线程
    43         for(int i=0; i<numThreads; i++){
    44             Thread t = new Thread(new RequestProcessor(documentRootDirectory, indexFileName));
    45             t.start();
    46         }
    47         System.out.println("Accepting connection on port "+server.getLocalPort());
    48         System.out.println("Document Root: "+documentRootDirectory);
    49         
    50         //无限循环接受请求,收到一个请求将其放入RequestProcessor的请求池
    51         while(true){
    52             try{
    53                 Socket request=server.accept();
    54                 RequestProcessor.processRequest(request);  
    55             }catch (Exception e) {
    56                 // TODO: handle exception
    57             }
    58         }
    59     }
    60 
    61     /**
    62      * @param args
    63      */
    64     public static void main(String[] args) {
    65         //设置文档根目录
    66         File docroot;
    67         try {
    68             docroot = new File(args[0]);
    69         } catch (ArrayIndexOutOfBoundsException e) {
    70             System.out.println("Usage: java JHTTP docroot port indexfile");
    71             return;
    72         }
    73         
    74         //读取端口号
    75         int port;
    76         try {
    77             port = Integer.parseInt(args[1]);
    78             if (port < 0 || port > 65535) {
    79                 port = 80;
    80             }
    81         } catch (Exception e) {
    82             port = 80;
    83         }
    84         
    85         //构造一个新的JHTTP线程并启动
    86         try {
    87             JHTTP webserver = new JHTTP(docroot, port);
    88             webserver.start();
    89         } catch (IOException e) {
    90             System.out.println("Server could not start because of an "
    91                     + e.getClass());
    92             System.out.println(e);
    93         }
    94 
    95     }
    96 
    97 }
    JHTTP.java
      1 import java.io.BufferedInputStream;
      2 import java.io.BufferedOutputStream;
      3 import java.io.DataInputStream;
      4 import java.io.File;
      5 import java.io.FileInputStream;
      6 import java.io.IOException;
      7 import java.io.InputStreamReader;
      8 import java.io.OutputStream;
      9 import java.io.OutputStreamWriter;
     10 import java.io.Reader;
     11 import java.io.Writer;
     12 import java.net.Socket;
     13 import java.util.Date;
     14 import java.util.LinkedList;
     15 import java.util.List;
     16 import java.util.StringTokenizer;
     17 
     18 /**
     19  * @Title: RequestProcessor.java 
     20  * @Package  
     21  * @author 任伟
     22  * @date 2014-12-4 下午1:42:22 
     23  * @version V1.0  
     24  */
     25 
     26 /**
     27  * @ClassName: RequestProcessor
     28  * @Description: 请求处理程序
     29  * @author 任伟
     30  * @date 2014-12-4 下午1:42:22
     31  */
     32 public class RequestProcessor implements Runnable {
     33     private static List pool = new LinkedList();
     34     private File documentRootDirectory;
     35     private String indexFileName = "index.html";
     36 
     37     // 构造方法
     38     public RequestProcessor(File documentRootDirectory, String indexFileName) {
     39         if (documentRootDirectory.isFile()) {
     40             throw new IllegalArgumentException();
     41         }
     42         this.documentRootDirectory = documentRootDirectory;
     43         try {
     44             this.documentRootDirectory = documentRootDirectory
     45                     .getCanonicalFile();
     46         } catch (IOException e) {
     47         }
     48 
     49         if (indexFileName != null) {
     50             this.indexFileName = indexFileName;
     51         }
     52     }
     53 
     54     // 向请求池加入请求
     55     public static void processRequest(Socket request) {
     56         synchronized (pool) {
     57             pool.add(pool.size(), request);
     58             pool.notifyAll();
     59         }
     60     }
     61 
     62     /*
     63      * (non-Javadoc)
     64      * 
     65      * @see java.lang.Runnable#run()
     66      */
     67     @Override
     68     public void run() {
     69         // 无限循环处理
     70         while (true) {
     71 
     72             // 先进性安全性检测,然后从连接池获取一个链接
     73             Socket connection;
     74             synchronized (pool) {
     75                 while (pool.isEmpty()) {
     76                     try {
     77                         pool.wait();
     78                     } catch (Exception e) {
     79                     }
     80                 }
     81                 connection = (Socket) pool.remove(0);
     82             }
     83 
     84             // 开始处理
     85             try {
     86                 // 获得输入输出流
     87                 OutputStream raw = new BufferedOutputStream(
     88                         connection.getOutputStream());
     89                 Writer out = new OutputStreamWriter(raw);
     90                 Reader in = new InputStreamReader(new BufferedInputStream(
     91                         connection.getInputStream()));
     92 
     93                 // 拼接请求字符串
     94                 StringBuffer request = new StringBuffer(80);
     95                 while (true) {
     96                     int c = in.read();
     97                     if (c == '	' || c == '
    ' || c == -1) {
     98                         break;
     99                     }
    100                     request.append((char) c);
    101                 }
    102 
    103                 // 记录日志 eg:
    104                 // localhost:port/a/b/c/index.html
    105                 // GET /a/b/c/index.html HTTP/1.1
    106                 String get = request.toString();
    107                 System.out.println(get);
    108 
    109                 // 分析请求
    110                 StringTokenizer st = new StringTokenizer(get);
    111                 String method = st.nextToken();// 请求的方法 GET
    112                 String fileName;// 请求的文件名
    113                 String version = "";// 协议版本
    114                 String contentType;// 相应返回的内容类型
    115 
    116                 if (method.equals("GET")) {// 方法是“GET”
    117                     fileName = st.nextToken();
    118                     if (fileName.endsWith("/")) {
    119                         fileName += indexFileName;
    120                     }
    121                     contentType = guessContentTypeFromName(fileName);
    122                     if (st.hasMoreTokens()) {
    123                         version = st.nextToken();
    124                     }
    125 
    126                     // 根据文件目录读出文件,并返回响应
    127                     File theFile = new File(documentRootDirectory,
    128                             fileName.substring(1, fileName.length()));
    129                     String root = documentRootDirectory.getPath();
    130                     if (theFile.canRead()
    131                             && theFile.getCanonicalPath().startsWith(root)) {// 读取文件成功
    132                         DataInputStream fis = new DataInputStream(
    133                                 new BufferedInputStream(new FileInputStream(
    134                                         theFile)));
    135                         byte[] theData = new byte[(int) theFile.length()];
    136                         fis.readFully(theData);
    137                         fis.close();
    138 
    139                         // HTTP请求,返回响应头
    140                         if (version.startsWith("HTTP")) {
    141                             out.write("HTTP/1.0 200 OK
    ");
    142                             Date now = new Date();
    143                             out.write("Date: " + now + "
    ");
    144                             out.write("Server: JHTTP 1.0
    ");
    145                             out.write("Content-length: " + theData.length
    146                                     + "
    ");
    147                             out.write("Content-Type: " + contentType
    148                                     + "
    
    ");
    149                             out.flush();
    150                         }
    151                         raw.write(theData);
    152                         raw.flush();
    153 
    154                     } else {// 读取文件不成功
    155                         if (version.startsWith("HTTP")) { // 是HTTP请求返回 响应头 404
    156                             out.write("HTTP/1.0 404 File Not Found
    ");
    157                             Date now = new Date();
    158                             out.write("Date: " + now + "
    ");
    159                             out.write("Server: JHTTP 1.0
    ");
    160                             out.write("Content-Type: text/html
    
    ");
    161                         }
    162                         out.write("<HTML>
    ");
    163                         out.write("<HEAD><TITLE>File Not Found</TITLE></HRAD>
    ");
    164                         out.write("<BODY>
    ");
    165                         out.write("<H1>HTTP Error 404: File Not Found</H1>");
    166                         out.write("</BODY></HTML>
    ");
    167                         out.flush();
    168                     }
    169                 } else {// 方法不是“GET”
    170                     if (version.startsWith("HTTP")) {
    171                         out.write("HTTP/1.0 501 Not Implemented
    ");
    172                         Date now = new Date();
    173                         out.write("Date: " + now + "
    ");
    174                         out.write("Server: JHTTP 1.0
    ");
    175                         out.write("Content-Type: text/html
    
    ");
    176                     }
    177                     out.write("<HTML>
    ");
    178                     out.write("<HEAD><TITLE>Not Implemented</TITLE></HRAD>
    ");
    179                     out.write("<BODY>
    ");
    180                     out.write("<H1>HTTP Error 501: Not Implemented</H1>");
    181                     out.write("</BODY></HTML>
    ");
    182                     out.flush();
    183                 }
    184 
    185             } catch (Exception e) {
    186                 // TODO: handle exception
    187             } finally {
    188                 try {
    189                     connection.close();
    190                 } catch (IOException e) {
    191                     e.printStackTrace();
    192                 }
    193             }
    194         }
    195 
    196     }
    197 
    198     // 根据文件名字猜测返回的文件的内容类型
    199     public static String guessContentTypeFromName(String name) {
    200         if (name.endsWith(".html") || name.endsWith(".htm")) {
    201             return "text/html";
    202         } else if (name.endsWith(".txt") || name.endsWith(".java")) {
    203             return "text/plain";
    204         } else if (name.endsWith(".class")) {
    205             return "application/octet-stream";
    206         } else if (name.endsWith(".gif")) {
    207             return "image/gif";
    208         } else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) {
    209             return "image/jpeg";
    210         } else if (name.endsWith(".png")) {
    211             return "image/png";
    212         } else {
    213             return "text/plain";
    214         }
    215     }
    216 
    217 }
    RequestProcessor.java

    测试结果:

    图1

      JHTTP类的main()方法根据args[0]设置文档的根目录。端口从args[1]读取,或者使用默认的80.然后构造一个新的JHTTP线程并启动。此JHTTP线程生成50个RequestProcessor线程处理请求,每个线程在可用时从RequestProcessor池获取入站连接请求。JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中。每个连接由下例所示的RequestProcessor类的run()方法处理。此方法将一直等待,直到从池中得到一个Socket。一旦得到Socket,就获取输入和输出流,并链接到阅读器和书写器。接着的处理,除了多出文档目录、路径的处理,其他的同单文件服务器。

      最后,花点时间考虑一下可以采用什么方法来优化此服务器。如果真的希望使用JHTTP运行高流量的网站,还可以做一些事情来加速此服务器。第一点也是最重要的一点就是使用即时编译器(JIT),如HotSpot。JIT可以将程序的性能提升大约一个数量级。第二件事就是实现智能缓存。记住接受的请求,将最频繁的请求文件的数据存储在Hashtable中,使之保存在内存中。使用低优先级的线程更新此缓存。

  • 相关阅读:
    excelhelp
    导入数据到GridView
    sql2005悠改sa
    状态存储管理 encode,decode,transfer
    Session持久化
    Microsoft SQL Server 2005 数据类型
    Cookie (HttpCookie的实例)
    role设计
    ajax
    poj 3370 Halloween treats 夜
  • 原文地址:https://www.cnblogs.com/renwei/p/4143138.html
Copyright © 2020-2023  润新知