28 网络编程——Socket、TCP/UDP与HttpClient

目录

  • [🌐 28 网络编程------Socket、TCP/UDP与HttpClient](#🌐 28 网络编程——Socket、TCP/UDP与HttpClient)
    • 一、网络编程基础
      • [1.1 网络通信三要素](#1.1 网络通信三要素)
      • [1.2 TCP与UDP对比](#1.2 TCP与UDP对比)
      • [1.3 InetAddress](#1.3 InetAddress)
    • 二、TCP编程
      • [2.1 TCP通信模型](#2.1 TCP通信模型)
      • [2.2 TCP服务端](#2.2 TCP服务端)
      • [2.3 TCP客户端](#2.3 TCP客户端)
      • [2.4 文件传输示例](#2.4 文件传输示例)
    • 三、UDP编程
      • [3.1 UDP发送端](#3.1 UDP发送端)
      • [3.2 UDP接收端](#3.2 UDP接收端)
    • [四、HttpClient(Java 11+)](#四、HttpClient(Java 11+))
      • [4.1 同步请求](#4.1 同步请求)
      • [4.2 异步请求](#4.2 异步请求)
      • [4.3 HttpClient vs 传统HttpURLConnection](#4.3 HttpClient vs 传统HttpURLConnection)
    • 五、WebSocket基础
      • [5.1 WebSocket简介](#5.1 WebSocket简介)
      • [5.2 Java WebSocket客户端(Java 11+)](#5.2 Java WebSocket客户端(Java 11+))
    • 六、实战:简易聊天室
    • 七、常见面试题解析
    • 八、总结与下篇预告
      • 本篇要点
      • [📢 下篇预告](#📢 下篇预告)
      • [💬 互动话题](#💬 互动话题)

🌐 28 网络编程------Socket、TCP/UDP与HttpClient

更新日期 :2026年5月 | Java入门到精通系列 · 第四阶段·高级特性

© 版权声明:本文为原创技术文章,转载请联系作者并注明出处。



一、网络编程基础

1.1 网络通信三要素

要素 说明 示例
IP地址 设备的网络标识 192.168.1.100
端口号 应用程序的标识(0-65535) 8080
协议 通信规则 TCP、UDP、HTTP

1.2 TCP与UDP对比

特性 TCP UDP
连接方式 面向连接(三次握手) 无连接
可靠性 可靠,保证数据完整 不可靠,可能丢包
传输方式 字节流 数据报
速度 较慢 较快
适用场景 文件传输、网页、邮件 视频直播、游戏、DNS
Java实现 Socket / ServerSocket DatagramSocket / DatagramPacket

1.3 InetAddress

java 复制代码
import java.net.InetAddress;

public class InetAddressDemo {
    public static void main(String[] args) throws Exception {
        // 获取本机地址
        InetAddress local = InetAddress.getLocalHost();
        System.out.println("本机IP:" + local.getHostAddress());
        System.out.println("本机名:" + local.getHostName());

        // 根据域名获取地址
        InetAddress baidu = InetAddress.getByName("www.baidu.com");
        System.out.println("百度IP:" + baidu.getHostAddress());

        // 判断是否可达
        System.out.println("百度是否可达:" + baidu.isReachable(3000));
    }
}

二、TCP编程

2.1 TCP通信模型

复制代码
服务端(ServerSocket)              客户端(Socket)
    │                                  │
    │  accept()等待连接               │  connect()发起连接
    │<─────── 三次握手 ──────────────>│
    │                                  │
    │  Socket(连接)                  │  Socket(连接)
    │                                  │
    │  getInputStream()               │  getOutputStream()
    │  读取客户端数据                   │  发送数据
    │                                  │
    │  getOutputStream()              │  getInputStream()
    │  发送响应数据                     │  读取响应
    │                                  │
    │  close()                        │  close()
    │<─────── 四次挥手 ──────────────>│

2.2 TCP服务端

java 复制代码
import java.io.*;
import java.net.*;

/**
 * TCP服务端
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket,监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("✅ 服务端启动,监听端口 8888...");

        while (true) {
            // 等待客户端连接(阻塞)
            Socket clientSocket = serverSocket.accept();
            System.out.println("客户端已连接:" + clientSocket.getInetAddress());

            // 为每个客户端创建一个线程处理
            new Thread(() -> handleClient(clientSocket)).start();
        }
    }

    private static void handleClient(Socket clientSocket) {
        try (
            // 获取输入流(读取客户端数据)
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));
            // 获取输出流(发送数据给客户端)
            PrintWriter out = new PrintWriter(
                    clientSocket.getOutputStream(), true)
        ) {
            String message;
            while ((message = in.readLine()) != null) {
                System.out.println("收到:" + message);
                // 回复客户端
                out.println("服务端收到:" + message);
            }
        } catch (IOException e) {
            System.out.println("客户端断开连接");
        } finally {
            try { clientSocket.close(); } catch (IOException e) {}
        }
    }
}

2.3 TCP客户端

java 复制代码
import java.io.*;
import java.net.*;
import java.util.Scanner;

/**
 * TCP客户端
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        // 连接服务端
        Socket socket = new Socket("127.0.0.1", 8888);
        System.out.println("✅ 已连接服务端");

        try (
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(
                    socket.getOutputStream(), true);
            Scanner scanner = new Scanner(System.in)
        ) {
            // 启动接收线程
            new Thread(() -> {
                try {
                    String response;
                    while ((response = in.readLine()) != null) {
                        System.out.println("服务端:" + response);
                    }
                } catch (IOException e) {}
            }).start();

            // 主线程发送数据
            System.out.println("请输入消息(输入bye退出):");
            while (scanner.hasNextLine()) {
                String message = scanner.nextLine();
                out.println(message);
                if ("bye".equalsIgnoreCase(message)) {
                    break;
                }
            }
        } finally {
            socket.close();
            System.out.println("已断开连接");
        }
    }
}

2.4 文件传输示例

java 复制代码
import java.io.*;
import java.net.*;

/**
 * TCP文件传输 - 服务端
 */
public class FileServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        System.out.println("文件服务启动...");

        Socket socket = server.accept();
        System.out.println("客户端已连接");

        try (
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            FileOutputStream fos = new FileOutputStream("D:/received_" + dis.readUTF())
        ) {
            long fileSize = dis.readLong();
            byte[] buffer = new byte[4096];
            long totalRead = 0;
            int len;

            while (totalRead < fileSize && (len = dis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                totalRead += len;
                System.out.printf("接收进度:%.1f%%\n", (totalRead * 100.0 / fileSize));
            }

            System.out.println("✅ 文件接收完成!");
        }

        socket.close();
        server.close();
    }
}

/**
 * TCP文件传输 - 客户端
 */
class FileClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9999);
        File file = new File("D:/test/document.pdf");

        try (
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            FileInputStream fis = new FileInputStream(file)
        ) {
            dos.writeUTF(file.getName());   // 发送文件名
            dos.writeLong(file.length());   // 发送文件大小

            byte[] buffer = new byte[4096];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, len);
            }
            dos.flush();

            System.out.println("✅ 文件发送完成!");
        }

        socket.close();
    }
}

三、UDP编程

3.1 UDP发送端

java 复制代码
import java.net.*;

/**
 * UDP发送端
 */
public class UDPSender {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();

        String message = "Hello, UDP!";
        byte[] data = message.getBytes();

        // 创建数据包
        DatagramPacket packet = new DatagramPacket(
                data, data.length,
                InetAddress.getByName("127.0.0.1"), 9998
        );

        // 发送
        socket.send(packet);
        System.out.println("消息已发送:" + message);

        socket.close();
    }
}

3.2 UDP接收端

java 复制代码
import java.net.*;

/**
 * UDP接收端
 */
public class UDPReceiver {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9998);
        System.out.println("UDP接收端已启动,等待消息...");

        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 接收(阻塞)
        socket.receive(packet);

        // 解析数据
        String message = new String(packet.getData(), 0, packet.getLength());
        System.out.println("收到来自 " + packet.getAddress() + " 的消息:" + message);

        socket.close();
    }
}

四、HttpClient(Java 11+)

4.1 同步请求

java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

/**
 * HttpClient同步请求
 */
public class HttpClientSyncDemo {
    public static void main(String[] args) throws Exception {
        // 创建HttpClient
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();

        // GET请求
        HttpRequest getRequest = HttpRequest.newBuilder()
                .uri(URI.create("https://httpbin.org/get"))
                .header("User-Agent", "Java HttpClient")
                .GET()
                .build();

        HttpResponse<String> getResponse = client.send(
                getRequest, HttpResponse.BodyHandlers.ofString());
        System.out.println("GET状态码:" + getResponse.statusCode());
        System.out.println("GET响应:" + getResponse.body().substring(0, 200));

        // POST请求
        HttpRequest postRequest = HttpRequest.newBuilder()
                .uri(URI.create("https://httpbin.org/post"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(
                        "{\"name\":\"张三\",\"age\":25}"))
                .build();

        HttpResponse<String> postResponse = client.send(
                postRequest, HttpResponse.BodyHandlers.ofString());
        System.out.println("\nPOST状态码:" + postResponse.statusCode());
    }
}

4.2 异步请求

java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

/**
 * HttpClient异步请求
 */
public class HttpClientAsyncDemo {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://httpbin.org/get"))
                .build();

        // 异步发送
        CompletableFuture<HttpResponse<String>> future = 
                client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        // 处理响应
        future.thenApply(HttpResponse::body)
              .thenAccept(body -> System.out.println("异步响应:" + body.substring(0, 100)))
              .join();  // 等待完成

        // 批量异步请求
        for (int i = 0; i < 5; i++) {
            final int num = i;
            client.sendAsync(
                HttpRequest.newBuilder()
                    .uri(URI.create("https://httpbin.org/get?id=" + num))
                    .build(),
                HttpResponse.BodyHandlers.ofString()
            ).thenAccept(resp -> 
                System.out.println("请求" + num + " 完成,状态:" + resp.statusCode())
            );
        }

        Thread.sleep(3000);  // 等待所有异步请求完成
    }
}

4.3 HttpClient vs 传统HttpURLConnection

特性 HttpClient (Java 11+) HttpURLConnection
API设计 现代化,流畅API 老旧,使用繁琐
异步支持 原生支持 不支持
HTTP/2 支持 不支持
WebSocket 支持 不支持
推荐程度 ⭐⭐⭐⭐⭐ ⭐⭐(遗留代码)

五、WebSocket基础

5.1 WebSocket简介

WebSocket是一种在单个TCP连接上进行全双工通信的协议,适合实时通信场景(聊天、推送、游戏等)。

特性 HTTP WebSocket
通信方式 请求-响应(单向) 全双工(双向)
连接方式 短连接(每次请求建立) 长连接(一次握手)
实时性 需要轮询 服务端主动推送
开销 每次带HTTP头 握手后数据帧开销小

5.2 Java WebSocket客户端(Java 11+)

java 复制代码
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CompletableFuture;

/**
 * WebSocket客户端
 */
public class WebSocketClient {
    public static void main(String[] args) throws Exception {
        CompletableFuture<WebSocket> wsFuture = new CompletableFuture<>();

        WebSocket ws = HttpClient.newHttpClient()
            .newWebSocketBuilder()
            .buildAsync(URI.create("ws://localhost:8080/ws"), new WebSocket.Listener() {
                @Override
                public void onOpen(WebSocket webSocket) {
                    System.out.println("✅ WebSocket连接已建立");
                    webSocket.request(1);  // 请求接收消息
                }

                @Override
                public CompletionStage<?> onText(WebSocket webSocket, 
                        CharSequence data, boolean last) {
                    System.out.println("收到消息:" + data);
                    webSocket.request(1);  // 请求下一条消息
                    return null;
                }

                @Override
                public CompletionStage<?> onClose(WebSocket webSocket, 
                        int statusCode, String reason) {
                    System.out.println("连接关闭:" + reason);
                    return null;
                }
            })
            .join();

        // 发送消息
        ws.sendText("Hello, WebSocket!", true);
        ws.sendText("这是一条WebSocket消息", true);

        Thread.sleep(5000);
        ws.sendClose(WebSocket.NORMAL_CLOSURE, "客户端关闭");
    }
}

六、实战:简易聊天室

java 复制代码
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * TCP聊天室 - 服务端
 */
public class ChatServer {
    private static final int PORT = 8888;
    // 所有连接的客户端输出流
    private static final Set<PrintWriter> clients = ConcurrentHashMap.newKeySet();

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(PORT);
        System.out.println("💬 聊天室服务端已启动,端口:" + PORT);

        while (true) {
            Socket socket = server.accept();
            System.out.println("新用户加入:" + socket.getInetAddress());
            new ClientHandler(socket).start();
        }
    }

    // 广播消息给所有客户端
    private static void broadcast(String message) {
        for (PrintWriter writer : clients) {
            writer.println(message);
        }
    }

    // 客户端处理线程
    static class ClientHandler extends Thread {
        private Socket socket;
        private String nickname;
        private PrintWriter out;
        private BufferedReader in;

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                in = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);

                // 获取昵称
                out.println("请输入你的昵称:");
                nickname = in.readLine();
                clients.add(out);

                broadcast("📢 " + nickname + " 加入了聊天室!(" + clients.size() + "人在线)");

                // 接收消息
                String message;
                while ((message = in.readLine()) != null) {
                    if ("bye".equalsIgnoreCase(message.trim())) {
                        break;
                    }
                    broadcast(nickname + ":" + message);
                }
            } catch (IOException e) {
                System.out.println(nickname + " 连接异常");
            } finally {
                clients.remove(out);
                broadcast("📢 " + nickname + " 离开了聊天室 (" + clients.size() + "人在线)");
                try { socket.close(); } catch (IOException e) {}
            }
        }
    }
}
java 复制代码
import java.io.*;
import java.net.*;
import java.util.Scanner;

/**
 * TCP聊天室 - 客户端
 */
public class ChatClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        Scanner scanner = new Scanner(System.in);

        // 接收线程
        new Thread(() -> {
            try {
                String message;
                while ((message = in.readLine()) != null) {
                    System.out.println(message);
                }
            } catch (IOException e) {
                System.out.println("与服务器断开连接");
            }
        }).start();

        // 发送消息
        while (scanner.hasNextLine()) {
            String message = scanner.nextLine();
            out.println(message);
            if ("bye".equalsIgnoreCase(message)) {
                break;
            }
        }

        socket.close();
    }
}

七、常见面试题解析

Q1:TCP三次握手和四次挥手的过程?

三次握手:客户端发送SYN → 服务端回复SYN+ACK → 客户端发送ACK。确认双方的发送和接收能力正常。

四次挥手:客户端发送FIN → 服务端回复ACK → 服务端发送FIN → 客户端回复ACK。因为TCP是全双工,需要双向关闭。

Q2:HttpClient比HttpURLConnection好在哪?

HttpClient支持异步请求、HTTP/2、WebSocket,API更现代简洁。HttpURLConnection是Java早期的实现,使用繁琐,功能有限。


八、总结与下篇预告

本篇要点

要点 说明
TCP编程 Socket/ServerSocket实现可靠通信
UDP编程 DatagramSocket/DatagramPacket实现快速通信
HttpClient Java 11+的现代化HTTP客户端
WebSocket 全双工实时通信
网络三要素 IP、端口、协议

📢 下篇预告

第29篇:反射机制------我们将学习Class类、动态代理,了解框架底层的秘密!

💬 互动话题

你做过网络编程相关的项目吗?Socket编程和HTTP请求哪个用得多?欢迎在评论区分享!


参考资料

相关推荐
智慧物业老杨4 小时前
物业日常巡查的数智化重构:从“打卡式巡检“到“闭环式风控“
android·java·人工智能·系统架构·rxjava
步行cgn10 小时前
Spring c 命名空间注入详解
java·后端·spring
明月_清风11 小时前
Maven 到底是什么?一篇文章搞懂 Java 项目构建与依赖管理
java·后端·maven
比兔代理11 小时前
正向代理与反向代理技术辨析,为什么代理 IP 属于正向代理
服务器·网络·http·ip
aramae11 小时前
MySQL复合查询(8)
java·c语言·开发语言·后端·算法
Rain的Java大神之路12 小时前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
小贺儿开发12 小时前
Unity 家居视频遥控(细节优化)
科技·unity·程序员·udp·视频·工具·通信
Wang's Blog12 小时前
Java 接入Redis: 通用命令与键管理
java·服务器·redis
Wang's Blog12 小时前
Java 接入Redis: 列表集合与有序集合操作命令
java·服务器·redis
Ivanqhz13 小时前
SVD++算法
java·服务器·网络·深度学习·神经网络