【Java与网络6】实现一个自己的HTTP浏览器

news2024/9/28 5:27:58

前面我们讨论了HTTP协议的基本结构和Socket编程的基本原理,本文我们来整个大活:自己实现一个简单的浏览器。

目录

1.主线程循环体

2.readHostAndPort()方法的实现

3.readHttpRequest()方法的实现

4.sendHttpRequest()方法的实现

5.readHttpResponse(...)方法的实现


在讨论HTTP协议的具体请求和响应头字段之前,让我们先来利用以前所学的知识来实现一个HTTP模拟器。所谓HTTP模拟器就是可以在用户输入HTTP的请求消息后,由这个模拟器将HTTP请求发送给相应的服务器,再接收服务器的响应消息。这个HTTP模拟器有几下特点:
1.  可以手工输入HTTP请求,并向服务器发送。
2.  接收服务器的响应消息。
3.  消息头和实体内容分段显示,也就是说,并不是象Telnet等客户端一样将HTTP响
应消息全部显示,而是先显示消息头,然后由用户决定是否显示实体内容。
4.  集中发送请求。这个HTTP模拟器和Telnet不同的是,并不是一开始就连接服务器,
而是将域名、端口以及HTTP请求消息都输完后,才连接服务器,并将这些请求发送给服务器。这样做的可以预防服务器提前关闭网络连接的现象。
    5. 可以循环做上述的操作。
从以上的描述看,要实现这个HTTP模拟器需要以下五步:

  1. 主线程里建立一个死循环的while,在循环内部是一个请求/响应对。这样就可以向服务器发送多次请求/响应以了。下面的四步都是被包括在循环内部的。
  2. 从控制台读取域名和端口,这个功能可以由readHostAndPort(...)来完成。
  3. 从控制台读取HTTP请求消息,这个功能由readHttpRequest(...)来完成。
  4. 向服务器发送HTTP请求消息,这个功能由sendHttpRequest()来完成。
  5. 读取服务器回送的HTTP响应消息,这个功能由readHttpResponse(...)来完成。

下面我们就来逐步实现这五步:

1.主线程循环体

在建立这个循环之前,先建立一个中叫HttpSimulator的类,并在这个类中定义一个run方法用来运行这个程序。实现代码如下:

public class HttpSimulator {
    private Socket socket;
    private int port = 80;
    private String host = "localhost";
    private String request = ""; // HTTP请求消息 012
    private boolean isPost, isHead;

    public void run() throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true)  // 开始大循环
        {
            try {
                if (!readHostAndPort(reader)) break;
                readHttpRequest(reader);
                sendHttpRequest();
                readHttpResponse(reader);
            } catch (Exception e) {
                System.out.println("err:" + e.getMessage());
            }

        }
    }

    public static void main(String[] args) throws Exception {
        new HttpSimulator().run();

    }

从上面的代码可以看出,我们分别调用了上述的四个方法。这些方法的具体实现将在后面讨论。上面的代码除了调用这四个核心方法外,还做了一些准备工作。在008至012行定义了一些以后要用到的变量。在016和017行使用控制台的输入流建立了BufferedReader对象,通过这个对象,可以直接从控制台读取字符串,而不是一个个地字节。

2.readHostAndPort()方法的实现

 这个方法的主要功能是从控制台读取域名和端口。域名和端口通过":"隔开,":"和域名以及端口之间不能有空格。当从控制台读取一个"q"时,这个函数返回false,表示程序可以退出了,否则返回true,表示输入的域名和端口是正确的。这个方法的实现代码如下:

    private boolean readHostAndPort(BufferedReader consoleReader) throws Exception {
        System.out.print("host:port>");
        String[] ss = null;
        String s = consoleReader.readLine();
        if (s.equals("q")) return false;
        else {
            ss = s.split("[:]");
            if (!ss[0].equals("")) host = ss[0];
            if (ss.length > 1) port = Integer.parseInt(ss[1]);
            System.out.println(host + ":" + String.valueOf(port));
            return true;
        }
    }

上面的代码,我们做一个分析:

  1. 这个方法有一个BufferedReader类型的参数,这个参数的值就是在HttpSimulator.java中的第016和017行根据控制台输入流建立的BufferedReader对象。
  2. 这输出HTTP模拟器的控制符,就象Windows的控制台的"C:">"一样。
  3. consoleReader.readLine()从控制台读取一行字符串。
  4. ss = s.split("[:]") 通过字符串的split方法和响应的正则表示式("[:]")将域名和端口分开。域名的默认值是localhost,端口的默认值是80。 

3.readHttpRequest()方法的实现

 这个方法的主要功能是从控制台读取HTTP请求消息,如果输入一个空行,表示请求消息头已经输完;如果使用的是POST方法,还要输入POST请求的实体内容。这个方法的实现代码如下:

    private void readHttpRequest(BufferedReader consoleReader) throws Exception {
        System.out.println("请输入HTTP请求:");
        String s = consoleReader.readLine();
        request = s + "\r\n";
        boolean isPost = s.substring(0, 4).equals("POST");
        boolean isHead = s.substring(0, 4).equals("HEAD");
        while (!(s = consoleReader.readLine()).equals("")) {
            request = request + s + "\r\n";
        }
        request = request + "\r\n";
        if (isPost) {
            System.out.println("请输入POST方法的内容:");
            s = consoleReader.readLine();
            request = request + s;

        }
    }

上面的代码,我们简单解释一下:

  1. consoleReader.readLine() 读入HTTP请求消息的第一行。
  2. isPost 和isHead 用于确定所输入的请求方法是不是POST和HEAD。之后的代码读入HTTP请求消息的其余行。
  3. if (isPost) 代码段的功能是:如果HTTP请求使用的是POST方法,要求用户继续输入HTTP请求的实体内容。

4.sendHttpRequest()方法的实现

这个方法的功能是将request变量中的HTTP请求消息发送到服务器。下面是这个方法的实现代码:

   private void sendHttpRequest() throws Exception {
        socket = new Socket();
        socket.setSoTimeout(10 * 1000); //设置读取数据超时为10秒。
        System.out.println("正在连接服务器");
        socket.connect(new InetSocketAddress(host, port), 10 * 1000); //超时时间
        System.out.println("服务器连接成功!");
        OutputStream out = socket.getOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(out);
        writer.write(request);
        writer.flush();

    }

5.readHttpResponse(...)方法的实现

这个方法的主要功能是从服务器读取返回的响应消息。首先读取了响应消息头,然后要求用户输入Y或N以确定是否显示响应消息的实体内容。这个程序之所以这样做,主要有两个原因:
(1) 为了研究HTTP协议。
(2) 由于本程序是以字符串形式显示响应消息的,因此,如果用户请求了一个二进制Web资源,如一个rar文件,那么实体内容将会显示乱码。所以在显示完响应消息头后由用户决定是否显示实体内容。
这个方法的实现代码如下: 

 private void readHttpResponse(BufferedReader consoleReader) {
        String s = "";
        try {
            InputStream in = socket.getInputStream();
            InputStreamReader inReader = new InputStreamReader(in);
            BufferedReader socketReader = new BufferedReader(inReader);
            System.out.println("---------HTTP头---------");

            boolean b = true; // true: 未读取消息头 false: 已经读取消息头 011
            while ((s = socketReader.readLine()) != null) {
                if (s.equals("") && b == true && !isHead) {
                    System.out.println("------------------------");
                    b = false;
                    System.out.print("是否显示HTTP的内容(Y/N):");
                    String choice = consoleReader.readLine();
                    if (choice.equals("Y") || choice.equals("y"))   {
                        System.out.println("---------HTTP内容---------");
                        continue;

                    }                    else break;

                } else System.out.println(s);

            }
        } catch (Exception e) {
            System.out.println("err:" + e.getMessage());

        } finally {
            try {
                socket.close();

            } catch (Exception e) {

            }
        } System.out.println("------------------------");

    }

在上面的代码中013行是最值得注意的。其中s.equals("")表示读入一个空行(表明消息头已经结束);由于在实体内容中也可以存在空行,因此,b == true来标记消息头是否已经被读过,当读完消息头后,将b设为false,如果以后再遇到空行,就不会当成消息头来处理了。当HTTP请求使用HEAD方法时,服务器只返回响应消息头;因此,使用!isHead来保证使用HEAD发送请求时不显示响应消息的内容实体。
现在我们已经实现了这个HTTP模拟器,下面让我们来运行并测试它。 

我们将上面的代码执行起来,然后会有个输入的提示:

我们输入www.csdn.net

然后继续分行输入如下的HTTP请求消息:

GET / HTTP/1.1
Host: www.csdn.net

之后根据提示输入Y,运行的结果如下所示:

------------------------
是否显示HTTP的内容(Y/N):Y
---------HTTP内容---------
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>WAF</center>
</body>
</html>

到此我们就实现了一个简单的HTTP浏览器了。

参考:

本文参考了李宁老师(蒙娜丽宁)的文章和介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1414440.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vector迭代器失效

目录 迭代器失效的场景 insert插入元素时迭代器失效 erase删除元素时迭代器失效 本期我们主要进行vector迭代器失效问题的讨论。 迭代器失效的场景 insert插入元素时迭代器失效 先看代码&#xff1a; iterator insert(iterator pos, T val){assert(pos > _start);ass…

不知道如何批量处理图片

你是否曾经遇到过需要批量处理大量图片的情况&#xff1f;例如&#xff0c;需要将多张图片转换为统一格式、调整大小或进行美化处理。这种时候&#xff0c;如果一张张手动处理&#xff0c;不仅效率低下&#xff0c;还容易出错。那么&#xff0c;有没有一种方法可以快速、准确地…

web前端项目-动画特效【附源码】

文章目录 一&#xff1a;赛车游戏动画HTML源码&#xff1a;JS源码&#xff1a;CSS源码&#xff1a;&#xff08;1&#xff09;normalize.css&#xff08;2&#xff09;style.css 二&#xff1a;吉普车动画演示HTML源码&#xff1a;CSS源码&#xff1a;&#xff08;1&#xff09…

matlab对负数开立方根得到虚数的解决方案

问题描述&#xff1a;在matlab中&#xff0c;对负数开立方根&#xff0c;不出意外你将得到虚数。 例如 − 27 3 \sqrt[3]{-27} 3−27 ​&#xff0c;我们知道其实数解是-3&#xff0c;但在matlab中的计算结果如下&#xff1a; 问题原因&#xff1a;matlab中的立方根运算是在…

仅使用 Python 创建的 Web 应用程序(前端版本)第08章_商品详细

在本章中,我们将实现一个产品详细信息页面。 完成后的图像如下。 Model、MockDB、Service都是在产品列表页实现的,所以创建步骤如下。 No分类内容1Page定义PageId并创建继承自BasePage的页面类2Application将页面 ID 和页面类对添加到 MultiPageApp 的页面中Page:定义PageI…

项目管理平台

技术架构&#xff1a; MySQL、Servlet、JSP 功能模块&#xff1a; 从管理员角度看: 用户登入系统后&#xff0c;可以修改管理员的密码。同时具有以下功能&#xff1a; 1、管理员可以管理具体项目信息。 2、管理员可以管理项目经费信息。 3、管理员可以管理项目资源信息。 4、…

确定软件项目范围基准 5个重点

软件项目范围基准明确了项目的边界、目标和主要交付成果&#xff0c;有助于提高项目成本、进度和资源估算的准确性&#xff0c;便于实施项目控制&#xff0c;而且还可以帮助我们清楚分派责任&#xff0c;防止范围蔓延&#xff0c;从而提升项目的成功率。 如果没有明确确定范围基…

Hive实战 —— 电商数据分析(全流程详解 真实数据)

目录 前言需求概述数据清洗数据分析一、前期准备二、项目1. 数据准备和了解2.确定数据粒度和有效列3.HDFS创建用于上传数据的目录4.建库数仓分层 5.建表5.1近源层建表5.2. 明细层建表为什么要构建时间维度表&#xff1f;如何构建时间维度表&#xff1f; 5.3 轻聚层建表6. 指标数…

transformer架构的理解

一、transformer 架构 如上图所示&#xff0c;transformer&#xff08;形状像变压器&#xff1f;或者翻译成变形金刚&#xff0c;由不同模块拼装而成&#xff09;的架构左边是n个结构体相同的编码器&#xff08;例如&#xff0c;原论文是6个编码器的串联&#xff09;&#xff0…

第6章 python深度学习—第6章(波斯美女)

第6章 深度学习用于文本和序列 6.1 处理文本数据 与其他所有神经网络一样&#xff0c;深度学习模型不会接收原始文本作为输入&#xff0c;它只能处理数值张量。 文本向量化&#xff08;vectorize&#xff09;是指将文本转换为数值张量的过程。它有多种实现方法。 将文本分割…

迪文串口屏文本显示的使用

一、使用背景 由于迪文屏的数据变量显示功能不能显示字母类等字符&#xff0c;比如屏幕上要显示期日时间格式为2024-1-27 11:22:20&#xff0c;则不适合用数据变量显示&#xff0c;此时可以使用文本显示功能。 二、实现过程 1、首先查看迪文屏的使用手册DGUSII 应用开发指南…

两个近期的计算机领域国际学术会议(软件工程、计算机安全):欢迎投稿

近期&#xff0c;受邀担任两个国际学术会议的Special session共同主席及程序委员会成员&#xff08;TPC member&#xff09;&#xff0c;欢迎广大学界同行踊跃投稿&#xff0c;分享最新研究成果。期待这个夏天能够在夏威夷檀香山或者加利福尼亚圣荷西与各位学者深入交流。 SERA…

【投稿优惠|EI优质会议】2024年材料化学与清洁能源国际学术会议(IACMCCE 2024)

【投稿优惠|优质会议】2024年材料化学与清洁能源国际学术会议(IACMCCE 2024) 2024 International Conference Environmental Engineering and Mechatronics Integration(ICEEMI 2024) 一、【会议简介】 随着全球能源需求的不断增长&#xff0c;清洁能源的研究与应用成为了国际…

【leetcode题解C++】144. 94. 145.二叉树前序、中序、后序遍历 and 102.二叉树的层序遍历

144. 二叉树前序遍历 给出一个根节点&#xff0c;返回前中后序遍历的结果的。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;root…

vue3 npm i 一直卡到不动

一. 首先node 版本要18.0及以上 查看node版本并安装指定版本 二. 查看npm镜像源以及指定安装npm的镜像 三. 删除项目中的package-lock.json文件 最好是把node_modules安装包也删除掉&#xff0c;然后npm i 就可以了

js打地鼠

文章目录 1实现效果2代码实现 1实现效果 游戏难度&#xff1a;简单&#xff0c;一般&#xff0c;困难&#xff0c;噩梦&#xff08;控制setInterval的time参数&#xff09; 按钮功能&#xff1a;结束&#xff08;可以通过修改gameScore的值来修改判定结束的分数&#xff09;&am…

python-自动化篇-办公-excel-实例应用(一维转二维)

文章目录 准备代码效果 准备 放根目录 代码 import openpyxl wbopenpyxl.load_workbook(业绩表.xlsx) if not 二维表 in wb.sheetnames:nwswb.create_sheet(二维表)wswb.worksheets[0]rngslist(ws.values)[1:]mmlist({m.value: for m in ws[b][1:]})namelist({m.value: for …

语义分割(3):损失函数解析

文章目录 1. 常见语义分割损失1.1 Cross Entropy1.2 dice Loss1.2.1 为什么使用Dice loss1.2.2 公式1.2.3 Dice loss 和 F1-score代码 1.3 focal loss1.3.1 公式&#xff1a;1.3.2 代码 2. 语义分割损失应用参考 语义分割任务实际上是一种像素层面上的分类&#xff0c;需要识别…

NPDP认证:产品经理的国际专业认证

你是否想证明自己在产品开发与管理方面的专业能力&#xff1f;NPDP认证正是你需要的&#xff01;&#x1f525; NPDP认证&#xff0c;即产品经理国际资格认证&#xff0c;由美国产品开发与管理协会&#xff08;PDMA&#xff09;所发起&#xff0c;是全球公认的新产品开发专业认…

Vulnhub靶机:FunBox 8

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;FunBox 8&#xff08;10.0.2.38&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/funb…