耶和华对挪亚说:“你和你的全家都要进入方舟,因为在这世代中,我见你在我面前是义人。凡洁净的畜类,你要带七公七母;不洁净的畜类,你要带一公一母;空中的飞鸟也要带七公七母,可以留种,活在全地上;因为再过七天,我要降雨在地上四十昼夜,把我所造的各种活物都从地上除灭。”
《创世记》 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,一张“世界图片”,如下图。
其中index.html的文件内容:
Web容器示例
index.html的内容!
启动“服务器”,就可以在浏览器里输入相应路径访问,请求“服务器”上的“世界地图”时,返回如下图:
请求index.html,返回的结果,如下图:
在两次访问后时,控制台的输出信息:
与客户端建立链接: /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的规范,有很多的机制,加了很多的功能,实现时也采用了很多的巧妙设计等。