zoukankan      html  css  js  c++  java
  • tomcat解析之简单web服务器(图)

    链接地址:http://gogole.iteye.com/blog/587163

    之前有javaeyer推荐了一本书《how tomcat works》,今天晚上看了看,确实不错,第一眼就着迷了。 于是乎就学着书上的例子敲了敲,学会了一个简单web服务器的大概实现,当然,这个简直就无法称之为web服务器,但是也算是走进web服务器的第一步吧。

         这篇文章仅限于学习记录,文笔凌乱之处,还望各位见谅。 OK,下面进入正题;

          开始之前,首先我们要清楚以下几个内容。

         首先,一个最简单服务器包括三个部分:
         web服务器----HttpServer 
                    请求---Request
                    响应---Response

         这个服务器如何使用: 
          1. 请求静态资源 通过 http://localhost:8090/index.html   
                        成功 则返回页面, 否则会返回 File Not Found 的错误信息.
           PS:比如上面的index.html 必须放在一个webroot目录下.
         
          2. 服务器的关闭通过uri来处理
            通过http://lcoalhost:8090/SHUTDOWN 这个Uri来停止服务器.

        
       需要注意的其他几个知识点: 
          1. HTTP/1.1 协议的知识。  比如 请求,响应的结构。 发送与接收形式等.
          2. Java中网络的相关只是     ServerSocket 与 Socket 的使用.

     OK, 放上代码, 代码只有3个类,都挺简单的, 这个应用只是一个最最简单的雏形:

     主类: HttpServer

    Java代码  收藏代码
    1. package chapter1.simplewebserver;  
    2.   
    3. import java.io.File;  
    4. import java.io.InputStream;  
    5. import java.io.OutputStream;  
    6. import java.net.InetAddress;  
    7. import java.net.InterfaceAddress;  
    8. import java.net.ServerSocket;  
    9. import java.net.Socket;  
    10.   
    11. /** 
    12.  *  
    13.  *  下午11:59:29 
    14.  * @author gogole_09 
    15.  * 简单web服务器 
    16.  */  
    17. public class HttpServer {  
    18.     //定位到webroot目录  
    19.     public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";  
    20.     //停止命令  
    21.     private static final String SHUTDOWN_COMMAND="/SHUTDOWN";  
    22.       
    23.     //是否接收到了关闭命令  
    24.     private boolean shutdown=false;  
    25.       
    26.     /** 
    27.      * 等待命令 
    28.      */  
    29.     public void await(){  
    30.         ServerSocket serverSocket=null;  
    31.         int port=8090;  
    32.         try {  
    33.             serverSocket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));  
    34.         } catch (Exception e) {  
    35.             e.printStackTrace();  
    36.             System.exit(1);  
    37.         }  
    38.           
    39.         //监听端口,等待请求。  
    40.         while(!shutdown){  
    41.             Socket socket=null;  
    42.             InputStream input=null;  
    43.             OutputStream output=null;  
    44.             try {  
    45.                 socket=serverSocket.accept();  
    46.                 //输入流 有过jsp编程的哥们都知道这是干嘛的  
    47.                 input=socket.getInputStream();  
    48.                 //输出流  
    49.                 output=socket.getOutputStream();  
    50.                   
    51.                 //构建请求  
    52.                 Request request=new Request(input);  
    53.                 //解析请求信息  
    54.                 request.parse();  
    55.                   
    56.                 //响应类  
    57.                 Response response=new Response(output);  
    58.                 response.setRequest(request);  
    59.                 response.sendStaticResource();  
    60.                 //处理完毕,关闭   
    61.                 socket.close();  
    62.                   
    63.                 //检查提供的URI是否为shutdown命令  
    64.                 shutdown=request.getUri().equals(SHUTDOWN_COMMAND);  
    65.                   
    66.             } catch (Exception e) {  
    67.                 e.printStackTrace();  
    68.                 continue;  
    69.             }  
    70.         }  
    71.           
    72.     }  
    73.       
    74.     public static void main(String[] args) {  
    75.         HttpServer server=new HttpServer();  
    76.         server.await();  
    77.     }  
    78.       
    79. }  

    接下来是请求类: 

       Request

    Java代码  收藏代码
    1. package chapter1.simplewebserver;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5.   
    6. /** 
    7.  *  
    8.  *  上午12:09:36 
    9.  * @author gogole_09 
    10.  *  简单服务器的请求类 
    11.  */  
    12. public class Request {  
    13.   
    14.     private InputStream input;  
    15.     private String uri;  
    16.       
    17.     public Request(InputStream in) {  
    18.             this.input=in;  
    19.     }  
    20.       
    21.     /** 
    22.      * 解析Uri操作 
    23.      */  
    24.     public void parse() {  
    25.         StringBuffer buf=new StringBuffer();  
    26.         int i;  
    27.         byte[] buffer=new byte[2048];  
    28.         try {  
    29.             i=input.read(buffer);  
    30.         } catch (IOException e) {  
    31.                 e.printStackTrace();  
    32.                 i=-1;  
    33.         }  
    34.           
    35.         for(int j=0;j<i;j++){  
    36.             buf.append((char)buffer[j]);  
    37.         }  
    38.         System.out.println(buf.toString());  
    39.         uri=parseUri(buf.toString());  
    40.     }  
    41.   
    42.       
    43.     /** 
    44.      * 解析Uri   
    45.      *  为什么要以' '为做标识 , 这里需要了解HTTP协议的相关结构; 
    46.      *   一个请求行 以 请求方法开头 + 请求URI+ 请求协议版本 + CRLF字符结束 
    47.      *    比如,你请求index.html 用GET方式 ,那么Uri形式为: 
    48.      *     
    49.      *    GET /index.html HTTP/1.1 
    50.      *     
    51.      *     
    52.      * @param requestString 
    53.      * @return 
    54.      */  
    55.     private String parseUri(String requestString){  
    56.         int index1,index2;  
    57.         index1=requestString.indexOf(' ');  
    58.         if(index1!=-1){  
    59.             index2=requestString.indexOf(' ',index1+1);  
    60.             if(index2>index1){  
    61.                 return requestString.substring(index1+1,index2);  
    62.             }  
    63.         }  
    64.         return null;  
    65.     }  
    66.       
    67.     public String getUri() {  
    68.           
    69.         return uri;  
    70.     }  
    71.   
    72. }  

     有了请求了,服务器就得响应啊,试想,我发个请求没反应,你的第一反应是不是想砸电脑呢?

     OK,Response类来了。

    Java代码  收藏代码
    1. package chapter1.simplewebserver;  
    2.   
    3. import java.io.File;  
    4. import java.io.FileInputStream;  
    5. import java.io.IOException;  
    6. import java.io.OutputStream;  
    7.   
    8. /** 
    9.  *  
    10.  * 上午12:10:58 
    11.  *  
    12.  * @author gogole_09 简易服务器的响应类 
    13.  */  
    14. public class Response {  
    15.   
    16.     /** 
    17.      * HTTP响应格式:= Status-Line 
    18.      * *((general-header|response-header|entity-header)CRLF) CRLF [message-body] 
    19.      * Status-Line=HTTP-Version SP(空格) Status-Code SP Reason-Phrase CRLF 
    20.      *  
    21.      */  
    22.   
    23.     private static final int BUFFER_SIZE = 1024;  
    24.     Request request;  
    25.     private OutputStream output;  
    26.   
    27.     public Response(OutputStream output) {  
    28.         this.output = output;  
    29.     }  
    30.   
    31.     public void setRequest(Request request) {  
    32.         this.request = request;  
    33.     }  
    34.   
    35.     /** 
    36.      * 服务器解析并发送静态资源 
    37.      * @throws IOException 
    38.      */  
    39.     public void sendStaticResource() throws IOException {  
    40.         byte[] bytes = new byte[BUFFER_SIZE];  
    41.         FileInputStream fis = null;  
    42.         try {  
    43.             File file = new File(HttpServer.WEB_ROOT, request.getUri());  
    44.             if (file.exists()) {  
    45.                 fis = new FileInputStream(file);  
    46.                 int ch = fis.read(bytes, 0, BUFFER_SIZE);  
    47.                 while (ch != -1) {  
    48.                     output.write(bytes, 0, ch);  
    49.                     ch = fis.read(bytes, 0, BUFFER_SIZE);  
    50.                 }  
    51.             } else {  
    52.                 //文件没找到  
    53.                 String errorMessage = "HTTP/1.1 404 File Not Found "  
    54.                         + "Content-Type:text/html "  
    55.                         + "Content-Length:23 "+" <ht>File Not Found</h1>";  
    56.                 output.write(errorMessage.getBytes());  
    57.             }  
    58.         } catch (Exception e) {  
    59.             System.out.println(e.toString());  
    60.               
    61.         }finally{  
    62.             if(fis!=null)  
    63.                 fis.close();  
    64.         }  
    65.     }  
    66.   
    67. }  

     OK,现在这个应用可以运行了,但是你运行起来,没办法用,为什么, 因为还需要有用户请求的静态资源。

      在HttpServer类中,我们hard code了一个webroot目录,约定把所有的静态资源都放到这个目录下面的。

      下面我们就把这个目录建好,并放一点资源进去 ,我是这样放的, 如图:

      

     index.html 中 就只有一行代码:

      

     OK, 下面我们运行一下 HttpServer ,得到页面 如下图:

      

      在控制台,你将会看到程序打印出:

     

     接着,我们尝试请求一个不存在的资源:   我们会的到一个404的错误页面.

     

    OK, 一个简单的web服务器就完成了, 虽然简单,但是我们可以通过这个了解一个大概的流程。

     以及复习一下java net包与HTTP/1.1协议的一些知识。

    也希望这个篇文章对都有需要的人有帮助。

     PS: 最近有很多朋友都问这本书哪里有买,我本人并非看的纸质的,而是电子版的, 需要下载的可以去新浪共享频道找找, 我的是从哪里下来的, 中文书名就叫 <tomcat工作原理>

    分享到:  
    评论
    14 楼 nbnxyuyun 2010-06-12  
    public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";中user.dir如何设,我试了无法侦测到8090端口.
    13 楼 sw1982 2010-03-26  
    刚好受这个帖子推荐看到shutdownhook这个玩意,可是这个东东只能处理本身正在运行的进程退出。 
    现在的场景是我用sh脚本nohup启动一个java进程后台运行,然后怎么友好关闭这个线程呢?  最简单的就是向一个端口发stop请求。 

    gogole_09 写道
    sw1982 写道
    比较精华的地方是启动、关闭操作。。 
    占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理

    这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。
    12 楼 gogole_09 2010-03-25  
    sw1982 写道
    比较精华的地方是启动、关闭操作。。
    占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理

    这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。
    11 楼 gogole_09 2010-03-25  
    mercyblitz 写道
    Tomcat 不也是处理Socket吗?

    tomcat内部也是处理socket的, 不知有什么不对吗?
    10 楼 sw1982 2010-03-22  
    比较精华的地方是启动、关闭操作。。 
    占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
    9 楼 mercyblitz 2010-03-22  
    Tomcat 不也是处理Socket吗?
    8 楼 fantasybei 2010-02-18  
    也没你说得那么好,一般般吧,有些东西说得太罗嗦.
    7 楼 hellojinjie 2010-02-16  
    为何不用 NIO 来实现呢,,
    6 楼 smartman_jc 2010-02-10  
    不错!学习了,受益匪浅
    5 楼 gogole_09 2010-02-08  
    wanggp 写道
    应该是how tomcat works

    嘿嘿,是的。 书名写错了,马上修正。
    4 楼 elgs 2010-02-07  
    how tomcat works在中国买不到,这么好的书。
    3 楼 wanggp 2010-02-07  
    应该是how tomcat works
    2 楼 gogole_09 2010-02-06  
    曾经de迷茫 写道
    请问楼主浏览器上装的什么东东,看请求资源的那个?

    是Http Watch. 查看请求url,传递的参数,以及服务器返回数据的好东西。
    1 楼 曾经de迷茫 2010-02-06  
    请问楼主浏览器上装的什么东东,看请求资源的那个?
    如果一件事情你觉得难的完不成,你可以把它分为若干步,并不断寻找合适的方法。最后你发现你会是个超人。不要给自己找麻烦,但遇到麻烦绝不怕,更不要退缩。 电工查找电路不通点的最快方法是:分段诊断排除,快速定位。你有什么启示吗? 求知若饥,虚心若愚。 当你对一个事情掌控不足的时候,你需要做的就是“梳理”,并制定相应的规章制度,并使资源各司其职。
  • 相关阅读:
    C# 复制(深拷贝、浅拷贝)
    Nunit-Writing Tests
    Thread.Sleep vs. Task.Delay
    AutoMapper Getting started
    设计:抽象类类还是接口
    C++Primer第五版——习题答案详解(八)
    C++Primer第五版——习题答案详解(七)
    C++Primer第五版——习题答案详解(六)
    C++Primer第五版——习题答案详解(五)
    C/C++中的函数指针的使用与总结
  • 原文地址:https://www.cnblogs.com/wvqusrtg/p/5105434.html
Copyright © 2011-2022 走看看