耶和华对挪亚说:“你和你的全家都要进入方舟,因为在这世代中,我见你在我面前是义人。凡洁净的畜类,你要带七公七母;不洁净的畜类,你要带一公一母;空中的飞鸟也要带七公七母,可以留种,活在全地上;因为再过七天,我要降雨在地上四十昼夜,把我所造的各种活物都从地上除灭。”

《创世记》 7:1-4 家,一个温暖的名词。每个人都期望着有拥有这么一个避风港。在孤苦无助的时候,可以在这里得到抚慰。漫漫人生路,行走疲乏时,可以在这里休息片刻。家,就像一个容器,即可以容纳你的兴奋、欢笑,也可以接纳你的无奈和眼泪。 拥有了家,就像拥有了归宿,拥有了盼望,拥有了前进了动力。 无独有偶,Web应用也有他们自己家。这家就是Web容器,即俗称的Web服务器、或Http服务器之类的名词。不管给他取一个什么样的名字,究其本质,他们是Web应用的家。家是由人建立的,无人不成家。人是生活在家庭里的,没有家人的就在成了流浪的孤儿。同情同理,Web应用离不开Web容器。Web应用必须运行在Web服务器里,必须在这个家里接人待物。Web容器也离不开Web应用。没了Web应用,Web服务器就如水中没了鱼。水不在深,有鱼则灵。水中没了鱼,水固然还是那水,却只是一潭死水。 家建好了,每一个新的成员来到,某些生活用品已经拥有可以与其他人共有,毕竟是一家人。可有一些生活用品必须得为新人单独准备。同样的,Web容器,新的成员来到时,即发布新的应用时,要提供一些配置,诸如数据源等。如果已然有配置,则可以与其他公用。可访问路径,就像人名必须得单独起一样,也必须得单独配置。 文学家说,人生是一场旅行。<<圣经>>也时常提醒我们是寄居的。“亲爱的弟兄啊,你们是客旅,是寄居的。”(《彼得前书》 2:11)。在“客旅”、在“寄居”的人生岁月里,我们可能会面临搬家。“人非草木,孰能无情?”日久情深,在一个地方居久了,舍不得离开,所以每次搬家多少人有些伤感。不过,或许我们庆幸,因为我们没有像挪亚一样,把家搬到自己造的方舟里,待到在里面住了一段时间出来之后,再次来到他们曾经居住了数百年的家乡,除了一家八口,其他乡邻旧坊全没了,陆地也是刚被洪水浸泡、扫荡过,一片零乱。然而,话说回来,相对那个时代的其他人来说,挪亚算是有福的。在洪水来临之前,上帝就提前告诉他,要他造一个方舟、造一个家、造一个容器来免掉自己被洪水淹没。所以,在洪水来时,正国为有了这么一个临时的家、临时的容器才挽救了他一家八口。

程序语言翻译

海水冲出,如出胎胞,那时,谁将它关闭呢? 是我用云彩当海的衣服,用幽暗当包裹它的布, 为它定界限,又安门和闩, ‘你只可到这里,不可越过,你狂傲的浪要到此止住。’ 《约伯记》 38:8-11

当耶和华神,不再为海水定界限,打开它的门和闩时,可以想象海水漫上陆地那种场景,恐怕远胜过前不久日本的大地震。再加之地上的泉源裂开、天上的连续不断暴雨,其情其景,似乎整个世界就是一片汪洋大海。方舟在海面上晃荡,整个世界只有方舟里,对人、对任何动物才是安全的。出了方舟,遍地都是洪水。

代码清单1:NoahsArk类源代码

package com;

/** * 挪亚方舟 */ public class NoahsArk {

/**
 * 方舟里面
 */
public void inside(){
    System.out.println("方舟里面,挪亚一家八口安然无恙。。。");
}


/**
 * 方向外面
 */
public void outside(){
    System.out.println("方舟外面,遍地都是洪水。。。");
}

}

洪水退后,方舟停靠在亚拉腊山上。据说,有人乘飞机从上空飞过时看见,甚至还有个别的探险家从那冰山雪地时锯出一小块木头下来。依此,或许专家可以更好的推断方舟的构造结构等。
那么,回到自身,作为Web开发的“专家”,我们似乎也应该有义务去明白Web容器的内部构造结构、或实现原理。明白了其大概实现明理,遇到突发问题,以至于不会毫无头绪,病急乱投医。这正如我们归属于一个家,摸清了这个家的其他成员的性格,遇到问题时因为彼此理解就能化大为小,化小为无了。
Http服务器很多,Java方面的就有诸如Tomcat、WebSphere、JBoss等好几种。尽管如此,但只要明白一个,举一反三、触类旁通,其他的基本不知道大概是怎么一回事。Tomcat因为是主流,用的人比较多。本身是那玲珑剔透型的,研究起来方便。琢磨了些Tomcat的实现原理,其底层是采用Java Socket实现的。下面,我们同样可以用Java Socket做一个简单的Http服务器,可以接收简单的html、jpg的请求进行处理。

代码清单1:HTTPServerDemo类源代码

/** * Web容器示例 */ package container;

import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;

/** * 一个简单的处理http请求的Web服务端的示例 * */ public class HTTPServerDemo {

/**
 * 实际处理请求的方法
 * @param socket 即监听的端口
 */
public static void service(Socket socket) throws Exception {

    System.out.println("--------------本次请求处理开始---------------------");

    //得到请求的输入 流
    InputStream socketIn = socket.getInputStream();

    Thread.sleep(500);

    //读取请求的输入内容,并封装到字节数组buffer中
    int size = socketIn.available();
    byte[] buffer = new byte[size];
    socketIn.read(buffer);

    //打印请求的输入内容,即http请求报文
    String request = new String(buffer);
    System.out.println("request:"+request);


    //按照http请求的规范格式,解析请求内容。
    //第一行请求信息,规范为:请求方法(get或post等)+空格+请求内容(url)+空格+通讯协议(http)。
    //第一行请求信息示例:GET /index.html HTTP/1.1
    String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));

    //第一行请求信息以空格分割成字符串数组
    String[] parts = firstLineOfRequest.split(" ");

    //数组中的第二个值,即请求的URL。
    String uri = parts[1];

    //请求的文件类型
    String contentType;

    //请求的是网页html,或txt文件
    if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1
            || uri.indexOf("txt") != -1) {

        contentType = "text/html";

    //请求的是图片
    } else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1 
        || uri.indexOf("gif") != -1) {

        contentType = "image/jpeg";

    //只是为了演示,其他的暂时不作处理,设为空
    }  else {
        contentType = "";
    }

    //拼装服务器针对当前请求要返回的头信息
    String responseFirstLine = "HTTP/1.1 200 OK\r\n";
    String responseHeader = "Content-Type:" + contentType + "\r\n\r\n";

    System.out.println("服务器返回的内容:");
    System.out.print(responseFirstLine);
    System.out.print(responseHeader);

    //得到输入流
    OutputStream socketOut = socket.getOutputStream();    

    //将基本的返回信息打印到输入流中
    socketOut.write(responseFirstLine.getBytes());
    socketOut.write(responseHeader.getBytes());

    //得到请求的文件路径
    String localPath = uri.substring(1);
    System.out.println("请求的文件路径是:"+localPath);

    //读取请求的文件,并写到输入流中
    InputStream in = HTTPServerDemo.class.getResourceAsStream(localPath);
    int len = 0;
    buffer = new byte[128];
    if(in!=null){
        while ((len = in.read(buffer)) != -1) {
            socketOut.write(buffer, 0, len);
        }    
    }else{
        String message = "您请求的路径不对!";
        socketOut.write(message.getBytes());
    }

    Thread.sleep(1000);

    //关闭socket
    socket.close();

    System.out.println("--------------本次请求处理结束---------------------");
    System.out.println();
}


/**
 * 主程序入口
 * @param args
 * @throws Exception
 */
public static void main(String[] args)throws Exception{

    //监听的端口号
    int port = 9999;

    ServerSocket serverSocket;
    serverSocket = new ServerSocket(port);

    System.out.println("服务器正在监听端口:" + port);

    //用死循环来处理。
    while (true) {
        //监听从9999端口过来的请求
        final Socket socket = serverSocket.accept();
        System.out.println("与客户端建立链接: " + socket.getInetAddress()+ ":" + socket.getPort());

        //调用实际处理客户端的请求的方法
        service(socket);
    }

}

}

“服务器”写好了,再准备两个访问的示例文件。文件准备,一个简单的html,一张“世界图片”,如下图。

enter image description here

其中index.html的文件内容: Web容器示例 index.html的内容! 启动“服务器”,就可以在浏览器里输入相应路径访问,请求“服务器”上的“世界地图”时,返回如下图: enter image description here 请求index.html,返回的结果,如下图: enter image description here 在两次访问后时,控制台的输出信息: 与客户端建立链接: /127.0.0.1:1529 --------------本次请求处理开始--------------------- request:GET /world.jpg HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, / Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) Host: localhost:9999 Connection: Keep-Alive

服务器返回的内容: HTTP/1.1 200 OK Content-Type:image/jpeg

请求的文件路径是:world.jpg --------------本次请求处理结束---------------------

与客户端建立链接: /127.0.0.1:1531 --------------本次请求处理开始--------------------- request:GET /index.html HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, / Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) Host: localhost:9999 Connection: Keep-Alive

服务器返回的内容: HTTP/1.1 200 OK Content-Type:text/html

请求的文件路径是:index.html --------------本次请求处理结束---------------------

    就这样,一个非常简单的http服务器就完成了,可以处理简单的静态html和图片请求。自然,tomcat远比这复杂多。按照J2EE的规范,有很多的机制,加了很多的功能,实现时也采用了很多的巧妙设计等。