网络编程

发布于 2026-07-29 09:23 更新于 2026-07-29 09:43 1007 字 6 min read ... 访问量

文章介绍了TCP和UDP两种网络协议的特点与应用场景,指出TCP面向连接、可靠传输,适合对数据完整性要求高的场景;UDP无连接、开销小,适合容忍丢包的实时应用。随后讲解了TCP和UDP的编程模型,包括InetAddress、Socket、ServerSocket、DatagramSocket和DatagramPacket的使用,以及URL编程中如何通过字节流高效下载网络资源。

网络编程

TCP 和 UDP 协议

TCP 协议

TCP 是面向连接、可靠传输的协议。通信前需要建立连接,并通过确认、重传、排序等机制保证数据尽可能完整且有序地到达。

UDP 协议

UDP 是面向数据报的无连接协议。它不会保证数据一定到达,也不会保证到达顺序,但协议开销较小,适合能够容忍少量丢包或由应用层自行保证可靠性的场景。

选择 TCP 还是 UDP,应根据可靠性、时延、吞吐量和业务特征决定,不能只根据网络状况判断。

TCP 编程

InetAddress 类

InetAddress 用于表示 IP 地址及其对应的主机信息。

  • localhost 是指向本机的主机名。
  • 127.0.0.1 是 IPv4 环回地址。

客户端与服务器模型

TCP 通常采用客户端与服务器模型。服务器通过 ServerSocket 监听端口,客户端使用 Socket 主动发起连接。

image-001
image-001
image-002
image-002

TCP 服务器端

public class TestServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(7000);
             Socket socket = serverSocket.accept();
             BufferedReader reader = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()));
             PrintWriter writer = new PrintWriter(
                     socket.getOutputStream(), true)) {

            System.out.println("有客户端连接了服务器");
            System.out.println(reader.readLine());

            writer.println("1 + 1 的结果是 2");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

accept() 会阻塞当前线程,直到有客户端建立连接。连接成功后,服务器通过套接字的输入流接收数据,通过输出流发送数据。

TCP 客户端

public class TestClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 7000);
             PrintWriter writer = new PrintWriter(
                     socket.getOutputStream(), true);
             BufferedReader reader = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()))) {

            writer.println("我是客户端,1 + 1 = ?");
            System.out.println(reader.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端和服务器都可以同时读取、写入数据。实现双向聊天时,通常需要分别处理发送和接收操作,避免某一方持续阻塞导致无法继续通信。

UDP 编程

DatagramSocket

DatagramSocket 用于发送和接收 UDP 数据报,可以理解为程序收发数据报的通信端点。

DatagramPacket

DatagramPacket 表示一个数据报。发送时需要封装数据、目标地址和目标端口;接收时需要预先准备字节数组作为接收缓冲区。

A 端程序

public class ATestUDP {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket(7004)) {
            byte[] sendData = "hello".getBytes(StandardCharsets.UTF_8);
            DatagramPacket sendPacket = new DatagramPacket(
                    sendData,
                    sendData.length,
                    InetAddress.getByName("localhost"),
                    7003
            );
            socket.send(sendPacket);

            byte[] receiveData = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(
                    receiveData,
                    receiveData.length
            );
            socket.receive(receivePacket);

            String message = new String(
                    receivePacket.getData(),
                    0,
                    receivePacket.getLength(),
                    StandardCharsets.UTF_8
            );
            System.out.println(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

B 端程序

public class BTestUDP {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket(7003)) {
            byte[] receiveData = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(
                    receiveData,
                    receiveData.length
            );
            socket.receive(receivePacket);

            String message = new String(
                    receivePacket.getData(),
                    0,
                    receivePacket.getLength(),
                    StandardCharsets.UTF_8
            );
            System.out.println(message);

            byte[] sendData = "我收到了".getBytes(StandardCharsets.UTF_8);
            DatagramPacket sendPacket = new DatagramPacket(
                    sendData,
                    sendData.length,
                    receivePacket.getAddress(),
                    receivePacket.getPort()
            );
            socket.send(sendPacket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

receive() 会阻塞,直到接收到数据报。UDP 每次发送和接收的基本单位都是一个完整的数据报。

URL

URL 是统一资源定位符,用于定位网络上的资源。常见结构可表示为:

协议://主机:端口/路径?参数名=参数值&参数名=参数值

端口、路径和查询参数不一定全部存在。

URL 编程

读取网页内容

public class TestURL {
    public static void main(String[] args) {
        try {
            URL url = new URL("https://www.baidu.com/");
            URLConnection connection = url.openConnection();

            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(
                            connection.getInputStream(),
                            StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

下载网络文件

public class URLDownload {
    public static void main(String[] args) {
        String address = "https://copyright.bdstatic.com/vcg/creative/"
                + "65e11b3d96ac8f9b293b2d3486b7c422.jpg@wm_1,"
                + "k_cGljX2JqaHdh dGVyLmpwZw==".replace(" ", "");

        try {
            URL url = new URL(address);
            URLConnection connection = url.openConnection();

            try (BufferedInputStream input = new BufferedInputStream(
                    connection.getInputStream());
                 BufferedOutputStream output = new BufferedOutputStream(
                         new FileOutputStream("d:/lession/java2601/test/a.jpg"))) {

                byte[] buffer = new byte[8192];
                int length;
                while ((length = input.read(buffer)) != -1) {
                    output.write(buffer, 0, length);
                }
                output.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

下载二进制文件时,应使用字节流,并通过缓冲区批量读写,避免逐字节处理造成不必要的性能损耗。

喜欢的话,留下你的评论吧~

... 访问量
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka