简单实现一个http服务

1 阻塞式

  1. 代码结构

在这里插入图片描述
2. 代码实现

package study.wyy.net.http;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author wyaoyao
 * @date 2021/5/31 9:43
 * 一个简单的http服务器,监听8080端口
 */
@Slf4j
public class SimpleHttpServer {

    private final int port = 8080;

    private final ServerSocketChannel serverSocketChannel;

    private final ExecutorService executorService;

    private final int POOL_MULTIPLE = 4;


    public SimpleHttpServer() throws IOException {
        this.serverSocketChannel = ServerSocketChannel.open();
        // 绑定本地端口
        this.serverSocketChannel.socket().bind(new InetSocketAddress(this.port));
        // 使得在同一个主机上关闭了服务器,紧接着再启动服务器程序时,可以顺利绑定相同的端口
        this.serverSocketChannel.socket().setReuseAddress(true);
        this.executorService =
                Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_MULTIPLE);
        log.info("simple http server is started the port is {}", serverSocketChannel.socket().getLocalPort());
    }

    public void services() {
        while (true) {
            // 客户端的连接
            SocketChannel socketChannel = null;
            try {
                // 等待客户端连接
                socketChannel = serverSocketChannel.accept();
                executorService.submit(new RequestHandler(socketChannel));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 内部类,负责处理Http请求,实现Runnable接口
     * 每个http请求都将开辟一个线程去处理
     */
    @Slf4j
    private static class RequestHandler implements Runnable {
        private final SocketChannel socketChannel;
        private final Charset charset = Charset.forName("utf-8");

        private RequestHandler(SocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void run() {
            handleRequest();
        }

        private void handleRequest() {
            try {
                // 返回与当前SocketChannel关联的Socket对象,每个SocketChannel都与一个Socket对象关联
                Socket socket = this.socketChannel.socket();
                log.info("accept client connection from [{}:{}]", socket.getInetAddress().getHostName() + socket.getPort());
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 读取http请求,假定其长度不超过1024字节
                int read = this.socketChannel.read(buffer);
                buffer.flip();
                // 解码请求的内容
                String request = decode(buffer);
                // 打印请求内容
                log.info("this request content is [{}]", request);

                // 生成http结果
                StringBuffer response = new StringBuffer("HTTP/1.1 200 OK \r\n");
                // 最后一个请求头的时候要多家一个换行
                // 请求头和请求正文之间必须有空行分割
                response.append("Content-Type:text/html; charset=UTF-8\r\n\r\n");
                // 发送http响应的第一行和响应头
                this.socketChannel.write(encode(response.toString()));
                FileInputStream fileInputStream;
                // 获取第一行请求
                String firstLineRequest = request.substring(0, request.indexOf("\r\n"));
                //  首页
                URL resource = this.getClass().getClassLoader().getResource("index.html");
                fileInputStream = new FileInputStream(resource.getFile());
                if(firstLineRequest.indexOf("login.html") != -1){
                    // 登录页面
                    resource = this.getClass().getClassLoader().getResource("login.html");
                    fileInputStream = new FileInputStream(resource.getFile());
                }
                if(firstLineRequest.indexOf("/userLogin") != -1){
                    // 登录请求
                    resource = this.getClass().getClassLoader().getResource("success.html");
                    fileInputStream = new FileInputStream(resource.getFile());
                }
                FileChannel channel = fileInputStream.getChannel();
                // 发送响应正文
                 channel.transferTo(0,channel.size(),this.socketChannel);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (this.socketChannel != null) {
                        this.socketChannel.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private String decode(ByteBuffer buffer) {
            CharBuffer decode = charset.decode(buffer);
            return decode.toString();
        }

        private ByteBuffer encode(String content) {
            ByteBuffer encode = charset.encode(content);
            return encode;
        }

    }
}

核心来说就是按照http协议来解析请求,返回请求就好了。

关于http格式就不多介绍。。。。

浏览器访问http://localhost:8080/,就会返回首页(index.html)

这里提供一下login.html的代码

<html>

<body>
<form method="post" action="/userLogin">
    <label>用户名:</label><input name="username" type="text">
    <br>
    <label>密 码:</label><input name="password" type="password">
    <br>
    <input type="submit" value="登录">
</form>

</body>
</html>

现在访问http://localhost:8080/login.html

输入用户名密码登录(这里随便输入就好),观察后台输出的请求:

POST /userLogin HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 28
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Microsoft Edge";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.37
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/login.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: Idea-9ea971b1=c85e1a84-f8b7-4885-9555-497f2ed3a5e7; m=2258:Z3Vlc3Q6Z3Vlc3Q%253D

username=name&password=13123  # 请求参数(这里是表单请求,Content-Type: application/x-www-form-urlencoded)

2 非阻塞式

2.1 核心类介绍

  1. HttpServer是服务器的主程序,负责启动服务器
  2. AcceptHandler负责接受客户连接
  3. RequestHandler负责接收Http请求,并解析,然后生成响应
  4. Request表示Http请求
  5. Response负责Http响应
  6. Content表示Http响应正文
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值