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

前面我们讨论了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浏览器了。

参考:

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

由两个有限项的等差数列B, C, 求有多少个有限项的等差数列A,满足C是A, B的所有公共项,若有无穷个A满足条件,输出-1

题目 思路: #include <bits/stdc++.h> using namespace std; #define int long long #define pb push_back const int maxn = 1e6 + 5, inf = 1e9 + 5, maxm = 4e4 + 5, mod = 1e9 + 7, N = 1e6; // int a[maxn], b[maxn]; int n, m; string s;int qpow(int a, int b){…

Android P 屏保和休眠相关知识

Android P添加屏保功能&#xff0c;如果休眠时间设定大于屏保时间&#xff0c;则先进入屏保&#xff0c;达到休眠时间后再进入休眠 需求&#xff1a; 添加屏幕互保开关&#xff0c;默认关闭。只保留时钟&#xff0c;可设定指针和数字、夜间模式。启用时间改多长时间无操作进入…

Microsoft Visual Studio 2022的安装与使用

1 软件介绍 Microsoft Visual Studio 2022是Microsoft Visual Studio软件的一个高版本&#xff0c;能够编写和执行C/C代码&#xff0c;具有强大的功能&#xff0c;是开发C/C程序的主流软件。 Microsoft Visual Studio 2022有三个版本&#xff0c;分别是社区版&#xff08;Commu…

ACDSee 2024旗舰版 下载安装汉化教程,ACDSee 最新版,附安装包和工具,全网最简单,轻松搞的安装,无套路

前言 ACDSee是一款数字资产管理、图片管理编辑工具软件&#xff0c;提供良好的操作界面&#xff0c;简单人性化的操作方式&#xff0c;优质的快速图形解码方式&#xff0c;支持丰富的RAW格式&#xff0c;强大的图形文件管理功能等。 准备工作 1、提前准备好 ACDSee 2024 安装…

手把手教你优雅的安装虚拟机 Ubuntu —— 图文并茂

目录 Ubuntu 获取Vmware 安装新建虚拟机Ubuntu 安装虚拟机工具安装更多内容 本文教你如何优雅的在虚拟机中安装 Ubuntu&#xff0c;图文并茂、包教包会&#xff01; Ubuntu 获取 Ubuntu 官网镜像下载速度较慢&#xff0c;建议从国内镜像网站下载&#xff0c;如网易、中科大、…

针对于vue element-plus组件的el-date-picker日期区间组件的日期格式问题以及如何进行区间判断

<template><el-date-picker v-model"value1" type"daterange" range-separator"To" start-placeholder"开始日期" end-placeholder"结束日期" :size"size" change"sarend" /> </templat…

【网站项目】基于SSM的231论文投稿系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

《合成孔径雷达成像算法与实现》Figure5.19

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

【word visio绘图】关闭visio两线交叉的跳线(跨线)

【visio绘图】关闭visio两线交叉的跳线&#xff08;跨线&#xff09; 1 如何在Visio绘图中关闭visio两线交叉的跳线&#xff08;跨线&#xff09;第一步&#xff1a;打开Visio并创建您的图形第二步&#xff1a;绘制您的连接线第三步&#xff1a;关闭跳线第四步&#xff1a;手动…

XSS_Labs靶场通关笔记

每一关的方法不唯一&#xff1b;可以结合源码进行分析后构造payload&#xff1b; 通关技巧&#xff08;四步&#xff09;&#xff1a; 1.输入内容看源码变化&#xff1b; 2.找到内容插入点&#xff1b; 3.测试是否有过滤&#xff1b; 4.构造payload绕过 第一关 构造paylo…

软件设计师——计算机网络(四)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师——计算机网络的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…

【心得】java JNDI配合RMI实现注入个人笔记

目录 JNDI RMI 基本概念 RMI 基本逻辑 恶意利用 JNDI注入RMI实现攻击 JNDI Java Naming and Directory Interface Java 命令和目录接口 让配置参数 和 代码 解耦的规范或者思想 低耦合 高内聚 Name 命名 java对象 通过 命名 绑定到 容器环境 java对象和一个特定的…

二分算法模版

二分算法模版 实数二分算法模版实数二分模版题 整数二分算法模版向上取整二分模版向下取整二分模版二分模版的注意点二分模版中check函数的实现能够使用二分的条件 二分主要分两类&#xff0c; 一类是对实数进行二分&#xff0c;一类是对整数进行二分 对整数二分又分成2种&…

分布式id-Leaf算法

一、介绍 由美团开发&#xff0c;开源项目链接&#xff1a;https://github.com/Meituan-Dianping/Leaf Leaf同时支持号段模式和snowflake算法模式&#xff0c;可以切换使用。ID号码是趋势递增的8byte的64位数字&#xff0c;满足上述数据库存储的主键要求。 Leaf的snowflake模…

谷歌vue插件安装包

链接&#xff1a;https://pan.baidu.com/s/1wTCqn7ttc-rF_wZScfEgPw?pwde7k6 提取码&#xff1a;e7k6 修改D:\谷歌浏览器插件安装包\devtools-main\packages\shell-chrome下manifest.json文件 将里面这四个文件地址改为src下面&#xff0c;因为地址在src下&#xff0c;直接…

Unity中URP下逐顶点光照

文章目录 前言一、之前额外灯逐像素光照的数据准备好后&#xff0c;还有最后的处理二、额外灯的逐顶点光照1、逐顶点额外灯的光照颜色2、inputData.vertexLighting3、surfaceData.albedo 前言 在上篇文章中&#xff0c;我们分析了Unity中URP下额外灯&#xff0c;逐像素光照中聚…

【RTP】webrtc 学习2: webrtc对h264的rtp打包

切片只是拷贝帧的split的各个部分到新的rtp 包的封装中。并没有在rtp包本身标记是否为关键帧FU-A 切片 输入的H.264 数据进行split :SplitNalu SplitNalu : 按照最大1200字节进行切分 切分后会返回一个数组 对于FU-A :split的数据总大小是 去掉一个字节的nalu header size …

高德地图实现-微信小程序地图导航

效果图&#xff1a; 一、准备阶段 1、在高德开放平台注册成为开发者2、申请开发者密钥&#xff08;key&#xff09;。3、下载并解压高德地图微信小程序SDK 高德开放平台&#xff1a; 注册账号(https://lbs.amap.com/)) 申请小程序应用的 key 应用管理(https://console.ama…

数据结构-顺序表详解专题

目录 顺序表 1.简单了解顺序表 2.顺序表的分类 2.1静态顺序表 2.2动态顺序表 2.3typedef命名作用 3.动态顺序表的实现 SeqList.h SeqList.c test.c 顺序表 1.简单了解顺序表 顺序表是线性表的一种&#xff0c;线性表是在逻辑上是线性结构&#xff0c;在物理逻辑上并…

【数据结构】栈、队列、数组、列表

数据结构是什么&#xff1f; 数据结构是计算机存储、组织数据的方式 是指数据相互之间是以什么方式排列在一起的。 数据结构是为了更加方便的管理和使用数据&#xff0c;需要结合具体的业务场景来进行选择。一般情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者…