WebServer

V1:
``首先创建服务器,端口号为8088,代码如下:

package com.webserver.core;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * WebServer主类
 */
public class WebServerApplication {
   
    private ServerSocket serverSocket;

    public WebServerApplication(){
   
        try {
   
            System.out.println("正在启动服务端...");
            serverSocket = new ServerSocket(8088);
            System.out.println("服务端启动完毕!");
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

    public void start(){
   
        try {
   
            System.out.println("等待客户端链接...");
            Socket socket = serverSocket.accept();
            System.out.println("一个客户端链接了!");
            //启动一个线程处理与该客户端的交互
            ClientHandler handler = new ClientHandler(socket);
            Thread t = new Thread(handler);
            t.start();

        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
   
        WebServerApplication application = new WebServerApplication();
        application.start();
    }
}

V2
HTTP协议要求浏览器连接服务端后应当发送一个请求,因此本版本实现读取请求并输出到控制台来了解请求
的格式和内容。

实现:
由于服务端可以同时接收多客户端的连接,因此与聊天室相同,主线程仅负责接受客户端的连接,一旦一个
客户端连接后则启动一个线程来处理。
1:在com.webserver.core下新建类:ClientHandler(实现Runnable接口),作为线程任务。
工作是负责与连接的客户端进行HTTP交互
2:WebServerApplication主线程接收连接后启动线程执行ClientHandler这个任务处理客户端交互
3:在ClientHandler中读取客户端发送过来的内容(请求内容)并打桩输出

package com.webserver.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * 处理一次与客户端的HTTP交互操作
 * 处理一次HTTP交互由三步完成:
 * 1:解析请求
 * 2:处理请求
 * 3:发送响应
 */
public class ClientHandler implements Runnable {
   
    private Socket socket;

    public ClientHandler(Socket socket) {
   
        this.socket = socket;
    }

    @Override
    public void run() {
   
        try {
   
            //1解析请求
            InputStream in = socket.getInputStream();
            int d;
            while((d = in.read())!=-1){
   
                System.out.print((char)d);
            }

            //2处理请求

            //3发送响应
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
}

V3
解析请求
HTTP协议要求客户端连接后会发送一个请求,每个请求由三部分构成:
请求行 消息头 消息正文

首先请求行和消息头有一个共同的特点:都是以CRLF结尾的一行字符串.
因此先实现读取一行字符串的操作,测试将请求行读取出来并进行解析.之后可以再利用这个
操作完成消息头的读取并解析.

实现:
在ClientHandler中完成读取一行字符串的操作.

package com.webserver.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * 处理一次与客户端的HTTP交互操作
 * 处理一次HTTP交互由三步完成:
 * 1:解析请求
 * 2:处理请求
 * 3:发送响应
 */
public class ClientHandler implements Runnable {
   
    private Socket socket;

    public ClientHandler(Socket socket) {
   
        this.socket = socket;
    }

    @Override
    public void run() {
   
        try {
   
            //1解析请求

            //测试读取一行字符串(以CRLF结尾)
            InputStream in = socket.getInputStream();
            int d;//每次读取到的字节
            char cur='a',pre='a';//cur表示本次读取到的字符,pre表示上次读取到的字符
            StringBuilder builder = new StringBuilder();
            while((d = in.read())!=-1){
   
                cur = (char)d;
                if(pre==13&&cur==10){
   //是否已经连续读取到了回车+换行符
                    break;
                }
                builder.append(cur);
                pre = cur;
            }
            String line = builder.toString().trim();
            System.out.println("请求行:"+line);
            //请求行相关信息
            String method;  //请求方式
            String uri;     //抽象路径
            String protocol;//协议版本

            String[] data = line.split("\\s");
            method = data[0];
            uri = data[1];//这里可能出现数组下标越界,这是因为浏览器发送了空请求导致的。后期会解决。现在出现该异常先忽略。重新启动服务端重新测试。
            protocol = data[2];

            //测试路径:http://localhost:8088/index.html
            System.out.println("method:"+method);//method:GET
            System.out.println("uri:"+uri);//uri:/index.html
            System.out.println("protocol:"+protocol);//protocol:HTTP/1.1

            //2处理请求

            //3发送响应
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
}

V4
继续解析请求
上一个版本完成了解析请求行的操作,继续使用该操作完成解析消息头

实现:
1:在ClientHandler中定义方法:readLine,用于将读取一行字符串的操作重用
2:将解析请求行中读取第一行内容的操作改为调用readLine
3:继续利用readLine读取消息头并保存每个消息头

package com.webserver.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 处理一次与客户端的HTTP交互操作
 * 处理一次HTTP交互由三步完成:
 * 1:解析请求
 * 2:处理请求
 * 3:发送响应
 */
public class ClientHandler implements Runnable {
   
    private Socket socket;

    public ClientHandler(Socket socket) {
   
        this.socket = socket;
    }

    @Override
    public void run() {
   
        try {
   
            //1解析请求
            //1.1解析请求行
            String line = readLine();
            System.out.println("请求行:"+line);
            //请求行相关信息
            String method;  //请求方式
            String uri;     //抽象路径
            String protocol;//协议版本

            String[] data = line.split("\\s");
            method = data[0];
            uri = data[1];//这里可能出现数组下标越界,这是因为浏览器发送了空请求导致的。后期会解决。现在出现该异常先忽略。重新启动服务端重新测试。
            protocol = data[2];

            //测试路径:http://localhost:8088/index.html
            System.out.println("method:"+method);//method:GET
            System.out.println("uri:"+uri);//uri:/index.html
            System.out.println("protocol:"+protocol);//protocol:HTTP/1.1

            //1.2:解析消息头
            Map<String,String> headers = new HashMap<>();
            while(true) {
   
                line = readLine();
                if(line.isEmpty()){
   //如果readLine返回空字符串,说明单独读取到了回车+换行
                    break;
                }
                System.out.println("消息头:" + line);
                /*
                    将每一个消息头按照": "(冒号+空格拆)分为消息头的名字和消息头的值
                    并以key,value的形式存入到headers中
                 */
                data = line.split(":\\s");
                headers.put(data[0],data[1]);

            }//while循环结束,消息头解析完毕
            System.out.println("headers:"+headers);


            //2处理请求

            //3发送响应
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }

    private String readLine() throws IOException {
   //通常被重用的代码都不自己处理异常
        //同一个socket对象无论调用多少次getInputStream()获取的始终是同一个输入流
        InputStream in = socket.getInputStream();
        int d;//每次读取到的字节
        char cur='a',pre='a';//cur表示本次读取到的字符,pre表示上次读取到的字符
        StringBuilder builder = new StringBuilder();
        while((d = in.read())!=-1){
   
            cur = (char)d;
            if(pre==13&&cur==10){
   //是否已经连续读取到了回车+换行符
                break;
            }
            builder.append(cur);
            pre = cur;
        }
        return builder.toString().trim();
    }
}

V5
重构
进行功能个拆分,将ClientHandler中第一个环节解析请求的细节拆分出去,使得
ClientHandler仅关心处理一次HTTP交互的流程控制.

实现:
1:新建一个包:com.webserver.http
2:在http包下新建类:HttpServletRequest 请求对象
使用这个类的每一个实例表示客户端发送过来的一个HTTP请求
3:在HttpServletRequest的构造方法中完成解析请求的工作
4:ClientHandler第一步解析请求只需要实例化一个HttpServletRequest即可.

package com.webserver.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求对象
 * 该类的每一个实例用于表示浏览器发送过来的一个HTTP的请求内容
 * 每个请求HTTP协议要求由三部分构成:请求行,消息头,消息正文
 */
public class HttpServletRequest {
   
    private Socket socket;

    //请求行相关信息
    private String method;  //请求方式
    private String uri;     //抽象路径
    private String protocol;//协议版本

    //消息头相关信息
    private Map<String,String> headers = new HashMap<>();



    public HttpServletRequest(Socket socket) throws IOException {
   
        this.socket = socket;
        //1.1解析请求行
        parseRequestLine();
        //1.2:解析消息头
        parseHeaders();
        //1.3:解析消息正文
        parseContent();

    }

    //解析请求行
    private void parseRequestLine() throws IOException {
   
        String line = readLine();
        System.out.println("请求行:"+line);
        String[] data = line.split("\\s");
        method = data[0];
        uri = data[1];//这里可能出现数组下标越界,这是因为浏览器发送了空请求导致的。后期会解决。现在出现该异常先忽略。重新启动服务端重新测试。
        protocol = data[2];
    }
    //解析消息头
    private void parseHeaders() throws IOException {
   
        while(true) {
   
            String line = readLine();
            if(line.isEmpty()){
   //如果readLine返回空字符串,说明单独读取到了回车+换行
                break;
            }
            System.out.println("消息头:" + line);
                /*
                    将每一个消息头按照": "(冒号+空格拆)分为消息头的名字和消息头的值
                    并以key,value的形式存入到headers中
                 */
            String[] data = line.split(":\\s");
            headers.put(data[0],data[1]);

        }//while循环结束,消息头解析完毕
        System.out.println("headers:"+headers);
    }
    //解析消息正文
    private void parseContent(){
   }

    private String readLine() throws IOException {
   //通常被重用的代码都不自己处理异常
        //同一个socket对象无论调用多少次getInputStream()获取的始终是同一个输入流
        InputStream in = socket.getInputStream();
        int d;//每次读取到的字节
        char cur='a',pre='a';//cur表示本次读取到的字符,pre表示上次读取到的字符
        StringBuilder builder = new StringBuilder();
        while((d = in.read())!=-1){
   
            cur = (char)d;
            if(pre==13&&cur==10){
   //是否已经连续读取到了回车+换行符
                break;
            }
            builder.append(cur);
            pre = cur;
        }
        return builder.toString().trim();
    }

    public String getMethod() {
   
        return method;
    }

    public String getUri() {
   
        return uri;
    }

    public String getProtocol() {
   
        return protocol;
    }

    public String getHeader(String name) {
   
        return headers.get(name);
    }
}

package com.webserver.core;

import com.webserver.http.HttpServletRequest;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 处理一次与客户端的HTTP交互操作
 * 处理一次HTTP交互由三步完成:
 * 1:解析请求
 * 2:处理请求
 * 3:发送响应
 */
public class ClientHandler implements Runnable {
   
    private Socket socket;

    public ClientHandler(Socket socket) {
   
        this.socket = socket;
    }

    @Override
    public void run() {
   
        try {
   
            //1解析请求
            HttpServletRequest request = new HttpServletRequest(socket);
            System.out.println(request.getMethod());//GET


            //2处理请求

            //3发送响应

        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
}

V6
此版本完成响应客户端的工作
这里先将ClientHandler中处理一次交互的第三步:响应客户端 实现出来。
目标:将一个固定的html页面通过发送一个标准的HTTP响应回复给浏览器使其呈现出来。
需要的知识点:
1:HTML基础语法,html是超文本标记语言,用于构成一个"网页"的语言。
2:HTTP的响应格式。

实现:
一:先创建第一个页面index.html
1:在src/main/resource下新建目录static
这个目录用于存放当前服务端下所有的静态资源。

2:在static目录下新建目录新建第一个页面:index.html

二:实现将index.html页面响应给浏览器
在ClientHandler第三步发送响应处,按照HTTP协议规定的响应格式,将该页面包含在正文部分将其发送给浏览器即可。

三:第二步测试成功后,我们就可以根据请求中浏览器传递过来的抽象路径去static目录下定位浏览器实际
请求的页面,然后用上述方式将该页面响应给浏览器,达到浏览器自主请求其需要的资源。

四:一问一答实现后,在ClientHandler异常处理机制最后添加finally并最终与客户端断开链接。这也是
HTTP协议的要求,因此在这里调用socket.close()

五:可以在WebServerApplication中的start方法里将接受客户端链接的动作放在死循环里重复进行了。
无论浏览器请求多少次,我们都可以遵从一问一答的原则响应用户请求的所有页面了。
classtable.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>课程表</title>
</head>
<body>
<center>
    <h1>课程表</h1>
    <table border="1">
        <tr>
            <td>&nbsp;</td>
            <td>星期一</td>
            <td>星期二</td>
            <td>星期三</td>
            <td>星期四</td>
            <td>星期五</td>
        </tr>
        <tr>
            <td rowspan="4">上午</td>
            <td>语文</td>
            <td>数学</td>
            <td>英语</td>
            <td>体育</td>
            <td>生物</td>
        </tr>
        <tr>
            <td>生物</td>
            <td>数学</td>
            <td>英语</td>
            <td>语文</td>
            <td>体育</td>
        </tr>
        <tr>
            <td>生物</td>
            <td>数学</td>
            <td>英语</td>
            <td>语文</td>
            <td>体育</td>
        </tr>
        <tr>
            <td>生物</td>
            <td>数学</td>
            <td>英语</td>
            <td>语文</td>
            <td>体育</td>
        </tr>
        <tr>
            <td colspan="6" align="center"><a href="http://www.bilibili.com">午休,bilibili一下</a></td>
        </tr>
        <tr>
            <td rowspan="4">下午</td>
            <td>语文</td>
            <td>数学</td>
            <td>英语</td>
            <td>体育</td>
            <td>生物</td>
        </tr>
        <tr>
            <td>生物</td>
            <td>数学</td>
            <td>英语</td>
            <td>语文</td>
            <td>体育</td>
        </tr>
        <tr>
            <td>生物</td>
            <td>数学</td>
            <td>英语</td>
            <td>语文</td>
            <td>体育</td>
        </tr>
        <tr>
            <td>生物</td>
            
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web Server是一种软件系统,它的主要功能是接受来自客户端的HTTP请求,处理请求并向客户端返回响应。常见的Web Server软件包括Apache、Nginx、IIS等。\[2\]这些软件通过在服务器上运行的方式来实现Web Server系统的功能。Web Server软件处理来自客户端的HTTP请求,将请求与服务器上的资源匹配,生成响应,并将响应发送回客户端。\[2\]如果你对Web Server的具体实现感兴趣,可以参考《从零开始自制实现WebServer》系列博客,其中包含了C++ High-Performance WebServer的源码实现,涵盖了Util核心代码部分、Base核心代码部分、Http核心代码部分、Timer核心代码部分和Logging核心代码部分。\[1\] #### 引用[.reference_title] - *1* [从零开始自制实现C++ High-Performance WebServer 全流程记录](https://blog.csdn.net/qq_37500516/article/details/123754194)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Web Server系统概述](https://blog.csdn.net/weixin_45627194/article/details/130734775)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值