1.1.概述
网络编程可以简单理解为:
让不同计算机之间进行数据通信。生活中的通信方式可以类比为:
- 打电话:需要先建立连接,接通后才能通话,类似TCP
- 发短信:发送出去就结束,不保证对方一定马上收到,类似UDP
计算机网络的定义:
计算机网络是指将地理位置不同、具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件和网络通信协议的管理协调下,实现资源共享和信息传递的计算机系统。
网络编程的目的:
实现数据传输、信息交流、资源共享。要实现网络通信,至少要解决两个问题:
- 如何准确定位网络上的一台主机,以及主机上的某个程序?
- 找到主机和程序之后,如何传输数据?
对应到网络编程中:
定位主机:IP地址
定位程序:端口号
传输规则:通信协议,例如TCP、UDPJavaWeb和网络编程的区别可以粗略理解为:
| 类型 | 说明 |
|---|---|
| JavaWeb | 主要是网页开发,常见是B/S架构,也就是浏览器 / 服务器 |
| 网络编程 | 更底层,常见是C/S架构,也就是客户端 / 服务端 |
这里的B/S和C/S:
B/S:Browser / Server,浏览器访问服务器,比如网页系统
C/S:Client / Server,客户端访问服务器,比如聊天软件、游戏客户端1.2.网络通信的要素
要实现网络通信,需要三个基本要素:
- IP地址
- 端口号
- 通信协议
1. 通信双方地址
通信时必须先知道对方在哪里。
例如:
192.168.1.1这是一个局域网IP地址。
IP用来定位网络上的一台主机。
2. 端口号
同一台主机上可能运行很多程序,例如:
MySQL
Redis
Tomcat
Nginx
QQ
微信
浏览器IP只能定位到某台电脑,不能定位到电脑上的具体程序。
所以还需要端口号。
IP地址:定位主机
端口号:定位主机上的某个进程或程序3. 网络通信协议
协议就是通信规则。
比如人和人交流时要使用同一种语言,否则无法理解。
计算机通信也一样,需要遵守协议,例如:
TCP
UDP
HTTP
FTP常见协议:
| 协议 | 说明 |
|---|---|
| TCP | 面向连接,可靠传输 |
| UDP | 无连接,速度快但不保证可靠 |
| HTTP | 超文本传输协议,网页请求常用 |
| FTP | 文件传输协议 |
| IP | 网络互联协议,用于寻址和路由 |
TCP/IP参考模型: 
小结:
- 网络编程主要解决两个问题:
- 如何定位网络上的主机?
- 找到主机后如何通信?
- 网络编程三要素:
- IP
- 端口
- 协议
- Java中很多网络相关内容也被封装成了对象,例如:
InetAddressInetSocketAddressSocketServerSocket
1.3.IP
IP地址用于唯一定位网络上的一台计算机。
常见本机地址:
127.0.0.1
localhost它们都表示本机,也就是当前这台电脑。
1. IP地址分类
IP地址主要有两类:
IPv4
IPv6- IPv4 IPv4是32位地址,通常写成四段数字:
192.168.1.1
127.0.0.1每一段范围是:
0 ~ 255IPv4地址总量大约42亿个,但由于互联网发展很快,IPv4地址已经不够用了。
- IPv6 IPv6是128位地址,地址空间非常大。
它的出现主要是为了解决IPv4地址不够用的问题。
号称可以为地球上每一粒沙子分配一个ip
2. 公网IP和私网IP
IP还可以分为:
公网IP
私网IP- 公网IP 公网IP可以在互联网中被访问。
比如云服务器通常会有公网IP。
- 私网IP 私网IP主要用于局域网内部。
常见私网网段:
192.168.x.x
10.x.x.x
172.16.x.x ~ 172.31.x.x例如:
192.168.1.100通常是家里、学校、公司局域网中的设备地址。
3. 域名
IP地址不好记,所以出现了域名。
例如:
www.baidu.com域名会通过DNS解析成IP地址。(本地电脑递归查询,本地DNS服务器迭代查询)
也就是说:
域名是为了方便人记忆,真正通信时还是会解析成IP。4. Java中的InetAddress
Java使用InetAddress表示IP地址相关信息。
示例代码:
package lesson1;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class TestInetAddress {
public static void main(String[] args){
try {
//查询网站IP地址
InetAddress address=InetAddress.getByName("localhost");
System.out.println(address);
//常用方法:
System.out.println(address.getHostAddress());//IP
System.out.println(address.getHostName());//名字
System.out.println(address.getCanonicalHostName());//规范的名字
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}常用方法:
| 方法 | 说明 |
|---|---|
getByName() | 根据主机名、域名或IP获取InetAddress对象 |
getHostAddress() | 获取IP地址 |
getHostName() | 获取主机名 |
getCanonicalHostName() | 获取规范主机名 |
1.4.端口port
端口用于表示计算机上的某个程序或进程。
同一台电脑上可以运行很多程序,如果只有IP,无法区分具体访问哪个程序,所以需要端口号。
IP:定位主机
端口:定位主机上的程序端口范围:
0 ~ 65535TCP和UDP都有自己的端口范围。
也就是说:
TCP 80和UDP 80不冲突
但同一个协议下,同一个端口不能被两个程序同时占用。1. 端口分类
- 公有端口:0 ~ 1023 这些端口通常被系统服务或知名协议使用。
常见端口:
| 端口 | 服务 |
|---|---|
| 80 | HTTP |
| 443 | HTTPS |
| 21 | FTP |
| 23 | Telnet |
- 注册端口:1024 ~ 49151 通常给用户程序或常见软件使用。
常见端口:
| 端口 | 服务 |
|---|---|
| 8080 | Tomcat |
| 3306 | MySQL |
| 1521 | Oracle |
| 6379 | Redis |
| 9200 | Elasticsearch |
- 动态 / 私有端口:49152 ~ 65535 一般由系统临时分配给客户端程序使用。
比如你访问网页时,本机浏览器也会临时占用一个高位端口,用来和服务器通信。
2. Windows查看端口命令
查看所有端口:
netstat -tunlp查看指定端口:
netstat -tunlp | grep "8080"查看指定进程:
ps -ef | grep "进程号"这里的|是管道符,意思是:
把前一个命令的输出结果,交给后一个命令继续筛选。例如:
netstat -tunlp | grep "3306"含义是:
先查看所有端口,再从结果中筛选出包含3306的行。3. Java中的InetSocketAddress
InetSocketAddress用来表示:
IP + 端口示例代码:
package lesson1;
import java.net.InetSocketAddress;
public class TestInetSocketAddress {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress1=new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress inetSocketAddress2=new InetSocketAddress("localhost",8080);
System.out.println(inetSocketAddress1);
System.out.println(inetSocketAddress2);
System.out.println(inetSocketAddress2.getAddress());
System.out.println(inetSocketAddress2.getHostName());//主机名
System.out.println(inetSocketAddress2.getPort());//端口
}
}常用方法:
| 方法 | 说明 |
|---|---|
getAddress() | 获取InetAddress地址对象 |
getHostName() | 获取主机名 |
getPort() | 获取端口号 |
1.5.通信协议
协议就是双方通信时约定好的规则。
人和人交流要使用同一种语言,计算机之间通信也需要统一规则,例如:
数据格式
传输方式
传输速率
错误处理
连接控制网络通信协议非常复杂,所以通常采用分层设计。
1. TCP/IP协议簇
TCP/IP不是单个协议,而是一组协议。
常见协议:
| 协议 | 说明 |
|---|---|
| IP | 网络互联协议,负责寻址和路由 |
| TCP | Transmission Control Protocol,传输控制协议 |
| UDP | User Datagram Protocol,用户数据报协议 |
| HTTP | 超文本传输协议 |
| FTP | 文件传输协议 |
分层理解:
| 层级 | 说明 | 常见内容 |
|---|---|---|
| 应用层 | 面向应用程序 | HTTP、FTP、DNS、QQ、微信 |
| 传输层 | 负责进程间通信 | TCP、UDP |
| 网络层 | 负责主机寻址和路由 | IP |
| 数据链路层 | 负责具体物理传输 | 网卡、交换机、光纤、网线 |
2. TCP和UDP对比
TCP
TCP类似打电话。
特点:
面向连接
可靠传输
传输前需要建立连接
传输完成后释放连接
效率相对低TCP有客户端和服务端的概念。
连接过程常用“三次握手”描述。
三次握手
建立连接至少需要三次通信,目的是确认双方都具备发送和接收能力。
可以简单类比:
A:你能听到我吗?
B:我能听到,你能听到我吗?
A:我也能听到,那开始通信。四次挥手
断开连接时需要双方都确认不再发送数据。
可以简单类比:
A:我要断开了。
B:我知道了。
B:我也准备断开了。
A:好的,断开。UDP
UDP类似发短信。
特点:
无连接
不保证可靠
不保证顺序
速度快
开销小UDP没有严格的客户端和服务端连接关系。
发送方只负责把数据发出去,不管对方是否准备好,也不保证一定收到。
常见场景:
视频直播
语音通话
游戏同步
广播通知
DNS查询这些场景更关注速度和实时性,可以接受少量丢包。
3. TCP和UDP总结
| 对比项 | TCP | UDP |
|---|---|---|
| 是否连接 | 面向连接 | 无连接 |
| 是否可靠 | 可靠 | 不可靠 |
| 传输速度 | 较慢 | 较快 |
| 资源开销 | 较大 | 较小 |
| 是否保证顺序 | 保证 | 不保证 |
| 使用场景 | 文件传输、网页、登录、支付 | 直播、语音、游戏、广播 |
1.6 TCP
TCP通信在Java中主要通过Socket实现。
常见类:
| 类 | 作用 |
|---|---|
Socket | 客户端套接字,用于连接服务端 |
ServerSocket | 服务端套接字,用于监听客户端连接 |
基本流程:
服务端创建ServerSocket,监听端口
↓
客户端创建Socket,连接服务端IP和端口
↓
服务端accept() 接收连接,得到Socket
↓
双方通过输入流、输出流传输数据
↓
通信结束后关闭资源1. ServerSocket
服务端要先创建ServerSocket,并指定监听端口。
ServerSocket serverSocket = new ServerSocket(9900);然后调用:
Socket socket = serverSocket.accept();accept()的作用是等待客户端连接。
注意:
accept() 是阻塞方法。如果没有客户端连接,程序会一直停在这里。
2. Socket
客户端通过Socket连接服务端:
Socket socket = new Socket("127.0.0.1", 9900);含义:
连接127.0.0.1这台主机上9900端口对应的服务端程序。连接成功后,客户端和服务端都会有一个Socket对象。
双方通过Socket获取输入输出流:
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();注意方向:
一方的输出流,对应另一方的输入流。也就是说:
客户端OutputStream写出的数据,服务端InputStream可以读到。
服务端OutputStream写出的数据,客户端InputStream可以读到。3. TCP文件上传案例
案例目标:
客户端读取本地图片文件,通过TCP发送给服务端。
服务端接收文件并保存。
服务端接收完成后,给客户端返回确认消息。通信流程:
客户端连接服务端
↓
客户端读取本地文件
↓
客户端通过Socket输出流发送文件数据
↓
客户端shutdownOutput() 通知服务端发送结束
↓
服务端读取Socket输入流
↓
服务端写入本地文件
↓
服务端返回“我已接收”
↓
客户端读取服务端响应
↓
双方关闭资源1. 服务端代码
package lesson1;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPserver {
public static void main(String[] args) throws IOException {
//1.建立连接,创建服务
ServerSocket serverSocket=new ServerSocket(9900);
//2.监听客户端连接,accept 是阻塞方法
Socket socket=serverSocket.accept();
//3.获取客户端发送的数据输入流
InputStream is=socket.getInputStream();
//4.创建文件输出流,把收到的数据保存为文件
FileOutputStream fos=new FileOutputStream(new File("Test.png"));
//5.读取客户端发送的文件数据,并写入本地文件
byte[] bytes=new byte[1024];
int len;
//5.1.从客户端网络输入流里读取数据,放进 bytes 数组里
while((len=is.read(bytes))!=-1){
//5.2.把 bytes 数组里从 0 开始的 len 个字节写入 Test.png fos.write(bytes,0,len);
}
//6.给客户端返回确认消息
OutputStream os=socket.getOutputStream();
os.write("我已接受".getBytes());
//7.关闭资源
os.close();
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}服务端重点:
1. ServerSocket用来监听端口。
2. accept() 用来等待客户端连接。
3. getInputStream() 读取客户端发来的数据。
4. FileOutputStream把数据写入本地文件。
5. getOutputStream() 给客户端返回响应。2. 客户端代码
package lesson1;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
//1.创建Socket,连接服务端
Socket socket=new Socket(InetAddress.getByName("127.0.0.1"),9900);
//2.获取输出流,准备向服务端发送数据
OutputStream os=socket.getOutputStream();
//3.创建文件输入流,读取本地文件
FileInputStream fis=new FileInputStream(
new File("Snipaste_2024-03-18_18-50-07.png")
);
//4.读取文件数据,并通过Socket输出流发送给服务端
byte[] buffer=new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
//5.通知服务端:客户端数据已经发送完毕
socket.shutdownOutput();
//6.接收服务端返回的确认消息
InputStream is = socket.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while ((len2 = is.read(buffer2)) != -1) {
bos.write(buffer2, 0, len2);
}
System.out.println(bos.toString());
//7.关闭资源
bos.close();
is.close();
fis.close();
os.close();
socket.close();
}
}客户端重点:
1. Socket连接服务端IP和端口。
2. FileInputStream读取本地文件。
3. socket.getOutputStream() 把文件发送给服务端。
4. socket.shutdownOutput() 通知服务端发送结束。
5. socket.getInputStream() 接收服务端响应。3. 为什么要调用shutdownOutput()
客户端发送文件时,服务端使用:
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}服务端的read()什么时候返回-1?
当客户端输出流结束时,服务端才能读到 -1。如果客户端不调用:
socket.shutdownOutput();服务端可能一直认为客户端还有数据没发完,于是一直阻塞在read()中。
所以:
shutdownOutput() 是告诉服务端:我的数据已经发送完毕。注意:shutdownOutput() 只是关闭输出方向,不是关闭整个Socket。 客户端仍然可以继续读取服务端返回的响应。
1.7.UDP
UDP可以类比成发短信:
不需要提前建立连接
只需要知道对方IP和端口
发送方把数据包发出去即可
不保证对方一定收到
不保证顺序UDP通信使用的核心类:
| 类 | 作用 |
|---|---|
DatagramSocket | 用于发送或接收UDP数据包 |
DatagramPacket | UDP数据包,里面包含数据、长度、目标IP、目标端口等信息 |
InetAddress | 表示IP地址 |
InetSocketAddress | 表示IP + 端口 |
UDP和TCP最大区别:
TCP:先建立连接,再传输数据,可靠但开销较大
UDP:不建立连接,直接发送数据,速度快但不保证可靠1.7.1 UDP发送消息
- 发送端
UDP发送端流程:
1. 创建DatagramSocket
2. 准备目标IP和端口
3. 把字符串转成byte数组
4. 创建DatagramPacket数据包
5. 调用socket.send(packet) 发送
6. 关闭socket代码:
package lesson1;
import java.io.IOException;
import java.net.*;
public class UdpTest {
public static void main(String[] args) throws IOException {
//1.创建UDP Socket
DatagramSocket socket=new DatagramSocket();
//2.准备目标地址和端口
InetAddress address=InetAddress.getByName("localhost");
int port=9999;
//3.准备数据
byte[] data="你好".getBytes();
//4.创建数据包:数据、起始位置、长度、目标地址、目标端口
DatagramPacket packet=new DatagramPacket(
data,
0,
data.length,
address,
port);
//5.发送数据包
socket.send(packet);
//6.关闭资源
socket.close();
}
}UDP发送消息时,不需要和接收方建立连接,只要知道对方IP和端口即可。
- 接收端 UDP接收端流程:
1. 创建DatagramSocket,并绑定监听端口
2. 创建byte数组作为缓冲区
3. 创建DatagramPacket接收数据
4. 调用socket.receive(packet) 阻塞等待数据
5. 从packet中取出数据并转换成字符串
6. 关闭socket代码:
package lesson1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpTest2 {
public static void main(String[] args) throws Exception {
//1. 开放端口,监听9999 DatagramSocket socket = new DatagramSocket(9999);
while (true) {
//2. 创建缓冲区
byte[] buffer = new byte[1024];
//3. 创建数据包,用来接收数据
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//4. 阻塞接收数据包
socket.receive(packet);
//5. 取出实际接收到的数据
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println(msg);
//6. 收到bye时退出
if ("bye".equals(msg)) { break; }
}
//7. 关闭资源
socket.close();
}
}UDP接收端需要绑定端口,receive()是阻塞方法,会一直等到有数据包到来。
1.7.2循环发送消息
这一节实现的是:
发送方从控制台不断输入内容
输入一行就通过UDP发给接收方
如果输入bye,则结束发送- 发送方
流程:
1. 创建DatagramSocket,并指定本地发送端口
2. 使用BufferedReader读取控制台输入
3. 将输入内容转成byte数组
4. 封装成DatagramPacket
5. 发送数据包
6. 输入bye时退出循环
7. 关闭socket代码:
package lesson1;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpSend {
public static void main(String[] args) throws Exception {
// 1. 创建UDP Socket,绑定本地端口8888
DatagramSocket socket = new DatagramSocket(8888);
// 2. 控制台输入流,放在循环外即可
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
// 3. 读取控制台输入的一行
String msg = reader.readLine();
// 4. 转成字节数组(网络传输底层传输“字节”)
byte[] data = msg.getBytes();
// 5. 目标地址和端口
InetAddress address = InetAddress.getByName("localhost");
int port = 9999;
// 6. 封装数据包
DatagramPacket packet = new DatagramPacket(
data,
0,
data.length,
address,
port
);
// 7. 发送数据包
socket.send(packet);
// 8. 输入bye结束
if ("bye".equals(msg)) {
break;
}
}
// 9. 关闭资源
socket.close();
}
}- 接收方 流程:
1. 创建DatagramSocket,绑定9999端口
2. 循环接收数据包
3. 将数据包内容转成字符串
4. 打印消息
5. 收到bye退出循环
6. 关闭socket代码:
package lesson1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpReceive {
public static void main(String[] args) throws Exception {
// 1. 绑定端口9999
DatagramSocket socket = new DatagramSocket(9999);
while (true) {
// 2. 每次循环重新创建缓冲区
byte[] buffer = new byte[1024];
// 3. 准备接收数据包
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
// 4. 阻塞接收
socket.receive(packet);
// 5. 获取实际消息内容
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println(msg);
// 6. 收到bye退出
if ("bye".equals(msg)) {
break;
}
}
// 7. 关闭资源
socket.close();
}
}1.7.3在线咨询:两个人都可以发送
这一节实现的是简单UDP聊天:
学生端可以发送,也可以接收
老师端也可以发送,也可以接收因为发送和接收都要同时进行,所以需要使用多线程:
一个线程负责发送消息
一个线程负责接收消息- 发送端线程 发送线程需要知道:
自己的发送端口fromPort
对方IP toIP
对方接收端口toPort代码:
package UDP.Run;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Send implements Runnable {
private DatagramSocket socket;
private int fromPort;
private String toIP;
private int toPort;
public Send(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
// 绑定自己的发送端口
this.socket = new DatagramSocket(fromPort);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
// 1. 读取控制台输入
String msg = reader.readLine();
// 2. 字符串转字节数组
byte[] data = msg.getBytes();
// 3. 封装数据包,指定对方IP和端口
DatagramPacket packet = new DatagramPacket(
data,
0,
data.length,
new InetSocketAddress(this.toIP, this.toPort)
);
// 4. 发送数据
socket.send(packet);
// 5. 输入bye结束发送
if ("bye".equals(msg)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
socket.close();
}
}
}- 接收端线程 接收线程需要知道:
自己的监听端口port
对方显示名称fromName代码:
package UDP.Run;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Receive implements Runnable {
private DatagramSocket socket;
private String fromName;
private int port;
public Receive(String fromName, int port) {
this.fromName = fromName;
this.port = port;
try {
// 绑定自己的接收端口
this.socket = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
// 1. 每次循环新建缓冲区
byte[] buffer = new byte[1024];
// 2. 准备接收数据包
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
// 3. 阻塞接收
socket.receive(packet);
// 4. 获取实际消息
String msg = new String(packet.getData(), 0, packet.getLength());
// 5. 打印消息
System.out.println(fromName + ":" + msg);
// 6. 收到bye结束接收
if ("bye".equals(msg)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
socket.close();
}
}
}- 学生端
发送线程:从7754端口发消息给老师的9999端口
接收线程:监听7787端口,接收老师发来的消息代码:
package UDP.Run;
public class Student {
public static void main(String[] args) {
// 学生发送给老师
new Thread(new Send(7754, "localhost", 9999)).start();
// 学生接收老师消息
new Thread(new Receive("老师", 7787)).start();
}
}- 老师端
发送线程:从7755端口发消息给学生的7787端口
接收线程:监听9999端口,接收学生发来的消息代码:
package UDP.Run;
public class Teacher {
public static void main(String[] args) {
// 老师发送给学生
new Thread(new Send(7755, "localhost", 7787)).start();
// 老师接收学生消息
new Thread(new Receive("学生", 9999)).start();
}
}- 在线咨询流程总结 学生端:
发送:7754 → localhost:9999
接收:监听7787老师端:
发送:7755 → localhost:7787
接收:监听9999整体通信关系:
学生发送线程 → 老师接收线程
老师发送线程 → 学生接收线程1.8 URL
URL全称是:
Uniform Resource Locator中文叫:统一资源定位符
作用:
定位互联网上的某一个资源。例如:
https://www.baidu.com/这个URL可以定位到百度首页资源。
1.8.1 URL的组成
一个常见URL:
https://www.baidu.com/index.html?name=tom可以拆成:
| 部分 | 说明 |
|---|---|
https | 协议 |
www.baidu.com | 域名 |
/index.html | 资源路径 |
name=tom | 请求参数 |
域名需要通过DNS解析成IP地址后,才能真正访问服务器。
域名 → DNS解析 → IP地址 → 访问服务器资源1.8.2 Java通过URL下载资源
Java中可以使用URL和HttpURLConnection访问网络资源。
流程:
1. 创建URL对象
2. 通过url.openConnection() 打开连接
3. 获取输入流
4. 创建本地文件输出流
5. 循环读取网络数据并写入本地文件
6. 关闭资源
7. 断开连接代码:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class UrlDemo {
public static void main(String[] args) throws IOException {
// 1. 创建URL对象,定位网络资源
URL url = new URL("https://example.com/test.m4a");
// 2. 打开HTTP连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 3. 获取网络输入流
InputStream in = connection.getInputStream();
// 4. 创建本地文件输出流
FileOutputStream out = new FileOutputStream("d.m4a");
// 5. 边读边写,下载文件
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
// 6. 关闭资源
out.close();
in.close();
// 7. 断开连接
connection.disconnect();
}
}