Java之HTTP网络编程(一):TCP/SSL网页下载

Charzueus 2021-02-23 14:33:59
java 编程 http 网络 TCP


目录

一、简介:HTTP程序设计

    1、HTTP系统设计

    2、HTTP客户端工作过程

    3、HTTP服务端工作过程

二、基于TCP Socket的HTTP网页下载

三、基于SSL Socket的HTTPS网页下载

四、HTTP客户端完整代码

五、界面完整代码

六、最后+演示


一、简介:HTTP程序设计

期末复习之HTTP网络编程,主要学习记录HTTP(s)协议的网络编程,包括使用TCP Socket进行三次握手的HTTP网页下载,和使用SSL Socket的安全传输的HTTPs网页下载,通过案例实践自行完成编程,认识http(s)的实际工作机制!

现在的HTTP客户端比早期的复杂得多,不仅包括了网页文件下载和显示,还有许多新的功能:跨平台的显示、参数的传递、动态网页的实现和用户交互等。

1、HTTP系统设计

  • 客户端软件(web浏览器:Chrome、360浏览器等)
  • 服务端软件(web服务器:微软的IIS、Apache Tomcat)

2、HTTP客户端工作过程

  • 客户端软件和服务器建立连接(TCP的三次握手);
  • 发送HTTP头格式协议;
  • 接收网页文件;
  • 显示网页。

3、HTTP服务端工作过程

  • 服务器软件开启80端口;
  • 响应客户的要求、完成TCP连接;
  • 检查客户端的HTTP头格式发送客户请求的网页文件(含动态网页)。 

图1 HTTP请求-响应完整过程

网页下载技术是搜索引擎、网络爬虫、网页采集器或网络推送服务等相关应用领域内的基础技术,下面会介绍日常使用到的两种协议(http和https)的网页访问下载。

二、基于TCP Socket的HTTP网页下载

对于TCP套接字的连接过程已经有很深刻的认识了,在本地测试通信也使用过TCP的Socket建立连接,同理,与HTTP服务器建立连接,也是利用TCP进行信息交互的。

建立连接之后,需要发送HTTP请求头,服务器确认请求者,开启两端的通信,客户端可以接收网页文件信息,进而经过渲染后显示网页页面。这里我们先实现接收网页文件信息,在下一篇实现浏览器对网页渲染之后的功能。

以www.baidu.com为例,与HTTP服务器建立连接之后,需要我们发送网页请求,也就是HTTP请求头。构造请求头如下:

GET / HTTP/1.1  

HOST: www.baidu.com

Accept: */*

Accept-Language: zh-cn

User-Agent: User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)

Connection: Keep-Alive

 需要严格按照格式发送,并且通常用StringBuffer类的toString()方法可将完整的HTTP请求头转换为字符串,一致发送到HTTP服务器。

StringBuffer msg = new StringBuffer();
msg.append("GET / HTTP/1.1\r\n"+
"HOST: "+domainName+"\r\n"+
"Accept: */*\r\n"+
"Accept-Language: zh-CN\r\n"+
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"+
"Connection: Keep-Alive\r\n"
);

 

 换行符使用\r\n是为了避免由于编码问题出错。

发送请求之后如果网页信息显示区返回的第一条信息是“HTTP/1.1 200 OK”,则说明访问正常。

可以看到HTTP服务器返回许多信息,这也是响应头,包含了许多关键信息内容。

三、基于SSL Socket的HTTPS网页下载

以上面设计的基于TCP通信传输的HTTP,我们尝试访问www.sina.com.cn,结果发现响应头信息第一行是HTTP/1.1 302 Moved Temporarily(站点被移除),出于安全考虑,现在绝大部分的web站点都将放弃HTTP而启用HTTPS,都使用了安全加密传输的HTTPS协议,而关闭了HTTP,只允许启用了SSL/TLS的HTTPS安全连接,这种连接默认是使用443端口。所以TCP Socket建立连接的方式无正常访问网页。

那只是端口改为443能正常吗,答案如下。

原因在前面也能看出,需要使用SSL/TLS的HTTPS安全连接,来建立与HTTPS服务器的通信,因此需要修改Socket类型。

这里使用到了Java安全套接字扩展(Java Secure Socket Extension,JSSE),基于SSL和TLS协议的Java网络应用程序提供了Java API以及参考实现,这里使用其客户端的SSLSocket套接字。SSLSocket相对之前学习的客户端套接字,只是创建方法不同,SSLSocket对象由SSLSocketFactory创建。

在类中声明成员变量以及创建Socket连接:

private SSLSocket socket;
private SSLSocketFactory factory;
factory=(SSLSocketFactory)SSLSocketFactory.getDefault();
socket=(SSLSocket)factory.createSocket(ip,Integer.parseInt(port));

 

 对SSL Socket的使用与TCP相同,只是创建方法不同,经过稍微修改之后,可以成功请求HTTPS网站的网页信息。

四、HTTP客户端完整代码

这里给出HTTP客户端的完整代码,HTTPS只需改改上述讲到的SSL Socket。

/*
* HTTPClient.java
* Copyright (c) 2020-12-21
* author : Charzous
* All right reserved.
*/
package chapter08;
import java.io.*;
import java.net.Socket;
public class HTTPClient {
private Socket socket;
private PrintWriter pw;
private BufferedReader br;
/**
* @param ip
* @param port
* @return
* @author Charzous
* @date 2020/12/21 14:52
*
*/
public HTTPClient(String ip, String port) throws IOException{
//主动向服务器发起连接,实现TCP三次握手
//不成功则抛出错误,由调用者处理错误
socket =new Socket(ip,Integer.parseInt(port));
//得到网络流输出字节流地址,并封装成网络输出字符流
OutputStream socketOut=socket.getOutputStream();
//参数true表示自动flush数据
pw=new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);
//得到网络输入字节流地址,并封装成网络输入字符流
InputStream socketIn=socket.getInputStream();
br=new BufferedReader(new InputStreamReader(socketIn,"utf-8"));
}
public void send(String msg) throws InterruptedException {
//输出字符流,由socket调用系统底层函数,经网卡发送字节流
try {
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
pw.println(msg);
}
public String receive(){
String msg=null;
try {
//从网络输入字符流中读取信息,每次只能接受一行信息
//不够一行时(无行结束符),该语句阻塞
//直到条件满足,程序往下运行
msg=br.readLine();
}catch (IOException e){
e.printStackTrace();
}
return msg;
}
public void close(){
try {
if (socket!=null)
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}

 

五、界面完整代码

我直接用一个图形界面来访问http和https,融合以上两个图形客户端的功能,使得该图形客户端既能访问443的https内容,也可以访问非443端口(一般是80)的http内容。

/*
* HTTPAllClientFX.java
* Copyright (c) 2020-12-21
* author : Charzous
* All right reserved.
*/
package chapter08;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class HTTPAllClientFX extends Application {
private Button btnExit=new Button("退出");
private Button btnSend = new Button("网页请求");
// private TextField tfSend=new TextField();//输入信息区域
private TextArea taDisplay=new TextArea();//显示区域
private TextField ipAddress=new TextField();//填写ip地址
private TextField tfport=new TextField();//填写端口
private Button btConn=new Button("连接");
private HTTPSClient httpsClient;
private HTTPClient httpClient;
private Thread readThread;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
BorderPane mainPane=new BorderPane();
//连接服务器区域
HBox hBox1=new HBox();
hBox1.setSpacing(10);
hBox1.setPadding(new Insets(10,20,10,20));
hBox1.setAlignment(Pos.CENTER);
hBox1.getChildren().addAll(new Label("网页地址:"),ipAddress,new Label("端口:"),tfport,btConn);
mainPane.setTop(hBox1);
VBox vBox=new VBox();
vBox.setSpacing(10);
vBox.setPadding(new Insets(10,20,10,20));
vBox.getChildren().addAll(new Label("网页信息显示区"),taDisplay);
VBox.setVgrow(taDisplay, Priority.ALWAYS);
mainPane.setCenter(vBox);
HBox hBox=new HBox();
hBox.setSpacing(10);
hBox.setPadding(new Insets(10,20,10,20));
hBox.setAlignment(Pos.CENTER_RIGHT);
hBox.getChildren().addAll(btnSend,btnExit);
mainPane.setBottom(hBox);
Scene scene =new Scene(mainPane,700,500);
primaryStage.setScene(scene);
primaryStage.show();
//连接按钮
btConn.setOnAction(event -> {
String ip=ipAddress.getText().trim();
String port=tfport.getText().trim();
taDisplay.clear();
try {
if (port.equals("443")){
httpsClient = new HTTPSClient(ip, port);
//成功连接服务器,接受服务器发来的第一条欢迎信息
taDisplay.appendText("服务器连接成功。\n");
readThread = new Thread(()->{
String receiveMsg=null;//从服务器接收一串字符
if (port.equals("443")){
while ((receiveMsg=httpsClient.receive())!=null){
//lambda表达式不能直接访问外部非final类型局部变量,需要定义一个临时变量
//若将receiveMsg定义为类成员变量,则无需临时变量
String msgTemp = receiveMsg;
Platform.runLater(()->{
taDisplay.appendText(msgTemp+"\n");
});
}
}
});
readThread.start();
}
else if (port.equals("80")){
httpClient = new HTTPClient(ip, port);
taDisplay.appendText("服务器连接成功。\n");
readThread = new Thread(()-> {
String receiveMsg = null;
while ((receiveMsg = httpClient.receive()) != null) {
String msgTemp = receiveMsg;
Platform.runLater(() -> {
taDisplay.appendText(msgTemp + "\n");
});
}
});
readThread.start();
}
}catch (Exception e){
taDisplay.appendText("服务器连接失败!"+e.getMessage()+"\n");
}
});
//网页请求按钮事件
btnSend.setOnAction(event -> {
String ip=ipAddress.getText().trim();
String port=tfport.getText().trim();
String domainName=ipAddress.getText().trim();
try {
StringBuffer msg = new StringBuffer();
msg.append("GET / HTTP/1.1\r\n"+
"HOST: "+domainName+"\r\n"+
"Accept: */*\r\n"+
"Accept-Language: zh-CN\r\n"+
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"+
"Connection: Keep-Alive\r\n"
);
if (port.equals("443"))
httpsClient.send(msg.toString());
else if (port.equals("80"))
httpClient.send(msg.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
btnExit.setOnAction(event -> {
try {
exit();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//窗体关闭响应的事件,点击右上角的×关闭,客户端也关闭
primaryStage.setOnCloseRequest(event -> {
try {
exit();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
private void exit() throws InterruptedException {
if (httpsClient!=null||httpClient!=null){
readThread.sleep(1000);//多线程等待,关闭窗口时还有线程等待IO,设置1s间隔保证所有线程已关闭
 httpsClient.close();
httpClient.close();
}
System.exit(0);
}
}
View Code

六、最后+演示

HTTP连接www.baidu.com,成功

HTTP连接www.sina.com.cn,失败

HTTPS连接www.sina.com.cn,成功

期末复习,顺便写博客记录下来,这篇为上篇,介绍HTTP网页请求下载,主要是HTTP(s)协议的网络编程,包括使用TCP Socket进行三次握手的HTTP网页下载,和使用SSL Socket的安全传输的HTTPs网页下载,通过案例实践自行完成编程,认识http(s)的实际工作机制!

期待:Java之HTTP网络编程(下篇:网页浏览器程序设计),将看到网页的HTML源代码,以及经过浏览器功能渲染之后的网页!

 

如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习!


我的博客园:https://www.cnblogs.com/chenzhenhong/p/14435762.html

我的CSDN博客:https://blog.csdn.net/Charzous/article/details/111470556


 

版权声明:本文为博主原创文章,遵循  CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

 

本文链接: https://blog.csdn.net/Charzous/article/details/111470556

 

版权声明
本文为[Charzueus]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/chenzhenhong/p/14435762.html

  1. Add filter and execute filter in excel by Java
  2. Dialogue in spring
  3. 解决Docker MySQL无法被宿主机访问的问题
  4. Oracle OCP 19c 认证1Z0-083考试题库(第1题)
  5. Solve the problem that docker MySQL cannot be accessed by the host
  6. Oracle OCP 19C certification 1z0-083 examination question bank (question 1)
  7. 在 2021 年你需要掌握的 7 种关于 JavaScript 的数组方法
  8. Seven array methods for JavaScript you need to master in 2021
  9. 在 2021 年你需要掌握的 7 种关于 JavaScript 的数组方法
  10. Struts2 + Json _ 配置,异常解决及深入了解Struts2返回JSON数据的原理及具体应用范例
  11. Seven array methods for JavaScript you need to master in 2021
  12. Struts2 + Json _ Configuration, exception resolution and in-depth understanding of Struts2 return JSON data principle and specific application examples
  13. (三)MySQL锁机制 + 事务
  14. (3) MySQL lock mechanism + transaction
  15. 在 2021 年你需要掌握的 7 种关于 JavaScript 的数组方法
  16. Seven array methods for JavaScript you need to master in 2021
  17. 基于Kafka和Elasticsearch构建实时站内搜索功能的实践
  18. Practice of building real time search function in the website based on Kafka and elasticsearch
  19. Golang 实现 Redis(9): 使用GeoHash 搜索附近的人
  20. RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MVC架构的网络封装类库
  21. Golang 实现 Redis(9): 使用GeoHash 搜索附近的人
  22. RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MVC架构的网络封装类库
  23. Golang realizes redis (9): using geohash to search nearby people
  24. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  25. Golang realizes redis (9): using geohash to search nearby people
  26. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  27. 读懂框架设计的灵魂 — Java 反射机制
  28. 治疗磁盘空间不足焦虑症,释放容器占用空间——Win10+docker篇
  29. 别再用jodatime了!全网最权威Java8日期时间类LocalDate、LocalDateTime详解
  30. Understanding the soul of framework design java reflection mechanism
  31. 配置客户端以安全连接到Apache Kafka集群4:TLS客户端身份验证
  32. Treating anxiety of insufficient disk space and releasing space occupied by containers -- win10 + docker
  33. Don't use jodatime any more! The most authoritative java 8 date and time classes in the whole network: detailed explanation of localdate and localdatetime
  34. Configure clients to connect securely to Apache Kafka Cluster 4: TLS client authentication
  35. Spring break
  36. 高性能MySQL(三):Schema与数据类型优化
  37. High performance mysql (3): schema and data type optimization
  38. redis解决缓存、击穿、雪崩
  39. redis
  40. 骑士卡:基于Kafka搭建消息中心,上亿消息推送轻松完成
  41. Redis solves cache, breakdown and avalanche
  42. redis
  43. Knight card: build a message center based on Kafka, and push hundreds of millions of messages easily
  44. Oracle OCP 19c 认证1Z0-083考试题库(第2题)
  45. redis的三种模式
  46. kubernetes和docker----2.学习Pod资源
  47. 谈一谈如何远程访问MySQL(腾讯云,云主机)
  48. Linux(五):Linux的文档编辑器Vi
  49. Oracle OCP 19C certification 1z0-083 examination question bank (question 2)
  50. 云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第6篇
  51. kubernetes和docker----2.学习Pod资源
  52. JSP基于Java开发Web应用程序特点有哪些?
  53. Three modes of redis
  54. Kubernetes and docker -- 2. Learning pod resources
  55. Linux (5): the document editor VI of Linux
  56. Cloud native project practice Devops (gitops) + k8s + BPF + SRE, using golang to develop production level mahjong game server from 0 to 1
  57. Kubernetes and docker -- 2. Learning pod resources
  58. What are the characteristics of JSP developing web application based on Java?
  59. Lottie error: java.lang.AssertionError : android.util.JsonReader .peek
  60. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library