【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请求消息 012private 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: 已经读取消息头 011while ((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.rhkb.cn/news/247372.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端性能优化——图片压缩和懒加载

图片压缩 使用第三方工具手动压缩图片使用Webpack工具在打包时自动压缩图片 这里主要介绍第二种方法。 &#xff08;1&#xff09;将小于某个大小的图片转化成 data URI 形式&#xff08;Base64 格式&#xff09;&#xff0c;减少请求数量&#xff0c;但是体积变得更大 modu…

每日一换,美好随心——发现那些让屏幕焕发新彩的壁纸!

1、方小童在线工具集 网址&#xff1a; 方小童 该网站是一款在线工具集合的网站&#xff0c;目前包含PDF文件在线转换、随机生成美女图片、精美壁纸等功能&#xff0c;喜欢的可以赶紧去试试&#xff01;

阿里云SSL证书DV型

我们都在SSL证书厂家&#xff08;CA&#xff09;并不多&#xff0c;全球可以兼容性99%机构仅有3-4家&#xff0c;少的可怜&#xff0c;全球绝大部分都要去厂家授权提供商申请&#xff0c;这意味着很多也喜欢去阿里云买SSL证书也是这样的原因。 SSL证书无论在哪里购买肯定是DV类…

ElasticSearch 学习笔记

基本概念 术语 文档&#xff08;document&#xff09;&#xff1a;每条记录就是一个文档&#xff0c;会以 JSON 格式进行存储 映射&#xff08;mapping&#xff09;&#xff1a;索引中文档字段的约束信息&#xff0c;类似 RDBMS 中的表结构约束&#xff08;schema&#xff09…

【LIBS】交叉编译TCPDUMP

目录 1. 安装编译工具2. 设置环境变量3. 编译libpcap3.1 安装依赖3.2 交叉编译 4. 编译TCPDUMP4.1 克隆仓库与生成构建环境4.2 静态链接LIBPCAP4.3 动态链接LIBPCAP4.4 构建与安装 5. 查看交叉编译结果5.1 文件布局 1. 安装编译工具 sudo apt-get install -y autoconf automak…

[MRCTF2020]Ez_bypass1

代码审计&#xff0c;要求gg和id的MD5值相等而gg和id的值不等或类型不等 相同MD5值的不同字符串_md5相同的不同字符串-CSDN博客 不过这道题好像只能用数组 下一步是passwd不能是纯数字&#xff0c;但是下一个判断又要passwd等于1234567 这里通过passwd1234567a实现绕过 原…

Differentiated Key-Value Storage Management for Balanced I/O Performance——论文泛读

ATC 2021 Paper 论文阅读笔记整理 问题 现代键值&#xff08;KV&#xff09;存储采用LSM树作为管理KV对的核心数据结构&#xff0c;但存在较高的写放大和读放大问题。现有的LSM树优化通常需要做出设计权衡&#xff0c;并无法同时在写入、读取和扫描方面实现高性能。 现有方法…

QT发生弹出警告窗口

QTC开发程序弹出警告窗口&#xff0c;如上图 实施代码&#xff1a; #include <QMessageBox> int main() {// 在发生错误的地方QMessageBox::critical(nullptr, "错误", "发生了一个错误&#xff0c;请检查您的操作。");}上面的文字可以更改&#x…

线性代数--------学习总结

高斯消去法&#xff1a;对于任意的矩阵&#xff0c;总是能够利用倍加和行变换的方法变化成为阶梯形矩阵&#xff08;每一行第一个非零元叫做主元&#xff0c;他所在的列就叫做主列------每一行的主列都在他上方任意一行主列的右边&#xff09;和行简化阶梯矩阵&#xff08;主元…

【Web前端实操17】导航栏效果——滑动门

滑动门 定义: 类似于这种: 滑到导航栏的某一项就会出现相应的画面,里面有对应的画面出现。 箭头图标操作和引用: 像一些图标,如果需要的话,可以找字体图标,比如阿里巴巴矢量图标库:iconfont-阿里巴巴矢量图标库 选择一个——>添加至购物车——>下载代码 因…

精通Python第13篇—数据之光:Pyecharts旭日图的魔法舞台

文章目录 引言准备工作绘制基本旭日图调整颜色和样式添加交互功能定制标签和标签格式嵌套层级数据高级样式与自定义进阶主题&#xff1a;动态旭日图数据源扩展&#xff1a;外部JSON文件总结 引言 数据可视化在现代编程中扮演着重要的角色&#xff0c;而Pyecharts是Python中一个…

【学网攻】 第(13)节 -- 动态路由(OSPF)

系列文章目录 目录 系列文章目录 文章目录 前言 一、动态路由是什么&#xff1f; 二、实验 1.引入 实验拓扑图 实验配置 实验验证 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学…

Python爬虫请求库安装

请求库的安装 爬虫可以简单分为几步&#xff1a;抓取页面、分析页面和存储数据。 在抓取页面的过程中&#xff0c;我们需要模拟浏览器向服务器发出请求&#xff0c;所以需要用到一些 Python 库来实现 HTTP 请求操作。在本教程中&#xff0c;我们用到的第三方库有 requests、S…

(自用)learnOpenGL学习总结-高级OpenGL-立方体贴图

ok终于来到了立方体贴图了&#xff0c;在这里面我们可以加入好看的天空包围盒&#xff0c;这样的画我们的背景就不再是黑色的了&#xff01; 首先&#xff0c;立方体贴图和前面的sampler2D贴图一样&#xff0c;不过是6个2D组成的立方体而已。 那么为什么要把6个组合在一起呢&…

Go、容器以及Linux调度器

在容器中运行Go应用程序时&#xff0c;需要设置合理的GOMAXPROCS&#xff0c;从而避免调度中因为资源不足而造成STW。原文: Go, Containers, and the Linux Scheduler Go开发的应用程序通常部署在容器中。在容器中运行时&#xff0c;重要的一点是要设置CPU限制以确保容器不会耗…

MacOS X 中 OpenGL 环境搭建 Makefile的方式

1&#xff0c;预备环境 安装 brew&#xff1a; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 安装glfw&#xff1a; brew install glfw 安装glew&#xff1a; brew install glew 2.编译 下载源代码…

喜报|「云原生数据库PolarDB」、「阿里云瑶池一站式数据管理平台」揽获“2023技术卓越奖”

日前&#xff0c;国内知名IT垂直媒体&技术社区IT168公布2023年“技术卓越奖”评选结果&#xff0c;经由行业CIO/CTO大咖、技术专家及IT媒体三方的联合严格评审&#xff0c;阿里云瑶池数据库揽获两项大奖&#xff1a;云原生数据库PolarDB荣获“2023年度技术卓越奖”&#xf…

【双目】双目精度评估完整方案,可以直接使用(内涵数据,代码和评估报告)

【双目】基于findChessboardCorners的双目精度评估&#xff0c;可以直接使用 数据和代码见 运行结果&#xff1a; 结论与分析&#xff1a; 在使用鱼眼相机进行双目精度上限评估时&#xff0c;出现1米距离的误差大于2米距离的误差可能有几个原因&#xff1a; 畸变影响&#x…

网络协议与攻击模拟_09部署DHCP服务器

一、部署DHCP服务器 Windows server部署DHCP服务器 1、虚拟机网络架构理解 Vmware里面不同的虚拟机可以设置相同的Vmnet网络&#xff0c;也可以设置不同的Vmnet网络。两台虚拟机设置相同的Vmnet1网卡&#xff0c;可以看作为使用虚拟交换机将两台Vmnet1的虚拟机连接起来的。 …

深入理解HarmonyOS UIAbility:生命周期、WindowStage与启动模式探析

UIAbility组件概述 UIAbility组件是HarmonyOS中一种包含UI界面的应用组件&#xff0c;主要用于与用户进行交互。每个UIAbility组件实例对应最近任务列表中的一个任务&#xff0c;可以包含多个页面来实现不同功能模块。 声明配置 为了使用UIAbility&#xff0c;首先需要在mod…