HTTP 协议

HTTP 协议构建于 TCP/IP 协议之上,是一个应用层协议。

维基百科:https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE

HTTP 协议头信息必须是 ASCII 码,后面的数据可以是任何格式(服务器回应的时候,必须告诉客户端,数据是什么格式,Content-Type 字段的作用)。

https://developer.mozilla.org/zh-CN/docs/Web/HTTP

规范把 HTTP 请求分为三个部分:请求行(Request Line)、请求头(Headers)、消息主体(Body)。

其中,headers 和 body 是可选的。

Request Line 和 Headers 以 分隔,Headers 与 Body 之间有一个空行(两个 )。

<method> <request-URL> <version>
<headers>

<body>

method 有很多多,常见的就是 GET 和 POST。http 1.1 为了表达不同的语义,引入了诸如 DELETE、PATCH 等方法,这种语义不是强制性的。

协议并没有规定 GET 请求不能带 body 。理论上,任何 HTTP 请求都可以带 body 的。代理一般不会缓存 POST 请求的响应内容。

HTTP 请求例子:

GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

服务器响应

HTTP/1.1 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

GET 产生一个 TCP 数据包,POST 产生两个TCP 数据包。

对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应200(返回数据)。

对于 POST 方式的请求,浏览器先发送 header,服务器响应 100(continue),然后再发送 data,服务器响应200(返回数据)。

Java 实现的简单 HTTP 服务

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

public class MyHttpServer {
    // 事件轮询器
    private static Selector selector;
    // 服务通道
    private static ServerSocketChannel serverSocketChannel;

    public static void main(String[] args) throws Exception {
        // 创建 ServerSocketChannel
        serverSocketChannel = ServerSocketChannel.open();
        // 监听 80 端口
        serverSocketChannel.socket().bind(new InetSocketAddress(80));
        // 设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);
        // 为 ssc 注册选择器
        selector = Selector.open();
        /**
         * 将通道注册到选择器上, 并且指定“监听接收事件”
         * SelectionKey.OP_ACCEPT —— 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了
         * SelectionKey.OP_CONNECT —— 连接就绪事件,表示客户与服务器的连接已经建立成功
         * SelectionKey.OP_READ —— 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)
         * SelectionKey.OP_WRITE —— 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 创建处理器
        while (true) {
            // 等待请求,每次等待阻塞 3s,超过 3s 后线程继续向下运行,如果传入 0 或者不传参数将一直阻塞
            if (selector.select(3000) == 0) {
                continue;
            }
            // 获取待处理的SelectionKey
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

            while (keyIter.hasNext()) {
                SelectionKey key = keyIter.next();
                // 从待处理的 SelectionKey 迭代器中移除当前所使用的 key
                keyIter.remove();
                // 启动新线程处理 SelectionKey
                new Thread(new HttpHandler(key)).run();
            }
        }
    }

    private static class HttpHandler implements Runnable {
        private int bufferSize = 1024;
        private String localCharset = "UTF-8";
        private SelectionKey key;

        public HttpHandler(SelectionKey key) {
            this.key = key;
        }

        public void handleAccept() throws IOException {
            serverSocketChannel = (ServerSocketChannel) key.channel();
            // 建立和客户端的链接, 因为 OP_ACCEPT 是注册在 serverSocketChannel上,每个客户又有自己的 SocketChannel 通道
            SocketChannel clientChannel = serverSocketChannel.accept();
            clientChannel.configureBlocking(false);
            // 开启注册该客户端的可读时间,即该客户上传完所有的请求数据到系统 kernel 的 buffer 缓存中后,开启可读事件通知
            clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
        }

        public void handleRead() throws IOException {
            // 获取 channel
            SocketChannel sc = (SocketChannel) key.channel();
            sc.configureBlocking(false);
            // 获取 buffer 并重置
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();
            // 没有读到内容则关闭
            if (sc.read(buffer) == -1) {
                sc.close();
            } else {
                // 接收请求数据
                buffer.flip();
                String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
                // 控制台打印请求报文头
                String[] requestMessage = receivedString.split("
");
                for (String s : requestMessage) {
                    System.out.println(s);
                    // 遇到空行说明报文头已经打印完
                    if (s.isEmpty()) {
                        break;
                    }
                }
                // 根据上面的处理情况,再注册这里的信息
                sc.register(key.selector(), SelectionKey.OP_WRITE, requestMessage);
            }
        }

        public void handleWrite() throws IOException {
            SocketChannel sc = (SocketChannel) key.channel();
            sc.configureBlocking(false);
            String[] requestMessage = (String[]) key.attachment();

            // 返回客户端
            StringBuilder sendString = new StringBuilder();
            sendString.append("HTTP/1.1 200 OK
"); // 响应报文首行,200 表示处理成功
            sendString.append("Content-Type:text/html;charset=" + localCharset + "
");
            sendString.append("
"); // 报文头结束后加一个空行
            sendString.append("<html><head><title>显示报文</title></head><body>");
            sendString.append("接收到请求报文是:<br/>");
            for (String s : requestMessage) {
                sendString.append(s + "<br/>");
            }
            sendString.append("</body></html>");
            ByteBuffer buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
            sc.write(buffer);
            sc.close();

            // 根据上面的处理情况,再注册这里的信息
            // sc.register(key.selector(), SelectionKey.OP_CONNECT);
        }

        @Override
        public void run() {
            try {
                if (key.isAcceptable()) {
                    // 接收到连接请求时
                    handleAccept();
                } else if (key.isReadable()) {
                    // 读数据
                    handleRead();
                } else if (key.isWritable()) {
                    // 写数据
                    handleWrite();
                } else if (key.isConnectable()) {
                    System.out.println("isConnectable============");
                } else if (key.isValid()) {
                    System.out.println("isValid==================");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

https://www.ruanyifeng.com/blog/2016/08/http.html

https://hit-alibaba.github.io/interview/basic/network/HTTP.html

https://www.zhihu.com/question/28586791

https://blog.csdn.net/wangjun5159/article/details/47781443

原文地址:https://www.cnblogs.com/jhxxb/p/11679606.html