✨你好啊,我是“ 罗师傅”,是一名程序猿哦。
🌍主页链接:楚门的世界 - 一个热爱学习和运动的程序猿
☀️博文主更方向为:分享自己的快乐 briup-jp3-ing
❤️一个“不想让我曾没有做好的也成为你的遗憾”的博主。
💪很高兴与你相遇,一起加油!

前言

目标:Java高级编程,灵活运用反射,线程,IO和网络等进行编程

基本概念

计算机网络

强烈推荐真的超级棒滴⚓⚓:图解网络介绍 | 小林coding

也称为计算机通信网,其时利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式链接起来,以功能完善的网络软件及协议实现资源共享和信息传递的系统。最简单的计算机网络就只有两台计算机和连接它们的一条链路,即两个节点和一条链路

通过编程方式,使得计算机网络中不同计算机上的应用程序间能够进行数据的传输,这就是网络编程要做的事情

软件结构

  • Client/Server(C/S结构),表示 客户端/服务器 的软件结构,例如QQ、 微信、百度网盘客户端等,只要是需要我们下载安装,并且和服务器通 信的这一类软件,都属于C/S的软件结构。
  • Browser/Server(B/S结构),表示 浏览器/服务器 的软件结构,例如淘 宝网、新浪新闻、凤凰网等,只要是需要使用浏览器,并且和服务器通 信的这一类软件,都属于B/S的软件结构

C/S VS B/S :

  • C/S在图形的表现能力上以及运行的速度上肯定是强于B/S的

  • C/S需要运行专门的客户端,并且它不能跨平台,C++在windows下写的程序肯定是不能在linux下运行

  • B/S不需要传媒的客户端,只要系统中安装了浏览器即可访问,方便用户的使用

  • B/S是基于网页语言的、与操作系统无关,所以跨平台也是它的优势。

    随着网页技术以及浏览器的进步,B/S在表现能力上的处理以及运行的速 度上会越来越快,所以现在越来越多的C/S结构的软件,也推出了对应B/S的 版本,例如webQQ,在线文档工具、在线画图工具等。同时也包括很多网 页版的游戏,也是随着前端技术的发展,慢慢出现的。

通信要素

如果两台计算机上的应用程序能够实现通信,那么必须解决3个问题,找 到对方计算机,找到对方应用程序,采用相同的通信协议。

  • IP地址

可认为IP地址是个标识号,通过这个标识号能够找到网络世界中唯一的那个计算机

  • 端口

端口号可以用来标识计算机中唯一的那个应用程序。网络的通信,本质上是两个应用程序的通信。每台计算机都有很多应用程序,在网络通信时,就采用端口号进行区分这些应用程序。

  • 协议

当我们和其他人沟通交流的时候都要使用互相能听懂的语言。计算机也 一样,计算机与计算机通过网络进行数据和信息交换的时候,也要使用 同样的“语言”,这个语言被称为网络通讯协议。

网络通信协议对数据的传输格式、传输速率、传输步骤等做了统一规 定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议 和TCP协议。

IP地址

全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常 见的IP分类为:ipv4和ipv6

  • IPv4:共32位,表示范围43亿左右,一般使用点分4段表示法
  • IPv6:共128位,表示范围更大,号称可以为地球上每一粒沙子分配一个IP

IP域名: 本质上也是一个IP地址,可读性更好,更容易记忆,需要使用dns域名解 析服务器解析。

公网、内网IP:

  • 公网IP:是可以连接互联网的IP地址
  • 内网IP:也叫局域网IP,只能组织机构内部使用
    • 192.168.开头的是常见局域网地址,范围即为192.168.0.0~192.168.255.255,专门为组织机构内部使用

DOS常用命令:

  • ipconfig:查看本机IP地址
  • ping IP地址:检查网络是否连通

特殊IP地址:

  • 127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用

端口号

端口号用来标记正在计算机设备上运行的应用程序,其范围是0~65535。 其中,0~1023之间的端口号用于系统内部的使用,我们自己普通的应用程 序要使用1024以上的端口号即可,同时也要避免和一些知名应用程序默认 的端口冲突,例如:oracle启动后默认占用端口号1521mysql启动后默认 占用端口号3306redis启动后默认占用端口号6379tomcat启动后默认占 用端口号8080

  • 程序1,在客户端电脑中的内存中运行着,并且占用了端口号8899

  • 程序2,在服务器端电脑中的内存中运行着,并且占用了端口号8888 俩个程序,通过IP+端口号的方式,找到对方进行通信,传输信息

端口分类:

  • 周知端口

    • 0~1023,被预先定义的知名应用占用,如:HTTP占用80,FTP占用21,MySQL占用3306
  • 注册端口

    • 1024~49151,分配给用户进程或某些应用程序(可自己分配)
  • 动态端口

    • 49152~65535,之所以被称为动态端口,是因为它一般不固定分配某种进程,而是动态分配

注意事项:

  • 自己开发的程序一般选择使用注册端口
  • 同一时刻一个设备中两个程序的端口号不能重复,否则出错

通信协议

计算机网络中,不同设备进行连接和通信的规则被称为网络通信协议。通 信协议,对俩台计算机之间所传输数据的传输格式、传输步骤等做了统一 规定要求,通信双方必须同时遵守才能完成数据交换。

OSI(Open System Interconnect),即开放式网络互连标准。 一般叫OSI参 考模型,是ISO(国际标准化组织)在1985年研究的网络互连模型,它共包 含七层(why?),具体可参考下图。

TCP/IP网络模型,是事实上的国际标准,它被简化为了四个层,从下到上分 别依次是应用层、传输层、网络层、网络接口层。

  • 应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等
  • 传输层:主要使用网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议
  • 网络层:网络层时整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
  • 链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。

TCP和UDP

虽然完整的通信过程比较复杂,但是JavaAPI中把这些通信实现的细节进 行了封装,使得我们可以直接使用相应的类和接口,来进行网络程序开 发,而不用考虑通信的细节。

java.net包中对常见的两种通信协议进行了封装和支持:UDP和TCP

  • UDP:用户数据报协议(User Datagram Protocol) (了解)

UDP是无连接通信协议,在数据传输时,数据的发送端和接收端不建立连接,也不能保证对方能接受成功。

例如,当一台计算机向另外一台计算机发送数据时(UDP),发送端不会确认接收端是否存在,就会直接发出数据,同样接收端在收到数据 时,也不会向发送端反馈是否收到数据。

由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

但是在传输重要数据时,不建议使用UDP协议,因为它不能保证数据传输的完整性

  • TCP,传输控制协议(Transmission Control Protocol)(important)

TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立连接,然后再传输数据,它提供了两台计算机之间可靠的、无差错的。

在TCP连接中,将计算机明确划分为客户端与服务器端,并且由客户端 向服务端发出连接请求,每次连接的创建都需要经过“三次握手”的过 程,四次挥手断开连接。

TCP的三次握手: (打电话过程,客户端A,服务器B)

TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互, 以保证连接的可靠

  • 第一次握手:客户端向服务器端发送连接请求,等待服务器确认(A 打电话给B, 嘟嘟嘟嘟等待中
  • 第二次握手:服务器端向客户端回送一个响应,通知客户端收到了连接请求(**B接电话 喂~**)
  • 第三次握手:客户端再次向服务器端发送确认信息,确认连接(A听到B的声音后,回应:喂~在干嘛呀🌳)

完成上述的三次握手后,客户端和服务器端的连接就已经建立了,在这个安全的、可靠的连接基础之上,就可以开始进行数据传输了。 TCP协议应用的十分广泛,例如下载文件、浏览网页、远程登录等。

TCP的四次挥手: (打电话过程,客户端A,服务器B)

TCP网络编程

概述

在TCP通信协议下,计算机网络中不同设备上的应用程序之间可以通信,通信时需严格区分客户端(Client)与服务器端(Server)。

在Java中,对于这样基于TCP协议下连接通信的客户端和服务端,分别进行 了抽象:

  • java.net.ServerSocket:类表示服务端
  • java.net.Socket:类表示客户端

使用Socket 和 ServerSocket进行的编程,也称为套接字编程

通信流程

TCP客户端和服务器进行通信,其通信流程是固定的,具体如下:

服务器端:

  • 创建ServerSocket(需绑定端口,方便客户端连接)
  • 调用ServerSocket对象的accept()方法接收一个客户端请求,得到一个Socket
  • 调用SocketgetInputStream() 和 getOutputStream() 获取和客户端相连的IO流
    • 输入流可以读取客户端发送过来的数据
    • 输出流可以发送数据到客户端
  • 操作完成,close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建绑定到指定端口的服务器套接字 (就是服务在监听这个端口比如port=2333)
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}

// 监听要连接到此的套接字并接受它 (客户端连接这个端口,就会被服务器监听到)
public Socket accept() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!isBound())
throw new SocketException("Socket is not bound yet");
Socket s = new Socket((SocketImpl) null);
implAccept(s);
return s;
}

注意:accept() 方法是阻塞的,作用是等待客户端连接,如果有客户端连接则立马返回,如果没有客户端连接则一致阻塞等待。

客户端:

  • 创建Socket连接服务器(需指定服务器ip地址、端口),找对应的服务器进行连接
  • 调用SocketgetInputStream()和getOutputStream()方法获取和服务端相连的IO流
    • 输入流可以读取服务端输出流写出的数据
    • 输出流可以写出数据到服务端的输入流
  • 操作完毕,close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 构造方法
// 创建流套接字并将其连接到指定IP指定端口号
public Socket(InetAddress address, int port) throws IOException {
this(address != null ? new InetSocketAddress(address, port) : null,
(SocketAddress) null, true);
}

// 创建流套接字并将其连接到指定主机上的指定端口号
public Socket(String host, int port) throws UnknownHostException, IOException
{
this(host != null ? new InetSocketAddress(host, port) :
new InetSocketAddress(InetAddress.getByName(null), port),
(SocketAddress) null, true);
}
1
2
3
4
// 返回此套接字的输入流
public InputStream getInputStream() throws IOException {}
// 返回此套接字的输出流
public OutputStream getOutputStream() throws IOException {}

基础案例

搭建TCP客户端,发送信息到服务器

  • Test01_TcpClient
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test01_TcpClient {
public static void main(String[] args) throws IOException {
// 1、创建Socket对象,(指定)
String ip = "127.0.0.1";
int port = 23333;
Socket socket = new Socket(ip, port);
System.out.println("success connect port 23333 server, socket: " + socket);

// 2、获取数据传输的IO流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();

// 3、数据传输
// 3.1、先发送数据给服务器
os.write("hello server I am Tcp client".getBytes());
System.out.println("success send data! ");

// 3.2、接收服务返回的消息
byte[] buff = new byte[1024];
int len = is.read(buff);
System.out.println("read: " + new String(buff, 0, len));

// 4、关闭资源
os.close();
is.close();
socket.close();
}
}

  • Test02_TcpServer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Test02_TcpServer {
public static void main(String[] args) throws IOException {
// 1、创建ServerSocket(port)
int port = 23333;
ServerSocket serverSocket = new ServerSocket(port);
// 2、调用SocketServer的accept() 方法获得一个Socket
System.out.println("listening for port 23333");
Socket socket = serverSocket.accept();
// 3、获取网络通信的IO流对象
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 4、数据传输
// 4.1、获取客户端传输的数据
byte[] buff = new byte[1024];
int len = is.read(buff);
System.out.println("server read succeeded!");
System.out.println(new String(buff, 0, len));
// 4.2、server 发送消息给 client
System.out.println("server send mes to client");
os.write("I love you".getBytes());
System.out.println("success send mes!");
// 5、关闭资源
os.close();
is.close();
socket.close();
serverSocket.close();
}
}

反转案例

搭建一个TCP客户端,从键盘录入整行数据(遇到quit结束录入)然后发送 给服务器,再接收服务器返回的数据并输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Test03_ReverseClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 23333);
System.out.println("success connect port 23333 server, socket: " + socket);

InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();

// 定义增强流 更好的实现功能
PrintStream ps = new PrintStream(os);
Reader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);

// 核心
Scanner sc = new Scanner(System.in);
System.out.println("please input data: ");
String line = null;
while (true) {
line = sc.nextLine();
ps.println(line);
System.out.println("success send data");
if ("quit".equals(line)) break;
// 从服务器接收 返回的消息
String msg = br.readLine();
System.out.println("receive msg: " + msg);
}

System.out.println("client will close");
br.close();
reader.close();
ps.close();
os.close();
is.close();
socket.close();
}
}

搭建一个TCP服务器,逐行接收从客户端发送过来的字符串(读取到quit字 符串则结束读取),然后对字符串进行反转,最后把反转的字符串返回给 客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Test04_ReverseServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(23333);
System.out.println("success start server port 23333");

Socket socket = serverSocket.accept();
System.out.println("success connect client " + socket);

// 3、获取IO流并增强
PrintStream ps = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

// 4.业务操作
// 4.1、逐行收取数据
String line = null;
StringBuffer sb = new StringBuffer();
while (true) {
sb.delete(0, sb.length());
line = br.readLine();
System.out.println("read: " + line);

// 读取到quit,则结束
if ("quit".equals(line)) break;

// 4.2 对数据 进行 反转
sb.append(line);
sb.reverse();
String msg = sb.toString();

ps.println(msg);
System.out.println("server success send reverse msg");
}

//5.关闭资源
System.out.println("服务器即将关闭!");
ps.close();
br.close();
socket.close();
serverSocket.close();
}
}

传输对象

准备一个stud.txt文件,放到src/dir目录下,内容如下:

1
2
3
4
010.tom.19
001.zs.21
003.lucy.19
002.jack.20

搭建TCP客户端,逐行读取stud.txt中数据,然后转化为Student对象,最后 将所有对象发送到服务器端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
* 逐行读取stud.txt中 数据 --> Student对象
* 再将所有对象 发送到 服务器
*/
public class Test05_对象客户端 {
public static void main(String[] args) throws
Exception {
//1.搭建客户端装增强
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//3.解析文件 得到对象 并发送
BufferedReader br = new BufferedReader(new FileReader("src/dir/stud.txt"));
String line = null;
List<Student> list = new ArrayList<>();
while((line = br.readLine()) != null) {
//拆分数据 001.zs.20
String[] arr = line.split("[.]");
String id = arr[0];
String name = arr[1];
int age = Integer.parseInt(arr[2]);
//封装学生对象 并添加到 list集合
Student s = new Student(id,name,age);
list.add(s);
}
// 发送集合(含所有学生)
oos.writeObject(list);
System.out.println("学生发送成功,数量: " + list.size());
//4.关闭资源
System.out.println("客户端即将关闭");
oos.close();
socket.close();
}
}

搭建TCP服务器,接收从客户端发送过来的所有学生,然后遍历输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* 搭建服务器,接收从客户端发送过来的所有学生
* 遍历输出
*/
public class Test06_对象服务器 {
public static void main(String[] args) throws
Exception {
//1.搭建服务器,指定端口
ServerSocket server = new ServerSocket(8989);
System.out.println("服务器启动成功 端口 8989...");
//2.接收客户端的连接
Socket socket = server.accept();
System.out.println("客户端成功连接:" + socket);
//3.获取输入流 并 包装增强
InputStream is = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
//4.接收 遍历数据
List<Student> list =
(List<Student>)ois.readObject();
System.out.println("成功接收学生数量: " +
list.size());
for (Student s : list) {
System.out.println(s);
}
//5.资源关闭
System.out.println("服务器即将终止");
ois.close();
socket.close();
server.close();
}
}

多线程案例

搭建一个TCP客户端,读取键盘信息,然后发送给服务器,遇到quit退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* 接盘录入信息 然后发给服务器,遇到quit结束
*/
public class Test07_聊天客户端 {
public static void main(String[] args) throws
Exception {
//1.搭建客户端
Socket socket = new Socket("127.0.0.1",8989);
System.out.println("客户端成功连接,socket: " + socket);
//2.封装得到IO流
PrintStream ps = new PrintStream(socket.getOutputStream());
//3.读取键盘信息 然后发送给服务器
Scanner sc = new Scanner(System.in);
String line = null;
while(true) {
line = sc.nextLine();
if("quit".equals(line))
break;
ps.println(line);
}
//4.关闭资源
System.out.println("客户端即将关闭");
ps.close();
socket.close();
}
}

搭建TCP服务器,分离多线程接收客户端发送过来的数据,然后进行输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class Test08_聊天服务器 {
//多线程 服务器
public static void main(String[] args) throws
Exception {
//1.搭建服务器,指定端口
ServerSocket server = new ServerSocket(8989);
System.out.println("服务器启动成功 端口 8989...");
while (true) {
//2.接收客户端的连接
Socket socket = server.accept();
System.out.println("客户端成功连接:" + socket);
// 单独分离子线程 为 当前客户端提供服务
Thread th = new Thread() {
@Override
public void run() {
//1.获取IO流
BufferedReader br = null;
try {
br = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
//2.数据操作
String line = null;
while ((line = br.readLine()) !=
null) {
System.out.println("read: " +
line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//3.关闭资源
System.out.println("客户端对应资源即 将关闭!");
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
th.start();
}
}

//实现 单线程服务器,接收多个客户端 聊天信息
public static void main01(String[] args) throws
Exception {
//1.搭建服务器,指定端口
ServerSocket server = new ServerSocket(8989);
System.out.println("服务器启动成功 端口 8989...");
while (true) {
//2.接收客户端的连接
Socket socket = server.accept();
System.out.println("客户端成功连接:" + socket);
//3.封装IO流对象,逐行读取聊天信息并输出
BufferedReader br =
new BufferedReader(new
InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("read: " + line);
}
//4.关闭资源
System.out.println("客户端即将关闭: " + socket);
br.close();
socket.close();
//能否关闭 server
}
}
}

UDP网络编程

概述

在UDP通信协议下,两台计算机之间进行数据交互,并不需要先建立连接,客户端直接往指定的IP和端口号上发送数据即可,但是它并不能保证数据一定能让对方收到。

java.net.DatagramSocketjava.net.DatagramPacket 是UDP编程中使用到的两个类,客户端和服务器都使用这两个类

  • java.net.DatagramSocket 负责接收和发送数据
  • java.net.DatagramPacket 负责封装要发送的数据和接收到的数据

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ServerUDP {
public static void main(String[] args) {
byte[] buf = new byte[1024];;
int port = 9999;
DatagramSocket socket = null;
DatagramPacket packet = null;
try {
//创建socket,并指定监听的端口号
socket = new DatagramSocket(port);
//创建packet,用于接收数据
//指定buf从下标0开始,最多接收length长度的数据
packet = new DatagramPacket(buf,0,buf.length);
System.out.println("服务器启动,等待客户端发送数据过来");
//receive方法会阻塞,等待客户端发送数据过来
//使用packet接收数据,数据存放在packet中的buf数组中
socket.receive(packet);
//把收的数据转为字符串输出,数据从buf的下标0开始,长度为packet.getLength()
System.out.println("服务器接收的数据为:"+new
String(buf,0,packet.getLength()));
} catch (IOException e) {
e.printStackTrace();
}finally {
if(socket!=null){
socket.close();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ClientUDP {
public static void main(String[] args) {
String ip = "127.0.0.1";
int port = 9999;
DatagramSocket socket = null;
DatagramPacket packet = null;
try {
byte[] buf = "hello world 中国".getBytes();
//socket负责发送数据
socket = new DatagramSocket();
//打好数据报,并指定要发生到的ip和端口号
//buf中从0开始,全部数据都发送
packet = new DatagramPacket(buf,
0,buf.length,InetAddress.getByName(ip), port);
//发送数据
socket.send(packet);
System.out.println("客户端发送数据完毕");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(socket!=null){
socket.close();
}
}
}
}

注意:即使不启动服务器,客户端也可以发送数据,因为UDP不提前建立连接,也不保证数据会让对方收到

URI和URL

URI(uniform resource identifier),统一资源标识符,用来唯一的标识一个 资源。

URL(uniform resource locator),统一资源定位符,它是一种具体的URI, 即URL可以用来标识一个资源,而且还指明了如何定位这个资源。

例如,http://127.0.0.1/hello ,这就是一个URL,它不仅标识了一个资源, 还能定位这个资源。

例如,/hello ,这就是一个URI,它只是标识了一个资源。

java.net.URL 可以表示一个URL地址,使用URL对象打开一个连接后,可 以获取这个网络资源返回的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {
try {
URL url = new URL("https://www.jd.com");
//打开这个URL的连接,强制为HttpURL的连接对象
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
//设置请求方式
httpConn.setRequestMethod("GET"); // 默认就是Get
//模拟浏览器发送的请求的情况
httpConn.setRequestProperty("User-Agent",
"Mozilla/5.0 (WindowsNT 10.0; Win64; x64; rv:79.0) Gecko/20100101Firefox/79.0");
//获取这个URL连接的输入流,准备读取数据
InputStream is = httpConn.getInputStream();
//字节流转换为字符流
Reader in = new InputStreamReader(is);
char[] cbuf = new char[1024];
int len = -1;
while ((len = in.read(cbuf)) != -1) {
System.out.print(new String(cbuf, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}

这里读到的信息,和用浏览器这URL获取的内容是一样的,只不过浏览 器能解析这些内容以网页的形式呈现

注意,浏览器发送请求时,请求头中都会携带User-Agent这个字段,例如:

Firefox浏览器中使用F12

❤️❤️❤️忙碌的敲代码也不要忘了浪漫鸭!

三更灯火五更鸡,正是男儿读书时。💪