《Java 程序设计》第 18 章 - Java 网络编程

发布于:2025-08-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

引言

        在当今的互联网时代,网络编程是软件开发中不可或缺的重要部分。Java 提供了丰富的类库来支持网络编程,使得开发者能够轻松地实现各种网络通信功能。本章将详细介绍 Java 网络编程的核心知识,包括网络基础概念、套接字通信、数据报通信以及 URL 编程等内容,并通过完整的代码示例帮助大家掌握实际应用技能。


18.1 网络概述

        网络编程的本质是实现不同设备之间的数据传输。在学习 Java 网络编程之前,我们需要先了解一些计算机网络的基本概念。

18.1.1 网络分层与协议

        为了实现不同计算机之间的通信,网络通信采用分层模型设计,每一层负责特定的功能,并通过协议规范通信方式。

最著名的网络模型有两种:

  • OSI 七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
  • TCP/IP 四层模型:网络接口层、网络层、传输层、应用层(或分为五层,将网络接口层细分为物理层和数据链路层)

常用协议

  • 网络层:IP 协议(Internet Protocol)
  • 传输层:TCP 协议(Transmission Control Protocol)和 UDP 协议(User Datagram Protocol)
  • 应用层:HTTP、FTP、SMTP 等

18.1.2 客户 / 服务器结构

在网络通信中,最常见的模式是客户 / 服务器(C/S)结构

  • 服务器(Server):提供服务的计算机或程序,被动等待客户端的连接请求
  • 客户端(Client):请求服务的计算机或程序,主动向服务器发起连接请求

B/S 结构(浏览器 / 服务器)是一种特殊的 C/S 结构,客户端是浏览器,通过 HTTP 协议与服务器通信。

18.1.3 IP 地址和域名

  • IP 地址:网络中每个设备的唯一标识,用于设备之间的定位

    • IPv4:32 位,格式如 192.168.1.1
    • IPv6:128 位,格式如 2001:0db8:85a3:0000:0000:8a2e:0370:7334(为解决 IPv4 地址耗尽问题)
    • 本地回环地址:127.0.0.1,通常映射到域名 localhost
  • 域名:为了方便记忆,用字符串标识网络中的设备,如 www.csdn.net

  • DNS(域名系统):负责将域名解析为对应的 IP 地址。

18.1.4 端口号与套接字

 

  • 端口号:标识设备上运行的不同网络程序,范围是 0-65535。

    • 0-1023:知名端口,被系统保留(如 HTTP 的 80,FTP 的 21)
    • 1024-49151:注册端口
    • 49152-65535:动态或私有端口,可用于普通应用程序
  • 套接字(Socket):网络通信的端点,由 IP 地址和端口号组成,用于标识通信的双方

    • 格式:IP地址:端口号(如 192.168.1.100:8080
    • Java 中通过 Socket 类实现套接字功能

18.2 Java 套接字通信

        Java 中基于 TCP 协议的网络通信主要通过套接字(Socket)实现,核心类是 ServerSocket(服务器端)和 Socket(客户端)。

18.2.1 套接字 API

  • ServerSocket:服务器端套接字,用于监听客户端的连接请求

    • 构造方法:ServerSocket(int port) - 绑定到指定端口
    • 核心方法:Socket accept() - 阻塞等待客户端连接,返回与客户端通信的 Socket 对象
  • Socket:客户端套接字,也用于服务器端与客户端通信

    • 构造方法:Socket(String host, int port) - 连接到指定主机的指定端口
    • 核心方法:
      • InputStream getInputStream() - 获取输入流,用于读取数据
      • OutputStream getOutputStream() - 获取输出流,用于发送数据
      • void close() - 关闭套接字
@startuml
class ServerSocket {
  + ServerSocket(int port) throws IOException
  + Socket accept() throws IOException
  + void close() throws IOException
  + int getLocalPort()
}

class Socket {
  + Socket(String host, int port) throws IOException
  + InputStream getInputStream() throws IOException
  + OutputStream getOutputStream() throws IOException
  + void close() throws IOException
  + InetAddress getInetAddress()
  + int getPort()
  + int getLocalPort()
}

ServerSocket "1" -- "*" Socket : 创建
@enduml

18.2.2 简单的客户和服务器程序

下面实现一个简单的 TCP 通信程序:客户端向服务器发送一条消息,服务器接收后回复一条消息。

服务器端代码(TCPServer.java)

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket clientSocket = null;
        
        try {
            // 创建服务器套接字,绑定到8888端口
            serverSocket = new ServerSocket(8888);
            System.out.println("服务器已启动,等待客户端连接...");
            
            // 阻塞等待客户端连接
            clientSocket = serverSocket.accept();
            System.out.println("客户端已连接:" + clientSocket.getInetAddress().getHostAddress());
            
            // 获取输入流和输出流
            BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
            PrintWriter out = new PrintWriter(
                new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true);
            
            // 读取客户端发送的消息
            String clientMsg = in.readLine();
            System.out.println("收到客户端消息:" + clientMsg);
            
            // 向客户端发送响应
            String serverMsg = "Hello Client! 我是服务器";
            out.println(serverMsg);
            System.out.println("已向客户端发送消息:" + serverMsg);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (clientSocket != null) clientSocket.close();
                if (serverSocket != null) serverSocket.close();
                System.out.println("服务器已关闭");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端代码(TCPClient.java)

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) {
        Socket socket = null;
        
        try {
            // 连接到本地服务器的8888端口
            socket = new Socket("localhost", 8888);
            System.out.println("已连接到服务器");
            
            // 获取输入流和输出流
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream(), "UTF-8"));
            PrintWriter out = new PrintWriter(
                new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
            
            // 向服务器发送消息
            String clientMsg = "Hello Server! 我是客户端";
            out.println(clientMsg);
            System.out.println("已向服务器发送消息:" + clientMsg);
            
            // 读取服务器的响应
            String serverMsg = in.readLine();
            System.out.println("收到服务器消息:" + serverMsg);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (socket != null) socket.close();
                System.out.println("客户端已关闭");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行说明

  1. 先运行 TCPServer,服务器启动并等待连接
  2. 再运行 TCPClient,客户端连接服务器并进行通信
  3. 程序执行后,服务器和客户端控制台会分别输出通信内容

18.2.3 服务多个客户

        上面的服务器程序只能处理一个客户端连接。要实现同时服务多个客户端,需要使用多线程主线程负责监听连接,每收到一个连接就创建一个新线程处理与该客户端的通信。

多线程服务器代码(MultiThreadTCPServer.java)

import java.io.*;
import java.net.*;

public class MultiThreadTCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        
        try {
            serverSocket = new ServerSocket(8888);
            System.out.println("多线程服务器已启动,等待客户端连接...");
            
            int clientCount = 0; // 客户端计数器
            
            while (true) { // 循环接受多个客户端连接
                Socket clientSocket = serverSocket.accept();
                clientCount++;
                System.out.println("第" + clientCount + "个客户端已连接:" + 
                                   clientSocket.getInetAddress().getHostAddress());
                
                // 创建新线程处理客户端通信
                new ClientHandler(clientSocket, clientCount).start();
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (serverSocket != null) serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 客户端处理线程
    static class ClientHandler extends Thread {
        private Socket clientSocket;
        private int clientId;
        
        public ClientHandler(Socket socket, int id) {
            this.clientSocket = socket;
            this.clientId = id;
        }
        
        @Override
        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;
            
            try {
                // 获取输入流和输出流
                in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
                out = new PrintWriter(
                    new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true);
                
                String clientMsg;
                // 循环读取客户端消息,直到客户端关闭连接
                while ((clientMsg = in.readLine()) != null) {
                    System.out.println("收到第" + clientId + "个客户端消息:" + clientMsg);
                    
                    // 向客户端发送响应
                    String serverMsg = "服务器已收到你的消息:" + clientMsg;
                    out.println(serverMsg);
                }
                
                System.out.println("第" + clientId + "个客户端已断开连接");
                
            } catch (IOException e) {
                System.out.println("第" + clientId + "个客户端通信异常:" + e.getMessage());
            } finally {
                // 关闭资源
                try {
                    if (in != null) in.close();
                    if (out != null) out.close();
                    if (clientSocket != null) clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端代码:可以使用前面的 TCPClient.java,也可以稍作修改使其能发送多条消息:

import java.io.*;
import java.net.*;
import java.util.Scanner;

public class MultiTCPClient {
    public static void main(String[] args) {
        Socket socket = null;
        Scanner scanner = null;
        
        try {
            socket = new Socket("localhost", 8888);
            System.out.println("已连接到服务器,输入消息发送(输入exit退出):");
            
            // 获取输入流和输出流
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream(), "UTF-8"));
            PrintWriter out = new PrintWriter(
                new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
            
            scanner = new Scanner(System.in);
            String msg;
            
            // 循环输入并发送消息
            while (true) {
                msg = scanner.nextLine();
                out.println(msg);
                
                if ("exit".equals(msg)) {
                    System.out.println("客户端将退出");
                    break;
                }
                
                // 读取服务器响应
                String serverMsg = in.readLine();
                System.out.println("服务器回复:" + serverMsg);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (scanner != null) scanner.close();
                if (socket != null) socket.close();
                System.out.println("客户端已关闭");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行说明

  1. 运行 MultiThreadTCPServer
  2. 可以启动多个 MultiTCPClient 实例,每个客户端都能与服务器独立通信
  3. 在客户端输入消息发送,输入 "exit" 退出

18.3 数据报通信

        UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议,与 TCP 相比,它不保证数据的可靠传输,但具有传输速度快、开销小的特点,适用于对实时性要求高但对可靠性要求不高的场景(如视频聊天、语音通话、游戏等)。

18.3.1 数据报通信概述

UDP 通信特点

  • 无连接:通信前不需要建立连接
  • 不可靠:不保证数据一定到达,也不保证顺序
  • 面向数据报:数据以数据报(Datagram)为单位传输
  • 速度快:协议简单,开销小

UDP 通信流程

  1. 发送方将数据打包成数据报,指定接收方的 IP 和端口
  2. 数据报通过网络发送
  3. 接收方从网络中接收数据报

18.3.2 DatagramSocket 类和 DatagramPacket 类

Java 中使用以下类实现 UDP 通信:

  • DatagramSocket:用于发送和接收数据报的套接字

    • 构造方法:
      • DatagramSocket() - 创建未绑定的套接字
      • DatagramSocket(int port) - 创建绑定到指定端口的套接字
    • 核心方法:
      • void send(DatagramPacket p) - 发送数据报
      • void receive(DatagramPacket p) - 接收数据报(阻塞)
      • void close() - 关闭套接字
  • DatagramPacket:表示数据报

    • 构造方法(接收数据时):DatagramPacket(byte[] buf, int length)
    • 构造方法(发送数据时):DatagramPacket(byte[] buf, int length, InetAddress address, int port)
    • 核心方法:
      • byte[] getData() - 获取数据报中的数据
      • int getLength() - 获取数据长度
      • InetAddress getAddress() - 获取发送方 / 接收方的 IP 地址
      • int getPort() - 获取发送方 / 接收方的端口号
@startuml
class DatagramSocket {
  + DatagramSocket() throws SocketException
  + DatagramSocket(int port) throws SocketException
  + void send(DatagramPacket p) throws IOException
  + void receive(DatagramPacket p) throws IOException
  + void close()
}

class DatagramPacket {
  + DatagramPacket(byte[] buf, int length)
  + DatagramPacket(byte[] buf, int length, InetAddress address, int port)
  + byte[] getData()
  + int getLength()
  + InetAddress getAddress()
  + int getPort()
  + void setData(byte[] buf)
  + void setLength(int length)
}

DatagramSocket "1" -- "*" DatagramPacket : 发送/接收
@enduml

18.3.3 简单的 UDP 通信例子

下面实现一个简单的 UDP 通信程序:客户端向服务器发送消息,服务器接收后回复

UDP 服务器代码(UDPServer.java)

import java.net.*;
import java.nio.charset.StandardCharsets;

public class UDPServer {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        byte[] receiveBuf = new byte[1024]; // 接收缓冲区
        
        try {
            // 创建UDP套接字,绑定到8888端口
            socket = new DatagramSocket(8888);
            System.out.println("UDP服务器已启动,等待消息...");
            
            while (true) { // 循环接收消息
                // 创建接收数据报
                DatagramPacket receivePacket = new DatagramPacket(receiveBuf, receiveBuf.length);
                
                // 接收数据报(阻塞)
                socket.receive(receivePacket);
                
                // 解析接收的数据
                String clientMsg = new String(
                    receivePacket.getData(), 0, receivePacket.getLength(), StandardCharsets.UTF_8);
                InetAddress clientAddr = receivePacket.getAddress();
                int clientPort = receivePacket.getPort();
                System.out.println("收到来自 " + clientAddr.getHostAddress() + ":" + clientPort + 
                                   " 的消息:" + clientMsg);
                
                // 准备回复消息
                String serverMsg = "服务器已收到:" + clientMsg;
                byte[] sendData = serverMsg.getBytes(StandardCharsets.UTF_8);
                
                // 创建发送数据报
                DatagramPacket sendPacket = new DatagramPacket(
                    sendData, sendData.length, clientAddr, clientPort);
                
                // 发送回复
                socket.send(sendPacket);
                System.out.println("已回复消息:" + serverMsg);
                
                // 如果收到"exit",则退出服务器
                if ("exit".equals(clientMsg)) {
                    System.out.println("服务器将关闭");
                    break;
                }
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭套接字
            if (socket != null) {
                socket.close();
            }
        }
    }
}

UDP 客户端代码(UDPClient.java)

import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class UDPClient {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        Scanner scanner = null;
        
        try {
            // 创建UDP套接字(不指定端口,系统会分配一个临时端口)
            socket = new DatagramSocket();
            // 服务器地址和端口
            InetAddress serverAddr = InetAddress.getByName("localhost");
            int serverPort = 8888;
            
            System.out.println("UDP客户端已启动,输入消息发送(输入exit退出):");
            scanner = new Scanner(System.in);
            
            while (true) {
                // 读取用户输入
                String msg = scanner.nextLine();
                
                // 准备发送数据
                byte[] sendData = msg.getBytes(StandardCharsets.UTF_8);
                
                // 创建发送数据报
                DatagramPacket sendPacket = new DatagramPacket(
                    sendData, sendData.length, serverAddr, serverPort);
                
                // 发送数据报
                socket.send(sendPacket);
                System.out.println("已发送消息:" + msg);
                
                // 如果输入"exit",则退出客户端
                if ("exit".equals(msg)) {
                    System.out.println("客户端将关闭");
                    break;
                }
                
                // 接收服务器回复
                byte[] receiveBuf = new byte[1024];
                DatagramPacket receivePacket = new DatagramPacket(receiveBuf, receiveBuf.length);
                socket.receive(receivePacket);
                
                // 解析回复数据
                String serverMsg = new String(
                    receivePacket.getData(), 0, receivePacket.getLength(), StandardCharsets.UTF_8);
                System.out.println("收到服务器回复:" + serverMsg);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (scanner != null) scanner.close();
            if (socket != null) socket.close();
        }
    }
}

运行说明

  1. 先运行 UDPServer
  2. 再运行 UDPClient
  3. 在客户端输入消息,服务器会收到并回复
  4. 输入 "exit" 可退出程序

18.4 URL 类编程

        URL(Uniform Resource Locator,统一资源定位符)用于标识互联网上的资源,如网页、图片、文件等。Java 提供了 URL 和 URLConnection 类来方便地访问 URL 指向的资源。

18.4.1 理解 HTTP

        HTTP(HyperText Transfer Protocol,超文本传输协议)是一种基于 TCP 的应用层协议,用于在客户端和服务器之间传输超文本数据(如 HTML)。

HTTP 通信流程

  1. 客户端(如浏览器)向服务器发送 HTTP 请求
  2. 服务器处理请求,返回 HTTP 响应
  3. 客户端解析响应内容并展示

HTTP 请求方法

  • GET:请求获取资源
  • POST:向服务器提交数据
  • PUT:更新资源
  • DELETE:删除资源

HTTP 响应状态码

  • 200:成功
  • 404:资源未找到
  • 500:服务器内部错误

18.4.2 URL 和 URL 类

URL 的格式协议://主机名:端口/路径?查询参数#片段
例如:https://www.csdn.net:443/article/list/1?type=1#content

Java 中的 java.net.URL 类用于表示 URL,并提供了访问 URL 资源的方法:

  • 构造方法:URL(String spec) - 根据字符串创建 URL 对象
  • 常用方法:
    • String getProtocol() - 获取协议
    • String getHost() - 获取主机名
    • int getPort() - 获取端口号
    • String getPath() - 获取路径
    • InputStream openStream() - 获取 URL 资源的输入流

18.4.3 URLConnection 类

   URLConnection 是一个抽象类,用于表示与 URL 所指向资源的连接。它比 URL 类提供了更多的功能,如设置请求头、获取响应头、处理表单提交等。

常用方法:

  • static URLConnection openConnection() - 获取 URLConnection 对象
  • void setRequestProperty(String key, String value) - 设置请求头
  • Map<String, List<String>> getHeaderFields() - 获取响应头
  • InputStream getInputStream() - 获取输入流,用于读取资源
  • OutputStream getOutputStream() - 获取输出流,用于发送数据(如 POST 请求)
  • void connect() - 建立连接

使用 URL 读取网页内容的示例(URLReader.java)

import java.io.*;
import java.net.*;

public class URLReader {
    public static void main(String[] args) {
        // 要访问的URL
        String urlStr = "https://www.baidu.com";
        BufferedReader reader = null;
        
        try {
            // 创建URL对象
            URL url = new URL(urlStr);
            System.out.println("协议:" + url.getProtocol());
            System.out.println("主机:" + url.getHost());
            System.out.println("端口:" + url.getPort()); // -1表示使用协议默认端口
            System.out.println("路径:" + url.getPath());
            
            // 打开URL连接,获取输入流
            reader = new BufferedReader(
                new InputStreamReader(url.openStream(), "UTF-8"));
            
            // 读取内容并输出
            String line;
            System.out.println("\n网页内容:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (MalformedURLException e) {
            System.out.println("URL格式错误:" + e.getMessage());
        } catch (IOException e) {
            System.out.println("读取失败:" + e.getMessage());
        } finally {
            // 关闭资源
            try {
                if (reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用 URLConnection 发送 POST 请求的示例(URLConnectionPost.java)

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class URLConnectionPost {
    public static void main(String[] args) {
        // 要提交的URL(这里使用一个测试接口)
        String urlStr = "https://httpbin.org/post";
        HttpURLConnection connection = null;
        BufferedReader reader = null;
        
        try {
            // 创建URL对象
            URL url = new URL(urlStr);
            
            // 打开连接
            connection = (HttpURLConnection) url.openConnection();
            
            // 设置请求方法为POST
            connection.setRequestMethod("POST");
            
            // 设置允许输出(发送数据)
            connection.setDoOutput(true);
            
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0");
            
            // 准备要提交的表单数据
            Map<String, String> params = new HashMap<>();
            params.put("name", "张三");
            params.put("age", "25");
            params.put("city", "北京");
            
            // 构建请求参数字符串
            StringBuilder postData = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (postData.length() > 0) {
                    postData.append('&');
                }
                // 对参数进行URL编码
                postData.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()));
                postData.append('=');
                postData.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()));
            }
            byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8);
            
            // 设置请求内容长度
            connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
            
            // 获取输出流,发送数据
            try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) {
                out.write(postDataBytes);
            }
            
            // 获取响应状态码
            int responseCode = connection.getResponseCode();
            System.out.println("响应状态码:" + responseCode);
            
            // 读取响应内容
            reader = new BufferedReader(
                new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
            
            String line;
            System.out.println("响应内容:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (MalformedURLException e) {
            System.out.println("URL格式错误:" + e.getMessage());
        } catch (IOException e) {
            System.out.println("请求失败:" + e.getMessage());
        } finally {
            // 关闭资源
            try {
                if (reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

运行说明

  • URLReader 会读取指定 URL 的内容并输出到控制台
  • URLConnectionPost 会向测试接口发送 POST 请求,并输出响应结果

注意:访问某些网站可能会被拒绝(如设置了反爬机制),可以尝试修改 User-Agent 等请求头信息模拟浏览器行为。


18.5 小结

本章主要介绍了 Java 网络编程的核心内容,包括:

  1. 网络基础概念:网络分层模型、C/S 结构、IP 地址、域名、端口号和套接字
  2. TCP 套接字通信:使用 ServerSocket 和 Socket 类实现可靠的面向连接的通信,以及多线程服务器的实现
  3. UDP 数据报通信:使用 DatagramSocket 和 DatagramPacket 类实现无连接的不可靠通信
  4. URL 编程:使用 URL 和 URLConnection 类访问互联网资源,包括发送 GET 和 POST 请求

        Java 网络编程是 Java 编程中的重要组成部分,掌握这些知识可以帮助我们开发各种网络应用,如客户端 / 服务器程序、网络爬虫、API 调用等。


编程练习

  1. 练习 1:实现文件传输

    • 编写一个 TCP 服务器和客户端,实现文件上传功能(客户端将本地文件发送到服务器,服务器保存文件)
  2. 练习 2:UDP 广播程序

    • 编写一个 UDP 程序,实现局域网内的广播功能(一个客户端发送消息,其他所有客户端都能收到)
  3. 练习 3:简易网页爬虫

    • 使用 URLConnection 编写一个简单的网页爬虫,爬取指定网页中的所有链接(<a> 标签的 href 属性)
  4. 练习 4:多线程聊天程序

    • 基于多线程 TCP 通信,实现一个简易的聊天程序,支持多个客户端之间的群聊功能

        通过这些练习,可以加深对 Java 网络编程的理解和应用能力。在实际开发中,还可以结合线程池、NIO 等技术进一步优化网络程序的性能。