该简易的J2EE WEB容器缺失很多功能,却可以提供给大家学习HTTP容器大致流程。

注:容器功能很少,只供学习。

1. 支持静态内容与Servlet,不支持JSP

2. 仅支持304/404

3. 该设计参考Jetty容器

GIT地址:https://git.oschina.net/redcode/jerry.git

一、HTTP请求处理流程:

HTTP包的解析直接使用Socket读取InputStream,再根据HTTP协议读取HTTP请求头于数据体,HTTP GET请求头类似如下:

GET / HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent:
Accept-Encoding: gzip, deflate
Host: www.baidu.com
Connection: Keep-Alive

1. 如GET / HTTP/1.1代表是GET 请求,请求路径为/,协议版本为HTTP 1.1,中间使用空格分隔,请求头每个属性一行,使用\n换行(WINDOWS为\r\n)。

当解析Socket的InputStream的时候首先读取第一行,代码类似如下:

BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) );
String reqCmd = br.readLine();
if(reqCmd == null){
return null; //数据包不正常,忽略
}
String[] cmds = reqCmd.split("\\s");

2. POST 请求包类似如下:

POST /login HTTP/1.1
Accept: */*
User-Agent:
Host:
Pragma: no-cache
Cookie:
Content-Length: 25 count=1&viewid=lNe3tRpyVj

请求头后换行,再封装POST请求数据:count=1&viewid0=lNe3tRpyVj

解析POST请求包时,读取请求头后再读取数据,存入Map中。检查请求类型如下:

//Request method check
if(!HttpMethod.isAccept(cmds[0])) {
return null;
}

接受的请求类型枚举:

public enum HttpMethod {
GET,
POST; public static boolean isAccept(String method) {
for(HttpMethod m : HttpMethod.values()) {
if(m.name().equals(method)) {
return true;
}
}
return false;
} public static HttpMethod getMethod(String method){
for(HttpMethod m : HttpMethod.values()) {
if(m.name().equals(method)) {
return m;
}
}
return null;
}
}

POST 请求需读取 Content-Length 属性,即需要知道POST包中的参数包大小,当TCP包被拆分通过几条链路到达目的地时,根据包长度使得服务端能合理的等待数据到来。

//Read headers
String line;
int contentLength = 0;
HashMap<String,String> headers = new HashMap<String, String>();
while( (line = br.readLine()) != null
&& !line.equals("") ) { int idx = line.indexOf(": ");
if(idx == -1) {
continue;
}
if(HttpHeaders.CONTENT_LENGTH.equals(line)) {
contentLength = Integer.parseInt(line.substring(idx+2).trim());
}
headers.put(line.substring(0, idx), line.substring(idx+2));
}

二、总体设计说明:

1.Main函数开始说明应该的设计方法,有些机制可用于其他软件的设计。

部署结构如下:

%HOME%/lib/*    ----依赖包

%HOME%/conf/*  -----配置文件夹

%HOME%/startup.sh  ---启动SHELL

%HOME%/logs/*    ----日志文件夹

%HOME%/webapps/*  ----页面部署路径

这个设计方法很类似于TOMCAT。ECLIPSE包结构截图如下:

工程启动类 org.mike.jerry.launcher.Main

lib类加载器 org.mike.jerry.launcher.ClassPath

服务加载类 org.mike.jerry.launcher.Bootstrap,该类中读取配置并启动服务端口监听。

配置文件conf/config.properties  默认配置80端口,启动后使用 http://127.0.0.1即可访问。

2. 请求接受与线程池

真正处理请求即为org.mike.jerry.server.SocketConnector ,启动与接受请求:

protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException{
ServerSocket ss= host==null?
new ServerSocket(port,backlog):
new ServerSocket(port,backlog,InetAddress.getByName(host)); return ss;
} public void accept() throws IOException {
log.info("Server started ..."); while(started){
Socket socket = serverSocket.accept();
ConnectorEndPoint connector = new ConnectorEndPoint(socket);
connector.dispatch();
}
}

每次请求开启一个ConnectorEndPoint线程处理,该线程从线程池中获取(org.mike.jerry.server.util.thread.ThreadPool),处理如下:

 /* Request Handler
*/
protected class ConnectorEndPoint extends SocketEndPoint implements Runnable { public ConnectorEndPoint(Socket socket) throws IOException {
super(socket);
socket.setSoTimeout(7000);
} public void dispatch() {
threadPool.dispatch(this);
} @Override
public void run() {
......
}
}

 3. HTTP包解析器

HTTP包解析类由org.mike.jerry.http.HttpRequestDecoder工作,HTTP请求处理都位于org.mike.jerry.http包中。

请求解析工作有几点:

1. 读取请求头,区分GET POST,获取请求头属性,GET读取URL中的符号“?”并解析参数,POST需要根据Content-Length再读取请求体中的请求参数。

把解析完成的数据存入Request中,根据Servlet设计规范,Request中需要存储请求体放入ServletInputStream in中,以供容器使用者在Servlet中能读取到InputStream.

2. 请求读取完毕后 把Resuqet交与 ResourceHandler 处理,读取所需要请求的资源。

4. 读取资源

资源的读取中,默认请求为/的会固定读取/index.html文件,该属性本应该在web.xml中配置,不过为了学习简易,硬编码于此。

1. 首先检查这路径是否在Servlet中有匹配的,如果没有,则进行下一步。

2. 从webapps文件夹中读取请求的文件,如果不存在,则返回404,如果存在,则进行下一步。

3. 读取请求中的ETag码,这个标志类似于MD5、SHA1等文件摘要,用于标志文件是否改变,如果未改变,则返回304,节省服务器资源(CPU、磁盘与网络等)

,只是MD5与SHA1计算文件摘要需要的CPU周期较长,固计算方法修改如下:

public String getWeakETag() {
try{
StringBuilder b = new StringBuilder(32);
b.append("M/\""); int length=uri.length();
long lhash=0;
for (int i=0; i<length;i++)
lhash= 31*lhash + uri.charAt(i); B64Code.encode(file.lastModified()^lhash, b);
B64Code.encode(length^lhash, b);
b.append('"');
return b.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

5. 如果文件发生改变,则重新读取文件字节流,放入响应包Response中。

 

5. 响应HTTP包封装

5.1 响应头输出: 首先获取socket输出流,再写出头信息,127.0.0.1抓包工具可使用rawcap,得到pcap包后使用wireshark查看,格式类似于:

HTTP/1.1 200 OK
ETag: M/"AJMRnIhabgYAJMQ2H/NnL0"
Date: Wed, 5 Nov 2014 09:58:17 GMT
Content-Length: 1102
Last-Modified: Wed, 2 Jul 2014 23:01:08 GMT
Connection: Keep-Alive
Content-Type: text/html
Server: M
Cache-Control: private

相应代码如:

         OutputStream out = socket.getOutputStream();

                //config status message
String respStat = HttpStatus.getMessage(response.getStatus()); StringBuilder headers = new StringBuilder();
headers.append(response.getHttpVersion() + " "
+ response.getStatus() + " " + respStat + StringUtil.CRLF); //write headers
for(Map.Entry<String, String> header : response.getHeaders().entrySet()){
headers.append(header.getKey() + ": " + header.getValue() + StringUtil.CRLF);
} headers.append(StringUtil.CRLF);//响应头写入完毕必须空一行,这也是协议规定,以区分响应体
out.write(headers.toString().getBytes())

写入响应头后再写入响应体,也就是请求的资源内容。

简易 HTTP Server 实现(JAVA)的更多相关文章

  1. FATAL Fatal error during KafkaServerStable startup. Prepare to shutdown (kafka.server.KafkaServerStartable) java.io.FileNotFoundException: /tmp/kafka-logs/.lock (Permission denied)

    1.启动kafka的时候,报错如下所示: [-- ::,] INFO zookeeper state changed (SyncConnected) (org.I0Itec.zkclient.ZkCl ...

  2. ArcGIS Server 10 Java 版的Rest服务的部署方法

    使用ArcGIS Server 10 Java版发布GIS服务,当使用ArcGIS Manager创建好服务后,然后打开“ArcGIS Services Directory”的链接时发现网页报出了找不 ...

  3. 【Tech】CAS多机部署Server和Java Client端

    昨天尝试把cas的java client端部署到另外一台机器,结果就有问题了.(localhost部署cas server和java client端参见:http://www.cnblogs.com/ ...

  4. mysql/sql server和java之间的数据类型对应关系

    Mysql************************************当前列 ClassName ColumnType DisplaySize TypeName0: java.lang.I ...

  5. kafka启动时出现FATAL Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer) java.io.IOException: Permission denied错误解决办法(图文详解)

    首先,说明,我kafk的server.properties是 kafka的server.properties配置文件参考示范(图文详解)(多种方式) 问题详情 然后,我启动时,出现如下 [hadoop ...

  6. Language Server for Java™ 1.0 在VS Code上正式发布!

    Nick Zhu form Senior Program Manager, Developer Division at Microsoft 今天,我们很高兴与大家宣布:Language Server ...

  7. ArcGIS Server 10 Java 版的Rest服务手动配置方法

    Java版的Manager中发布的服务默认只发布了该服务的SOAP接口,而REST接口需要用户在信息服务器,如Tomcat. Apache.WebLogic等中手工配置.由于在Java版的Server ...

  8. JAVA 连接 SQL Server 2008:java.lang.ClassNotFoundException: com.microsoft.jdbc.sqlserver.SQLServerDriver

    新项目需要修改Java开发的MES系统...Java忘的也差不多了...简单尝试以下JAVA连接SQL Server吧,没想到坑还是很多的.以前直接连oracle时没有这么多麻烦啊....可能微软和o ...

  9. WebSphere Application Server切换JAVA SDK版本

    最近在Windows Server 2008 R2服务器中搭建了一套IHS+WAS8.5集群环境,测试一个简单的demo应用没有问题,可是在部署正式应用时总是报类版本错误.换了好几个JDK对项目进行编 ...

随机推荐

  1. java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleExcept问题解决方案

    在部署Dynamic Web Project时,如果正确配置web.xml或者标注时,仍然出现以上异常的话,可以尝试以下内容讲解的方法: 首先,双击eclipse中的servers,位置如下图&quo ...

  2. scrapy初试

    scrapy初试 创建项目 打开cmd,在终端输入scrapy startproject tutorial,这里将在指定的文件夹下创建一个scrapy工程 其中将会创建以下的文件: scrapy.cf ...

  3. 使用 Socket.IO 开发聊天室

    前言 Socket.IO 是一个用来实现实时双向通信的框架,其本质是基于 WebSocket 技术. 我们首先来聊聊 WebSocket 技术,先设想这么一个场景: · 用户小A,打开了某个网站的充值 ...

  4. Chrome控制台使用详解

    Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快「开发调试」更强「进阶级的Frontender」 Bug无处遁形「 ...

  5. 外部世界如何访问容器? - 每天5分钟玩转 Docker 容器技术(37)

    上节我们学习了容器如何访问外部网络,今天讨论另一个方向:外部网络如何访问到容器? 答案是:端口映射. docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器.容器 ...

  6. 不错的 HttpHelper类 c#

    /// <summary>/// 类说明:HttpHelper类,用来实现Http访问,Post或者Get方式的,直接访问,带Cookie的,带证书的等方式,可以设置代理/// 重要提示: ...

  7. Vijos 1012 清帝之惑之雍正 平面最近点对(分治)

    背景 雍正帝胤祯,生于康熙十七年(1678)是康熙的第四子.康熙61年,45岁的胤祯继承帝位,在位13年,死于圆明园.庙号世宗. 胤祯是在康乾盛世前期--康熙末年社会出现停滞的形式下登上历史舞台的.复 ...

  8. 【Linux相识相知】bash的基础特性

    命令历史 shell进程会记录用户提交执行过的命令 可以是用history查看: [root@localhost dev]# history ss -tnl ifconfig vi /etc/sysc ...

  9. tp框架-----Model模型层

    1.Model模型层是用来做什么的呢? 主要是用来做操作数据库访问的.也就说明TP框架自带了一种访问数据库的方式,使用的是Model模型. 2.Model模型怎样使用呢? 要使用Model模型层访问数 ...

  10. Vulkan Tutorial 26 view and sampler

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 在本章节我们将为图形管线创建另外两个资源来对图像进行采样.第一个资源我们之前已经接触 ...