JAVA--Socket编程实现HTTP报文收发(模拟浏览器)

每天我们都会使用浏览器去访问一些网站页面,但是每次访问时,你是否会想知道,浏览器和服务器到底做了些什么,才让你看到浏览器呈现给你的这些具有样式排版,乃至动画的页面?

本文需要知识前提:HTTP协议了解,TCP/IP协议了解,Socket编程了解。

1.浏览器收发HTTP报文

你可能会回答,浏览器向服务器端发送HTTP请求,服务器回复HTTP请求,再经过浏览器内核的渲染和javascript引擎的解释执行,展现了这一切

这里写图片描述

可以看到浏览器进行了一次HTTP请求。

这里写图片描述

服务器返回的HTTP报文其实就是一些标签和javascript代码。

这里写图片描述

最后浏览器渲染给我们看到的就是一个美观的页面。

对!但并不彻底。

2.JAVA实现HTTP请求(爬虫)

我们知道HTTP协议是基于TCP协议的上层协议,那么HTTP请求说到底,也就是一次TCP请求。

Java中Socket编程可以进行TCP连接,那么是否使用Java可以模拟HTTP请求的过程呢?

答案是肯定的:熟悉的同学可能会想到,Java中内置的java.net.HttpURLConnectionjava.net.URL类就可以实现对基于HTTP协议的URL进行访问。

比如:

            URL url = new URL("https://www.csdn.net/");
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            /**
             * URL对象调用openConnection()方法以后并不是直接去创建Socket进行通信
             * 而是调用了getContent()方法以后(以及其他的各种获得Header方法等等)
             * 所以要在真正建立Socket通信访问服务器80端口以前setRequestMethod
             */
            httpURLConnection.setRequestMethod("GET");
            BufferedInputStream bufferedInputStream = new BufferedInputStream(httpURLConnection.getInputStream());
            // 使用字符流类读取内容,注意编码从二进制转为字符时使用 UTF-8
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream, "utf-8"));
            String tem;
            while((tem = bufferedReader.readLine())!=null) {
                System.out.println(tem);
            }

            bufferedReader.close();         

这段代码会打印出CSDN首页的HTTP源码,也就是说这就是一个最简单的网页爬虫。

这里写图片描述

但是这还不够,我们想知道,如果直接使用TCP连接去访问对端服务器的80端口会怎么样?

3.Socket编程实现HTTP请求发送(模拟浏览器)

这里我们首先在虚拟机上搭建了HTTP服务器环境,使用了Nginx + Ubuntu Server版本。

            Socket client = new Socket();
            InetSocketAddress inetSocketAddress = new InetSocketAddress("192.168.194.129", 8080);
            // 建立TCP连接,虚拟机的地址为192.168.194.129
            // Nginx监听的端口设置为8080
            client.connect(inetSocketAddress, 1000);
            String request = "GET /index.html HTTP/1.1\r\n"+
                    "Host: 192.168.194.129:8080\r\n";
            PrintWriter pWriter = new PrintWriter(client.getOutputStream(),true);
            // 这里使用println()方法是因为 HTTP 协议在报头和报文之间,有一行空行
            // 如果少了这一行空行,服务器是无法解析报文的,会出现 400 错误
            pWriter.println(request);
            client.close();

然后使用Socket编程对服务器发起一次TCP请求。

这里写图片描述

这时我们查看Nginx的访问日志,会发现有一个GET请求,请求页面是/index.html。也就是说,我们模拟浏览器发送了一个HTTP请求。

此时我们需要关注的是

            String request = "GET /index.html HTTP/1.1\r\n"+
                    "Host: 192.168.194.129:8080\r\n";

这是一段HTTP request报文,可以看到HTTP请求的方法是GET方法,请求的路径是/index.html,使用的协议是HTTP1.1。然后我们可以自己添加Header

一般浏览器发送HTTP请求的时候,会自动构造报文,而且会添加Header

这里写图片描述

其实这些信息,就是我们通过TCP连接发送给服务器的信息,无论是HTTP报文,还是HTTP Request Header,还是发送的消息,最终都是通过Sokcet传输的TCP连接。

了解清楚了这些以后,我们就可以自己构造HTTP报文,然后通过TCP连接的形式发送。

4.Socket编程实现HTTP请求的接收(模拟浏览器)

那么我们再模拟一下浏览器接收HTTP报文的过程。

            Socket client = new Socket();
            InetSocketAddress inetSocketAddress = new InetSocketAddress("192.168.194.129", 8080);
            // 建立TCP连接,虚拟机的地址为192.168.194.129
            // Nginx监听的端口设置为8080
            client.connect(inetSocketAddress, 1000);
            String request = "GET /index.html HTTP/1.1\r\n"+
                    "Host: 192.168.194.129:8080\r\n";
            PrintWriter pWriter = new PrintWriter(client.getOutputStream(),true);
            pWriter.println(request);

            String tem;
            // 这里要注意二进制字节流转换为字符流编码要使用UTF-8
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));
            while((tem = bufferedReader.readLine())!=null) {
                System.out.println(tem);
            }

            client.close();

这里写图片描述

可以看到控制台打印了以上内容。

这里写图片描述

这里写图片描述

我们在回头来看浏览器访问得到的HTTP报文,是不是一模一样呢(除了时间)

看到现在,再回头想想我们一开始使用的HttpURLConnection类,其实这个类和其依赖的关联类,最终还是需要使用Socket编程实现HTTP报文的收发,只不过他内置了一些简单地HTTP Header和一些方便的方法(比如自动使用UTF-8解码流)。

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值