使用NIO实现http请求流程

本文介绍了一种使用NIO(非阻塞I/O)技术实现HTTP请求的方法,通过代码示例详细展示了如何创建一个简单的HTTP服务器,包括处理GET和POST请求、解析HTTP请求和响应的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用NIO实现http请求流程

1 http协议请求流程如下

  

2 代码实现如下

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class SimpleHttpServer {
    private final Selector selector;
    int port;
    private Set<SocketChannel> allConnections = new HashSet<>();
    volatile boolean run = false;
    HttpServlet servlet;
    ExecutorService executor = Executors.newFixedThreadPool(5);
    public SimpleHttpServer(int port, HttpServlet servlet) throws IOException {
        this.port = port;
        this.servlet = servlet;
        ServerSocketChannel listenerChannel = ServerSocketChannel.open();
        selector = Selector.open();//打开一个选择器供channel注册
        listenerChannel.bind(new InetSocketAddress(port));
        listenerChannel.configureBlocking(false);
        listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public Thread start() {
        run = true;
        Thread thread = new Thread(() -> {
            try {
                while (run) {
                    dispatch();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }, "selector-io");
        thread.start();
        return thread;
    }

    public void stop(int delay) {
        run = false;
    }

    private void dispatch() throws IOException {
        int select = selector.select(2000);
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            SelectionKey  key = iterator.next();
            iterator.remove();
            if (key.isAcceptable()) {//此键的通道是否已准备好接受新的套接字连接
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = channel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {//此键的通道是否已准备好进行读取
                final SocketChannel channel = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                final ByteArrayOutputStream out = new ByteArrayOutputStream();
                while (channel.read(buffer) >0) {
                    buffer.flip();
                    out.write(buffer.array(), 0, buffer.limit());
                    buffer.clear();
                }
                if (out.size() <= 0) {
                    channel.close();
                    continue;
                }
                System.out.println("当前通道:"+channel);
                //解码
                executor.submit(() -> {
                    try {
                        Request request = decode(out.toByteArray());
                        Response response = new Response();
                        if (request.method.equalsIgnoreCase("GET")) {
                            servlet.doGet(request, response);
                        } else {
                            servlet.doPost(request, response);
                        }
                        channel.write(ByteBuffer.wrap(encode(response)));
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                });
            }
        }


    }


    // 解码Http服务
    private Request decode(byte[] bytes) throws IOException {
        Request request = new Request();
        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
        String firstLine = reader.readLine();
        System.out.println(firstLine);
        String[] split = firstLine.trim().split(" ");
        request.method = split[0];
        request.url = split[1];
        request.version = split[2];

        //读取请求头
        Map<String, String> heads = new HashMap<>();
        while (true) {
            String line = reader.readLine();
            if (line.trim().equals("")) {
                break;
            }
            String[] split1 = line.split(":");
            heads.put(split1[0], split1[1]);
        }
        request.heads = heads;
        request.params = getUrlParams(request.url);
        //读取请求体
        request.body = reader.readLine();
        return request;

    }

    //编码Http 服务
    private byte[] encode(Response response) {
        StringBuilder builder = new StringBuilder(512);
        builder.append("HTTP/1.1 ")
                .append(response.code).append(Code.msg(response.code)).append("\r\n");

        if (response.body != null && response.body.length() != 0) {
            builder.append("Content-Length: ")
                    .append(response.body.length()).append("\r\n")
                    .append("Content-Type: text/html\r\n");
        }
        if (response.headers!=null) {
            String headStr = response.headers.entrySet().stream().map(e -> e.getKey() + ":" + e.getValue())
                    .collect(Collectors.joining("\r\n"));
            builder.append(headStr+"\r\n");
        }


//      builder.append ("Connection: close\r\n");// 执行完后关闭链接
        builder.append("\r\n").append(response.body);
        return builder.toString().getBytes();
    }


    public abstract static class HttpServlet {

        abstract void doGet(Request request, Response response);

        abstract void doPost(Request request, Response response);
    }

    public static class Request {
        Map<String, String> heads;
        String url;
        String method;
        String version;
        String body;    //请求内容
        Map<String, String> params;
    }

    public static class Response {
        Map<String, String> headers;
        int code;
        String body; //返回结果
    }


    private static Map getUrlParams(String url) {
        Map<String, String> map = new HashMap<>();
        url = url.replace("?", ";");
        if (!url.contains(";")) {
            return map;
        }
        if (url.split(";").length > 0) {
            String[] arr = url.split(";")[1].split("&");
            for (String s : arr) {
                if (s.contains("=")) {
                    String key = s.split("=")[0];
                    String value = s.split("=")[1];
                    map.put(key, value);
                }else {
                    map.put(s,null);
                }
            }
            return map;

        } else {
            return map;
        }
    }
}

辅助类

class Code {

    public static final int HTTP_CONTINUE = 100;
    public static final int HTTP_OK = 200;
    public static final int HTTP_CREATED = 201;
    public static final int HTTP_ACCEPTED = 202;
    public static final int HTTP_NOT_AUTHORITATIVE = 203;
    public static final int HTTP_NO_CONTENT = 204;
    public static final int HTTP_RESET = 205;
    public static final int HTTP_PARTIAL = 206;
    public static final int HTTP_MULT_CHOICE = 300;
    public static final int HTTP_MOVED_PERM = 301;
    public static final int HTTP_MOVED_TEMP = 302;
    public static final int HTTP_SEE_OTHER = 303;
    public static final int HTTP_NOT_MODIFIED = 304;
    public static final int HTTP_USE_PROXY = 305;
    public static final int HTTP_BAD_REQUEST = 400;
    public static final int HTTP_UNAUTHORIZED = 401;
    public static final int HTTP_PAYMENT_REQUIRED = 402;
    public static final int HTTP_FORBIDDEN = 403;
    public static final int HTTP_NOT_FOUND = 404;
    public static final int HTTP_BAD_METHOD = 405;
    public static final int HTTP_NOT_ACCEPTABLE = 406;
    public static final int HTTP_PROXY_AUTH = 407;
    public static final int HTTP_CLIENT_TIMEOUT = 408;
    public static final int HTTP_CONFLICT = 409;
    public static final int HTTP_GONE = 410;
    public static final int HTTP_LENGTH_REQUIRED = 411;
    public static final int HTTP_PRECON_FAILED = 412;
    public static final int HTTP_ENTITY_TOO_LARGE = 413;
    public static final int HTTP_REQ_TOO_LONG = 414;
    public static final int HTTP_UNSUPPORTED_TYPE = 415;
    public static final int HTTP_INTERNAL_ERROR = 500;
    public static final int HTTP_NOT_IMPLEMENTED = 501;
    public static final int HTTP_BAD_GATEWAY = 502;
    public static final int HTTP_UNAVAILABLE = 503;
    public static final int HTTP_GATEWAY_TIMEOUT = 504;
    public static final int HTTP_VERSION = 505;

    static String msg (int code) {

      switch (code) {
        case HTTP_OK: return " OK";
        case HTTP_CONTINUE: return " Continue";
        case HTTP_CREATED: return " Created";
        case HTTP_ACCEPTED: return " Accepted";
        case HTTP_NOT_AUTHORITATIVE: return " Non-Authoritative Information";
        case HTTP_NO_CONTENT: return " No Content";
        case HTTP_RESET: return " Reset Content";
        case HTTP_PARTIAL: return " Partial Content";
        case HTTP_MULT_CHOICE: return " Multiple Choices";
        case HTTP_MOVED_PERM: return " Moved Permanently";
        case HTTP_MOVED_TEMP: return " Temporary Redirect";
        case HTTP_SEE_OTHER: return " See Other";
        case HTTP_NOT_MODIFIED: return " Not Modified";
        case HTTP_USE_PROXY: return " Use Proxy";
        case HTTP_BAD_REQUEST: return " Bad Request";
        case HTTP_UNAUTHORIZED: return " Unauthorized" ;
        case HTTP_PAYMENT_REQUIRED: return " Payment Required";
        case HTTP_FORBIDDEN: return " Forbidden";
        case HTTP_NOT_FOUND: return " Not Found";
        case HTTP_BAD_METHOD: return " Method Not Allowed";
        case HTTP_NOT_ACCEPTABLE: return " Not Acceptable";
        case HTTP_PROXY_AUTH: return " Proxy Authentication Required";
        case HTTP_CLIENT_TIMEOUT: return " Request Time-Out";
        case HTTP_CONFLICT: return " Conflict";
        case HTTP_GONE: return " Gone";
        case HTTP_LENGTH_REQUIRED: return " Length Required";
        case HTTP_PRECON_FAILED: return " Precondition Failed";
        case HTTP_ENTITY_TOO_LARGE: return " Request Entity Too Large";
        case HTTP_REQ_TOO_LONG: return " Request-URI Too Large";
        case HTTP_UNSUPPORTED_TYPE: return " Unsupported Media Type";
        case HTTP_INTERNAL_ERROR: return " Internal Server Error";
        case HTTP_NOT_IMPLEMENTED: return " Not Implemented";
        case HTTP_BAD_GATEWAY: return " Bad Gateway";
        case HTTP_UNAVAILABLE: return " Service Unavailable";
        case HTTP_GATEWAY_TIMEOUT: return " Gateway Timeout";
        case HTTP_VERSION: return " HTTP Version Not Supported";
        default: return " ";
      }
    }
}

业务类

import org.junit.Test;

import java.io.IOException;
import java.util.HashMap;


public class HttpServerTest {

    @Test
    public void simpleHttpTest() throws IOException, InterruptedException {
        SimpleHttpServer simpleHttpServer = new SimpleHttpServer(8080, new SimpleHttpServer.HttpServlet() {
            @Override
            void doGet(SimpleHttpServer.Request request, SimpleHttpServer.Response response) {
                System.out.println(request.url);
                response.body="hello word";
                response.code=200;
                response.headers=new HashMap<>();
                if (request.params.containsKey("short")) {
                    response.headers.put("Connection", "close");
                }else if(request.params.containsKey("long")){
                    response.headers.put("Connection", "keep-alive");
                    response.headers.put("Keep-Alive", "timeout=30,max=300");
                }
            }

            @Override
            void doPost(SimpleHttpServer.Request request, SimpleHttpServer.Response response) {

            }
        });
        simpleHttpServer.start().join();
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值