链接地址: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
- package chapter1.simplewebserver;
- import java.io.File;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.InterfaceAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- /**
- *
- * 下午11:59:29
- * @author gogole_09
- * 简单web服务器
- */
- public class HttpServer {
- //定位到webroot目录
- public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";
- //停止命令
- private static final String SHUTDOWN_COMMAND="/SHUTDOWN";
- //是否接收到了关闭命令
- private boolean shutdown=false;
- /**
- * 等待命令
- */
- public void await(){
- ServerSocket serverSocket=null;
- int port=8090;
- try {
- serverSocket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- //监听端口,等待请求。
- while(!shutdown){
- Socket socket=null;
- InputStream input=null;
- OutputStream output=null;
- try {
- socket=serverSocket.accept();
- //输入流 有过jsp编程的哥们都知道这是干嘛的
- input=socket.getInputStream();
- //输出流
- output=socket.getOutputStream();
- //构建请求
- Request request=new Request(input);
- //解析请求信息
- request.parse();
- //响应类
- Response response=new Response(output);
- response.setRequest(request);
- response.sendStaticResource();
- //处理完毕,关闭
- socket.close();
- //检查提供的URI是否为shutdown命令
- shutdown=request.getUri().equals(SHUTDOWN_COMMAND);
- } catch (Exception e) {
- e.printStackTrace();
- continue;
- }
- }
- }
- public static void main(String[] args) {
- HttpServer server=new HttpServer();
- server.await();
- }
- }
接下来是请求类:
Request
- package chapter1.simplewebserver;
- import java.io.IOException;
- import java.io.InputStream;
- /**
- *
- * 上午12:09:36
- * @author gogole_09
- * 简单服务器的请求类
- */
- public class Request {
- private InputStream input;
- private String uri;
- public Request(InputStream in) {
- this.input=in;
- }
- /**
- * 解析Uri操作
- */
- public void parse() {
- StringBuffer buf=new StringBuffer();
- 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++){
- buf.append((char)buffer[j]);
- }
- System.out.println(buf.toString());
- uri=parseUri(buf.toString());
- }
- /**
- * 解析Uri
- * 为什么要以' '为做标识 , 这里需要了解HTTP协议的相关结构;
- * 一个请求行 以 请求方法开头 + 请求URI+ 请求协议版本 + CRLF字符结束
- * 比如,你请求index.html 用GET方式 ,那么Uri形式为:
- *
- * GET /index.html HTTP/1.1
- *
- *
- * @param requestString
- * @return
- */
- 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;
- }
- }
有了请求了,服务器就得响应啊,试想,我发个请求没反应,你的第一反应是不是想砸电脑呢?
OK,Response类来了。
- package chapter1.simplewebserver;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- /**
- *
- * 上午12:10:58
- *
- * @author gogole_09 简易服务器的响应类
- */
- public class Response {
- /**
- * HTTP响应格式:= Status-Line
- * *((general-header|response-header|entity-header)CRLF) CRLF [message-body]
- * Status-Line=HTTP-Version SP(空格) Status-Code SP Reason-Phrase CRLF
- *
- */
- private static final int BUFFER_SIZE = 1024;
- Request request;
- private OutputStream output;
- public Response(OutputStream output) {
- this.output = output;
- }
- public void setRequest(Request request) {
- this.request = request;
- }
- /**
- * 服务器解析并发送静态资源
- * @throws IOException
- */
- 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<ht>File Not Found</h1>";
- output.write(errorMessage.getBytes());
- }
- } catch (Exception e) {
- System.out.println(e.toString());
- }finally{
- if(fis!=null)
- fis.close();
- }
- }
- }
OK,现在这个应用可以运行了,但是你运行起来,没办法用,为什么, 因为还需要有用户请求的静态资源。
在HttpServer类中,我们hard code了一个webroot目录,约定把所有的静态资源都放到这个目录下面的。
下面我们就把这个目录建好,并放一点资源进去 ,我是这样放的, 如图:
index.html 中 就只有一行代码:
OK, 下面我们运行一下 HttpServer ,得到页面 如下图:
在控制台,你将会看到程序打印出:
接着,我们尝试请求一个不存在的资源: 我们会的到一个404的错误页面.
OK, 一个简单的web服务器就完成了, 虽然简单,但是我们可以通过这个了解一个大概的流程。
以及复习一下java net包与HTTP/1.1协议的一些知识。
也希望这个篇文章对都有需要的人有帮助。
PS: 最近有很多朋友都问这本书哪里有买,我本人并非看的纸质的,而是电子版的, 需要下载的可以去新浪共享频道找找, 我的是从哪里下来的, 中文书名就叫 <tomcat工作原理>
评论
现在的场景是我用sh脚本nohup启动一个java进程后台运行,然后怎么友好关闭这个线程呢? 最简单的就是向一个端口发stop请求。
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。
tomcat内部也是处理socket的, 不知有什么不对吗?
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
嘿嘿,是的。 书名写错了,马上修正。
是Http Watch. 查看请求url,传递的参数,以及服务器返回数据的好东西。