Skip to content
DAILY QUOTE

“ ”

1.1.概述

网络编程可以简单理解为:

text
让不同计算机之间进行数据通信。

生活中的通信方式可以类比为:

  • 打电话:需要先建立连接,接通后才能通话,类似TCP
  • 发短信:发送出去就结束,不保证对方一定马上收到,类似UDP

计算机网络的定义:

计算机网络是指将地理位置不同、具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件和网络通信协议的管理协调下,实现资源共享和信息传递的计算机系统。

网络编程的目的:

text
实现数据传输、信息交流、资源共享。

要实现网络通信,至少要解决两个问题:

  1. 如何准确定位网络上的一台主机,以及主机上的某个程序?
  2. 找到主机和程序之后,如何传输数据?

对应到网络编程中:

text
定位主机:IP地址
定位程序:端口号
传输规则:通信协议,例如TCP、UDP

JavaWeb和网络编程的区别可以粗略理解为:

类型说明
JavaWeb主要是网页开发,常见是B/S架构,也就是浏览器 / 服务器
网络编程更底层,常见是C/S架构,也就是客户端 / 服务端

这里的B/S和C/S:

text
B/S:Browser / Server,浏览器访问服务器,比如网页系统
C/S:Client / Server,客户端访问服务器,比如聊天软件、游戏客户端

1.2.网络通信的要素

要实现网络通信,需要三个基本要素:

  • IP地址
  • 端口号
  • 通信协议

1. 通信双方地址

通信时必须先知道对方在哪里。

例如:

text
192.168.1.1

这是一个局域网IP地址。

IP用来定位网络上的一台主机。

2. 端口号

同一台主机上可能运行很多程序,例如:

text
MySQL
Redis
Tomcat
Nginx
QQ
微信
浏览器

IP只能定位到某台电脑,不能定位到电脑上的具体程序。

所以还需要端口号。

text
IP地址:定位主机
端口号:定位主机上的某个进程或程序

3. 网络通信协议

协议就是通信规则。

比如人和人交流时要使用同一种语言,否则无法理解。

计算机通信也一样,需要遵守协议,例如:

text
TCP
UDP
HTTP
FTP

常见协议:

协议说明
TCP面向连接,可靠传输
UDP无连接,速度快但不保证可靠
HTTP超文本传输协议,网页请求常用
FTP文件传输协议
IP网络互联协议,用于寻址和路由

TCP/IP参考模型:

小结:

  1. 网络编程主要解决两个问题:
    • 如何定位网络上的主机?
    • 找到主机后如何通信?
  2. 网络编程三要素:
    • IP
    • 端口
    • 协议
  3. Java中很多网络相关内容也被封装成了对象,例如:
    • InetAddress
    • InetSocketAddress
    • Socket
    • ServerSocket

1.3.IP

IP地址用于唯一定位网络上的一台计算机。

常见本机地址:

text
127.0.0.1
localhost

它们都表示本机,也就是当前这台电脑。

1. IP地址分类

IP地址主要有两类:

text
IPv4
IPv6
  • IPv4 IPv4是32位地址,通常写成四段数字:
text
192.168.1.1
127.0.0.1

每一段范围是:

text
0 ~ 255

IPv4地址总量大约42亿个,但由于互联网发展很快,IPv4地址已经不够用了。

  • IPv6 IPv6是128位地址,地址空间非常大。

它的出现主要是为了解决IPv4地址不够用的问题。

号称可以为地球上每一粒沙子分配一个ip

2. 公网IP和私网IP

IP还可以分为:

text
公网IP
私网IP
  • 公网IP 公网IP可以在互联网中被访问。

比如云服务器通常会有公网IP。

  • 私网IP 私网IP主要用于局域网内部。

常见私网网段:

text
192.168.x.x
10.x.x.x
172.16.x.x ~ 172.31.x.x

例如:

text
192.168.1.100

通常是家里、学校、公司局域网中的设备地址。

3. 域名

IP地址不好记,所以出现了域名。

例如:

text
www.baidu.com

域名会通过DNS解析成IP地址。(本地电脑递归查询,本地DNS服务器迭代查询)

也就是说:

text
域名是为了方便人记忆,真正通信时还是会解析成IP。

4. Java中的InetAddress

Java使用InetAddress表示IP地址相关信息。

示例代码:

java
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,无法区分具体访问哪个程序,所以需要端口号。

text
IP:定位主机
端口:定位主机上的程序

端口范围:

text
0 ~ 65535

TCP和UDP都有自己的端口范围。

也就是说:

text
TCP 80和UDP 80不冲突
但同一个协议下,同一个端口不能被两个程序同时占用。

1. 端口分类

  • 公有端口:0 ~ 1023 这些端口通常被系统服务或知名协议使用。

常见端口:

端口服务
80HTTP
443HTTPS
21FTP
23Telnet
  • 注册端口:1024 ~ 49151 通常给用户程序或常见软件使用。

常见端口:

端口服务
8080Tomcat
3306MySQL
1521Oracle
6379Redis
9200Elasticsearch
  • 动态 / 私有端口:49152 ~ 65535 一般由系统临时分配给客户端程序使用。

比如你访问网页时,本机浏览器也会临时占用一个高位端口,用来和服务器通信。

2. Windows查看端口命令

查看所有端口:

bash
netstat -tunlp

查看指定端口:

bash
netstat -tunlp | grep "8080"

查看指定进程:

text
ps -ef | grep "进程号"

这里的|是管道符,意思是:

text
把前一个命令的输出结果,交给后一个命令继续筛选。

例如:

bash
netstat -tunlp | grep "3306"

含义是:

text
先查看所有端口,再从结果中筛选出包含3306的行。

3. Java中的InetSocketAddress

InetSocketAddress用来表示:

text
IP + 端口

示例代码:

java
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.通信协议

协议就是双方通信时约定好的规则。

人和人交流要使用同一种语言,计算机之间通信也需要统一规则,例如:

text
数据格式
传输方式
传输速率
错误处理
连接控制

网络通信协议非常复杂,所以通常采用分层设计。

1. TCP/IP协议簇

TCP/IP不是单个协议,而是一组协议。

常见协议:

协议说明
IP网络互联协议,负责寻址和路由
TCPTransmission Control Protocol,传输控制协议
UDPUser Datagram Protocol,用户数据报协议
HTTP超文本传输协议
FTP文件传输协议

分层理解:

层级说明常见内容
应用层面向应用程序HTTP、FTP、DNS、QQ、微信
传输层负责进程间通信TCP、UDP
网络层负责主机寻址和路由IP
数据链路层负责具体物理传输网卡、交换机、光纤、网线

2. TCP和UDP对比

TCP

TCP类似打电话。

特点:

text
面向连接
可靠传输
传输前需要建立连接
传输完成后释放连接
效率相对低

TCP有客户端和服务端的概念。

连接过程常用“三次握手”描述。

三次握手

建立连接至少需要三次通信,目的是确认双方都具备发送和接收能力。

可以简单类比:

text
A:你能听到我吗?
B:我能听到,你能听到我吗?
A:我也能听到,那开始通信。
四次挥手

断开连接时需要双方都确认不再发送数据。

可以简单类比:

text
A:我要断开了。
B:我知道了。
B:我也准备断开了。
A:好的,断开。

UDP

UDP类似发短信。

特点:

text
无连接
不保证可靠
不保证顺序
速度快
开销小

UDP没有严格的客户端和服务端连接关系。

发送方只负责把数据发出去,不管对方是否准备好,也不保证一定收到。

常见场景:

text
视频直播
语音通话
游戏同步
广播通知
DNS查询

这些场景更关注速度和实时性,可以接受少量丢包。

3. TCP和UDP总结

对比项TCPUDP
是否连接面向连接无连接
是否可靠可靠不可靠
传输速度较慢较快
资源开销较大较小
是否保证顺序保证不保证
使用场景文件传输、网页、登录、支付直播、语音、游戏、广播

1.6 TCP

TCP通信在Java中主要通过Socket实现。

常见类:

作用
Socket客户端套接字,用于连接服务端
ServerSocket服务端套接字,用于监听客户端连接

基本流程:

text
服务端创建ServerSocket,监听端口

客户端创建Socket,连接服务端IP和端口

服务端accept() 接收连接,得到Socket

双方通过输入流、输出流传输数据

通信结束后关闭资源

1. ServerSocket

服务端要先创建ServerSocket,并指定监听端口。

java
ServerSocket serverSocket = new ServerSocket(9900);

然后调用:

java
Socket socket = serverSocket.accept();

accept()的作用是等待客户端连接。

注意:

text
accept() 是阻塞方法。

如果没有客户端连接,程序会一直停在这里。

2. Socket

客户端通过Socket连接服务端:

java
Socket socket = new Socket("127.0.0.1", 9900);

含义:

text
连接127.0.0.1这台主机上9900端口对应的服务端程序。

连接成功后,客户端和服务端都会有一个Socket对象。

双方通过Socket获取输入输出流:

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

注意方向:

text
一方的输出流,对应另一方的输入流。

也就是说:

text
客户端OutputStream写出的数据,服务端InputStream可以读到。
服务端OutputStream写出的数据,客户端InputStream可以读到。

3. TCP文件上传案例

案例目标:

text
客户端读取本地图片文件,通过TCP发送给服务端。
服务端接收文件并保存。
服务端接收完成后,给客户端返回确认消息。

通信流程:

text
客户端连接服务端

客户端读取本地文件

客户端通过Socket输出流发送文件数据

客户端shutdownOutput() 通知服务端发送结束

服务端读取Socket输入流

服务端写入本地文件

服务端返回“我已接收”

客户端读取服务端响应

双方关闭资源

1. 服务端代码

java
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();  
    }  
}

服务端重点:

text
1. ServerSocket用来监听端口。
2. accept() 用来等待客户端连接。
3. getInputStream() 读取客户端发来的数据。
4. FileOutputStream把数据写入本地文件。
5. getOutputStream() 给客户端返回响应。

2. 客户端代码

java
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();
    }
}

客户端重点:

text
1. Socket连接服务端IP和端口。
2. FileInputStream读取本地文件。
3. socket.getOutputStream() 把文件发送给服务端。
4. socket.shutdownOutput() 通知服务端发送结束。
5. socket.getInputStream() 接收服务端响应。

3. 为什么要调用shutdownOutput()

客户端发送文件时,服务端使用:

java
while ((len = is.read(buffer)) != -1) {
    fos.write(buffer, 0, len);
}

服务端的read()什么时候返回-1

text
当客户端输出流结束时,服务端才能读到 -1。

如果客户端不调用:

java
socket.shutdownOutput();

服务端可能一直认为客户端还有数据没发完,于是一直阻塞在read()中。

所以:

text
shutdownOutput() 是告诉服务端:我的数据已经发送完毕。

注意:shutdownOutput() 只是关闭输出方向,不是关闭整个Socket。 客户端仍然可以继续读取服务端返回的响应。

1.7.UDP

UDP可以类比成发短信:

text
不需要提前建立连接
只需要知道对方IP和端口
发送方把数据包发出去即可
不保证对方一定收到
不保证顺序

UDP通信使用的核心类:

作用
DatagramSocket用于发送或接收UDP数据包
DatagramPacketUDP数据包,里面包含数据、长度、目标IP、目标端口等信息
InetAddress表示IP地址
InetSocketAddress表示IP + 端口

UDP和TCP最大区别:

text
TCP:先建立连接,再传输数据,可靠但开销较大
UDP:不建立连接,直接发送数据,速度快但不保证可靠

1.7.1 UDP发送消息

  • 发送端

UDP发送端流程:

text
1. 创建DatagramSocket
2. 准备目标IP和端口
3. 把字符串转成byte数组
4. 创建DatagramPacket数据包
5. 调用socket.send(packet) 发送
6. 关闭socket

代码:

java
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接收端流程:
text
1. 创建DatagramSocket,并绑定监听端口
2. 创建byte数组作为缓冲区
3. 创建DatagramPacket接收数据
4. 调用socket.receive(packet) 阻塞等待数据
5. 从packet中取出数据并转换成字符串
6. 关闭socket

代码:

java
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循环发送消息

这一节实现的是:

text
发送方从控制台不断输入内容
输入一行就通过UDP发给接收方
如果输入bye,则结束发送
  • 发送方

流程:

text
1. 创建DatagramSocket,并指定本地发送端口
2. 使用BufferedReader读取控制台输入
3. 将输入内容转成byte数组
4. 封装成DatagramPacket
5. 发送数据包
6. 输入bye时退出循环
7. 关闭socket

代码:

java
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();
    }
}
  • 接收方 流程:
text
1. 创建DatagramSocket,绑定9999端口
2. 循环接收数据包
3. 将数据包内容转成字符串
4. 打印消息
5. 收到bye退出循环
6. 关闭socket

代码:

java
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聊天:

text
学生端可以发送,也可以接收
老师端也可以发送,也可以接收

因为发送和接收都要同时进行,所以需要使用多线程:

text
一个线程负责发送消息
一个线程负责接收消息
  • 发送端线程 发送线程需要知道:
text
自己的发送端口fromPort
对方IP toIP
对方接收端口toPort

代码:

java
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();
        }
    }
}
  • 接收端线程 接收线程需要知道:
text
自己的监听端口port
对方显示名称fromName

代码:

java
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();
        }
    }
}
  • 学生端
text
发送线程:从7754端口发消息给老师的9999端口
接收线程:监听7787端口,接收老师发来的消息

代码:

java
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();
    }
}
  • 老师端
text
发送线程:从7755端口发消息给学生的7787端口
接收线程:监听9999端口,接收学生发来的消息

代码:

java
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();
    }
}
  • 在线咨询流程总结 学生端:
text
发送:7754 → localhost:9999
接收:监听7787

老师端:

text
发送:7755 → localhost:7787
接收:监听9999

整体通信关系:

text
学生发送线程 → 老师接收线程
老师发送线程 → 学生接收线程

1.8 URL

URL全称是:

text
Uniform Resource Locator

中文叫:统一资源定位符

作用:

text
定位互联网上的某一个资源。

例如:

text
https://www.baidu.com/

这个URL可以定位到百度首页资源。

1.8.1 URL的组成

一个常见URL:

text
https://www.baidu.com/index.html?name=tom

可以拆成:

部分说明
https协议
www.baidu.com域名
/index.html资源路径
name=tom请求参数

域名需要通过DNS解析成IP地址后,才能真正访问服务器。

text
域名 → DNS解析 → IP地址 → 访问服务器资源

1.8.2 Java通过URL下载资源

Java中可以使用URLHttpURLConnection访问网络资源。

流程:

text
1. 创建URL对象
2. 通过url.openConnection() 打开连接
3. 获取输入流
4. 创建本地文件输出流
5. 循环读取网络数据并写入本地文件
6. 关闭资源
7. 断开连接

代码:

java
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();
    }
}