用Java Socket 捕捉http1.1的长连接

今天在运行一个demo的时候,遇到了一些问题,debug后发现与http的长连接有关。

故改造了下代码,尝试捕捉”长连接“。

demo 来自于 《How Tomcat Works》

原代码

Request

public class Request {
    private InputStream in;

    private String uri;

    public Request(InputStream inputStream) {
        this.in = inputStream;
    }

    public void parse() {
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[]buffer = new byte[4096];
        try {
            in.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String s = new String(buffer, StandardCharsets.UTF_8);
        System.out.println(s);

        uri = parseUri(s);
    }

    public String parseUri(String requestString) {
        String[] s = requestString.split(" ");
        if(s.length > 2) {
            return s[1];
        }
        return null;
    }

    public String getUri(){
        return uri;
    }
}

Response

public class Response {
    private static final int BUFFER_SIZE = 10240;

    private Request request;

    private final OutputStream outputStream;

    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public Request getRequest() {
        return request;
    }

    public void sendStaticResource() {
        byte[]bytes = new byte[BUFFER_SIZE];
        FileInputStream fis = null;
        try {
            File file = new File(HttpServer.WEB_ROOT, request.getUri());
            if(file.exists()) {
                fis = new FileInputStream(file);
                int ch = fis.read(bytes, 0, BUFFER_SIZE);
                String html = new String(bytes, 0, ch);
                String res = "";
                res += "HTTP/1.1 200 OK" + "\r\n";
                res += "Content-Type:text/html;charset=utf-8" + "\r\n";
                res += "Content-Length:" + html.length() + "\r\n";
                res += "\r\n";
                res += html;
                outputStream.write(res.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
            }else {
                String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
                        "Content-Type: text/html\r\n"+
                        "Content-Length: 23\r\n"+
                        "\r\n"+
                        "<h1>File Not Found<h1>\r\n";
                outputStream.write(errorMessage.getBytes());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

HttpServer

public class HttpServer {

    public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";

    private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

    private boolean shutdown = false;

    public static void main(String[] args) throws IOException {
        HttpServer httpServer = new HttpServer();
        httpServer.await();
    }
    public void await() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.01"));
        }catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (!shutdown) {
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;
            try {
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();
                Request request = new Request(input);
                request.parse();
                Response response = new Response(output);
                response.setRequest(request);
                response.sendStaticResource();
                socket.close();
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

静态资源放在这个文件夹里:

 问题分析

现象1:

一直用一个浏览器访问没有任何问题,但是一旦中途切换浏览器就会报错。

现象2:

理论上,每次处理完请求后,应该阻塞在accept这里

但实际上,每次一个请求处理完,都会阻塞在parse方法处, 准确点说是parse中的read方法处。

这就很奇怪了,我明明关闭了socket,理论上这个连接断开后,只要我不重新请求网页,就不会有新的网页了。

初步预计这个新的socket依然是和浏览器的连接。

所以我直接关闭了浏览器,发现不在阻塞了,连接也断开了。

说明,每次处理完请求后,服务器关闭了socket后,浏览器又主动的发起了连接(即使网页没有刷新)

代码改造

上述只能说明,服务器关闭socket后,浏览器又主动发起了一个连接。

怎么能证明这个连接是长连接呢?

那就需要改下代码了,不accept放到循环外,且服务器端不主动关闭 socket。

public class HttpServer {

    public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";

    private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

    private boolean shutdown = false;

    public static void main(String[] args) throws IOException {
        HttpServer httpServer = new HttpServer();
        httpServer.await();
    }
    public void await() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.01"));
        }catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        Socket socket = null;
        InputStream input = null;
        OutputStream output = null;
        try{
            socket = serverSocket.accept();
            input = socket.getInputStream();
            output = socket.getOutputStream();
        }catch (Exception e) {
            e.printStackTrace();
        }

        while (!shutdown) {

            try {
                Request request = new Request(input);
                request.parse();
                Response response = new Response(output);
                response.setRequest(request);
                response.sendStaticResource();
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

启动后可以发现,即使处理完请求,连接也不会断,不断地在read()函数处阻塞。

这算是成功捕捉到了所谓的”http长连接“。

长连接的时间由服务器端决定,服务器计算连接时长,到期后断开连接。

浏览器默认会一直保持连接,除非关闭整个浏览器。即使我再后端添加了响应头,keep-alive timeout,到期后浏览器也不会主动断开连接。

 如有问题,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值