手写一个Web服务器,极简版Tomcat

网络传输是通过遵守HTTP协议的数据格式来传输的。

HTTP协议是由标准化组织W3C(World Wide Web Consortium,万维网联盟)和IETF(Internet Engineering Task Force,国际互联网工程任务组)推动和制定的,最后形成RFC文档 [RFC1945](HTTP/1.0)和RFC2616[HTTP/1.1]

可以响应HTTP请求的程序就是Web Server,实现方法并没有统一规范,可以说HTTP协议就是Web Server和网络传输数据之间的接口规范。以后我们会看到,Web Server和处理程序之间的规范是CGI。

一些常见的Web Server有Apache、Nginx、Microsoft IIS、Google GWS。

我们这里手写一个Web Server。

首先我们需要接收网络的请求,根据OSI七层模型,网络的实现是分层的。Web Server属于应用层,要调用下一层(传输层,TCP/IP)的接口,来得到对方发来的网络请求,这个接口就叫做Socket。《计算机网络:自顶向下方法》中写到:“Socket是同一台主机内应用层与传输层之间的接口。因为Socket是建立网络应用程序的可编程接口,所以Socket也被称为应用程序和网络之间的API”。

一、最简单的Web Server

要编写一个Web Server,我们调用系统Socket接口,监听8180端口上收到的网络数据,打印到控制台,并给出一个符合http标准的响应,代码如下:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class Server {
    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(8180); // 调用系统socket接口,监听某端口的socket请求
            Socket httpInputSocket = serverSocket.accept(); // 在这里,程序线程等待socket输入
            this.printHttpRequest(httpInputSocket);
            this.responseHttpRequest(httpInputSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 打印来自外部的http请求
     *
     * @param httpInputSocket
     */
    private void printHttpRequest(Socket httpInputSocket) {
        try {
            // 调用系统IO,打印请求
            BufferedReader br = new BufferedReader(new InputStreamReader(httpInputSocket.getInputStream()));
            StringBuilder receivedHttp = new StringBuilder();
            String line = null;
            while ( !"".equals(line = br.readLine())) {
                receivedHttp.append(line);
                receivedHttp.append(System.getProperty("line.searator", "\n"));
            }
            System.out.println(receivedHttp.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回页面供请求方显示
     *
     * @param httpInputSocket
     */
    private void responseHttpRequest(Socket httpInputSocket) {

        // 创建响应体
        StringBuilder contextText = new StringBuilder();
        contextText.append(
                "<html>" +
                        "<head>" +
                        "<title>Build A Web Server</title>" +
                        "</head>" +
                        "<body>Hello World, This is my page</body>" +
                        "</html>");

        // 创建响应头
        StringBuilder headText = new StringBuilder();
        headText.append("HTTP/1.1").append(" ").append("200").append(" ").append("OK").append("\n");
        headText.append("Server:myServer").append(" ").append("0.0.1v").append(" ");
        headText.append("Date:Sat,"+" ").append(new Date()).append("\n");
        headText.append("Content-Type:text/html;charset=UTF-8").append("\n");
        headText.append("Content-Length:").append(contextText.toString().getBytes().length).append("\n");

        // http response
        StringBuilder responseText = new StringBuilder();
        responseText.append(headText).append("\n").append(contextText);
        System.out.println(responseText);

        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(httpInputSocket.getOutputStream()));
            bw.write(responseText.toString());
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

至此,一个100行不到的Web Server就写好了,运行一下,在浏览器里访问:http://localhost:8180/,可以看到结果:

程序控制台也正确打印:

这个Web Server会对请求到8180端口的所有请求都返回同样的页面,比如我们访问http://localhost:8180/really?foo=123,结果也是一样的:

 

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值