第一章 一个简单的Web服务器

该应用程序仅接受位于指定目录的静态资源的请求,如HTML文件和图像文件。它也可以将传入的HTTP请求字节流显示到控制台上。但是,它并不发送任何头信息到浏览器,如日期或者cookies等。

应用程序的入口在HttpServer的静态main方法中,main()方法会创建一个HttpServer实例。然后,调用其await()方法,顾名思义,await()方法就是在指定端口上等待HTTP请求,对其进行处理,然后发送响应信息回客户端,在接受到关闭命令之前,它会保持等待状态。

HttpServer.java

package com.simpleHttpServer;

import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File; public class HttpServer
{ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; // 关闭HttpServer命令
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // 是否收到关闭HttpServer命令
private boolean shutdown = false; public static void main(String[] args)
{
HttpServer server = new HttpServer();
server.await();
} public void await()
{
ServerSocket serverSocket = null;
int port = 8080;
try
{
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e)
{
e.printStackTrace();
System.exit(1);
} // 循环等待Http请求
while (!shutdown)
{
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try
{
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream(); // 创建一个Request对象并解析
Request request = new Request(input);
request.parse(); // 创建一个Response对象
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource(); // 关闭socket
socket.close(); // 检查是否是关闭命令
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e)
{
e.printStackTrace();
continue;
}
}
}
}

Request.java 

package com.simpleHttpServer;

import java.io.InputStream;
import java.io.IOException; public class Request
{ private InputStream input;
private String uri; public Request(InputStream input)
{
this.input = input;
} public void parse()
{
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try
{
i = input.read(buffer);
} catch (IOException e)
{
e.printStackTrace();
i = -1;
}
for (int j = 0; j < i; j++)
{
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
} private String parseUri(String requestString)
{
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1)
{
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
} public String getUri()
{
return uri;
} }

Response.java

package com.simpleHttpServer;

import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File; public class Response
{ private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output; public Response(OutputStream output)
{
this.output = output;
} public void setRequest(Request request)
{
this.request = request;
} public void sendStaticResource() throws IOException
{
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try
{
File file = new File(HttpServer.WEB_ROOT, request.getUri());
if (file.exists())
{
fis = new FileInputStream(file);
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch != -1)
{
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
} else
{
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + "<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
} catch (Exception e)
{
System.out.println(e.toString());
} finally
{
if (fis != null)
fis.close();
}
}
}

 

(1)System.getProperty("user.dir")是取得当前工作目录

HttpServer中定义的public static final String WEB_ROOT 即     工作目录/webroot

(2) 在创建TCP服务端监听的时候

try
{
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e)
{
e.printStackTrace();
System.exit(1);
}

程序主动捕获了IOException,深入ServerSocket源码可以看到:

public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
setImpl();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException(
"Port value out of range: " + port);
if (backlog < 1)
backlog = 50;
try {
bind(new InetSocketAddress(bindAddr, port), backlog);
} catch(SecurityException e) {
close();
throw e;
} catch(IOException e) {
close();
throw e;
}
}

  在创建ServerSocket对象(开启TCP服务端监听)的时候可能抛出三种异常

(1) new IllegalArgumentException  在要监听的TCP端口范围不在正确范围0-65535之间,就抛出参数异常 改异常时候RuntimeException 不用显式在方法后throws以及方法调用处try catch

(2) SecurityException 也是一个RuntimeException  异常触发条件, 如果安全管理器存在并且其 checkListen 方法不允许进行该操作。

(3) IOException 是非运行时异常  如果打开套接字时发生 I/O 错误。

简要介绍下ServerSocket:

把服务器套接字绑定到特定的端口号,这样远程客户端才能定位TCP服务,如果传递进来的值为零(zero),就使用任何空闲的端口--但是客户端可能没办法访问该服务,除非用什么方式通知了客户端端口号是多少,为队列分配足够的空间以支 持特定数量的客户端套接字。在ServerSocket(int port, int numberOfClients)构造函数的重载版本 中,加入了InetAddress参数,在多地址计算机上,它允许服务器套接字绑定到某个特定的IP地址。例如,某台计算机可能有两块网 卡,或者使用虚拟IP地址把它配置成像几台计算机一样工作的时候。如果地址的值为空(null),服务 器套接字将在所有的本地地址上接受请求。在默认情况下,队列的大小设置为50,但是也提供了备用的构造函数,它允许修改这个设置。如果端口已经被绑定了,或者安全性约束条件(例如安全性规则或知名端口上的操作系统约束条件)阻挡了访问,就会产生异常。

(3) 程序创建File对象,调用了构造函数,java.io.File类中不常用的一个构造方法

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
如果 parent 为 null,则创建一个新的 File 实例,这与调用以给定 child 路径名字符串作为参数的单参数 File 构造方法效果一样。 否则,parent 路径名字符串用于表示目录,child 路径名字符串用于表示目录或文件。如果 child 路径名字符串是绝对路径名,
则用与系统有关的方式将它转换为一个相对路径名。如果 parent 是空字符串,则通过将 child 转换为抽象路径名,
并根据与系统有关的默认目录解析结果来创建新的 File 实例。否则,将每个路径名字符串转换为一个抽象路径名,并根据父抽象路径名解析子抽象路径名。 参数:
parent - 父路径名字符串
child - 子路径名字符串
抛出:
NullPointerException - 如果 child 为 null

  

 public File(String parent, String child) {
if (child == null) {
throw new NullPointerException();
}
if (parent != null) {
if (parent.equals("")) {
this.path = fs.resolve(fs.getDefaultParent(),
fs.normalize(child));
} else {
this.path = fs.resolve(fs.normalize(parent),
fs.normalize(child));
}
} else {
this.path = fs.normalize(child);
}
this.prefixLength = fs.prefixLength(this.path);
}

  

《深入剖析Tomcat》阅读(一)的更多相关文章

  1. 深入剖析tomcat 笔记——第8章 载入器

    深入剖析tomcat 笔记 目录:

  2. 深入剖析Tomcat会话机制

    1缓存机制 Tomcat默认将Session保存到内存中.但同时,Tomcat也提供了PersistentManager配合不同的Store实现的方式,使Session可以被保存到不同地方(Datab ...

  3. 深入剖析TOMCAT

    理解tomcat之搭建简易http服务器 做过java web的同学都对tomcat非常熟悉.我们在使用tomcat带来的便利的同时,是否想过tomcat是如何工作的呢?tomcat本质是一个http ...

  4. 《深入剖析Tomcat》源码

    <深入剖析Tomcat>翻译自<How Tomcat Works> 可以到官网下载:https://brainysoftware.com/download 官网下载比较慢,我就 ...

  5. 《深入剖析Tomcat》阅读(三)

    这里要介绍下Tomcat的一个重要设计方法,Catalina设计方式. Servlet容器是一个复杂系统,但是,它有三个基本任务,对每个请求,servlet容器会为其完成以下三个操作: 1.创建一个R ...

  6. 《深入剖析Tomcat》阅读(二)

    Tomcat是基于Sun公司标准的开源Servlet容器. Servlet是什么? Servlet(Server Applet),全称Java Servlet,未有中文译文.是用Java编写的服务器端 ...

  7. How tomcat works(深入剖析tomcat)阅读笔记1-4章

    How tomcat works chapter 1 简单的web服务器 这一张的主要内容就是实现一个简单的静态资源服务器,socket编程,利用java提供的socket和serverSocket编 ...

  8. 《深入剖析Tomcat》读书笔记(一)

    一.Tomcat Tomcat,全名Apache Tomcat,最初是由Sun发起,后来捐赠给ASF,是Apache Jakarta下的一个子项目.Tomcat是对Servlet API定义的容器的一 ...

  9. How tomcat works(深入剖析tomcat)servlet容器

    How tomcat works (5)servlet容器阅读笔记 第四章阅读了tomcat默认连接器的实现,当时connector中的使用的容器是自定义的容器,也是非常之简单奥,一个人就干完了所有的 ...

随机推荐

  1. 食品药检所LIMS需求分析说明书

    1.XX市食品药检所LIMS系统需求分析... 5 1.1检验业务流程... 5 1.1.1样品受理... 5 1.1.1.1选择受理任务... 5 1.1.1.2录入检品信息... 5 1.1.1. ...

  2. [Java 8] (5) 使用Lambda表达式进行设计

    使用Lambda表达式进行设计 在前面的几篇文章中,我们已经见识到了Lambda表达式是怎样让代码变的更加紧凑和简洁的. 这一篇文章主要会介绍Lambda表达式怎样改变程序的设计.怎样让程序变的更加轻 ...

  3. JSP中嵌入java代码的标签方式(转)

    (1)声明变量或方法 :  <%! 声明; %> :慎重使用,因为此方法定义的是全局变量 (2)java片段(scriptlet):  <% java代码; %> (3)表达式 ...

  4. Messages的例子

    13.33 13.36 13.37 Message.h #ifndef MESSAGE_H #define MESSAGE_H #include<iostream> #include< ...

  5. myeclipse 编码问题

    在使用eclipse+MyEclipse开发中,许多文件编码默认是ISO-8859-1,不支持中文(如常用的JSP),这样我们每次建文件都要手动改编码,其实我们可以在设置文件默认编码,今后再创建时就不 ...

  6. RHEL7单独安装图形X11

    RHEL7 默认是最小化安装(Minimal Install),没有图形界面,我们应该选择Server with GUI.若已错过此步骤,我们采用以下方式补充安装GUI界面. # yum group ...

  7. mysql case when then else end 用法

    select case when 判断条件 then 输出结果  else 输出结果 end from table

  8. java web hello world

    首先在eclipse 里面创建一个java 动态项目, 记住路径,这里是直接通过根目录直接访问的webContent目录下面 的文件, 创建好后 ,在本地配置Tomcat服务器, 将server加入到 ...

  9. [Cookie] C#CookieHelper--C#操作Cookie的帮助类 (转载)

    点击下载 CookieHelper.rar 下面是代码大家看一下 /// <summary> /// 类说明:CookieHelper /// 联系方式:361983679 /// 更新网 ...

  10. Handler 原理分析和使用之HandlerThread

    前面已经提到过Handler的原理以及Handler的三种用法.这里做一个非常简单的一个总结: Handler 是跨线程的Message处理.负责把Message推送到MessageQueue和处理. ...