十六、Java 网络编程全解析:UDP/TCP 通信 + BS/CS 架构

网络编程是 Java 开发的核心技能之一,小到即时聊天工具、文件传输,大到分布式系统、微服务通信,都离不开网络数据交互。


目录

一、网络编程基础:

[1.1 什么是网络编程?](#1.1 什么是网络编程?)

[1.2 两大架构:CS vs BS](#1.2 两大架构:CS vs BS)

[1.3 核心术语:IP、端口、协议](#1.3 核心术语:IP、端口、协议)

[1.3.1 IP:互联网协议](#1.3.1 IP:互联网协议)

[1.3.2 端口:](#1.3.2 端口:)

[1.3.3 协议:](#1.3.3 协议:)

[二、UDP 通信:无连接的 "快速传输"](#二、UDP 通信:无连接的 “快速传输”)

[2.1 UDP 核心类与方法](#2.1 UDP 核心类与方法)

[2.2 UDP 一发一收](#2.2 UDP 一发一收)

核心步骤:

[1. 接收端](#1. 接收端)

[2. 发送端](#2. 发送端)

运行步骤:

[2.3 UDP 多发多收](#2.3 UDP 多发多收)

[1. 接收端](#1. 接收端)

[2. 发送端](#2. 发送端)

运行效果:

[三、TCP 通信:可靠的 "面向连接" 传输](#三、TCP 通信:可靠的 “面向连接” 传输)

[3.1 TCP 核心类与方法](#3.1 TCP 核心类与方法)

[3.2 TCP 一发一收](#3.2 TCP 一发一收)

核心步骤:

[1. 服务器端](#1. 服务器端)

[2. 客户端](#2. 客户端)

运行效果:

[3.3 TCP 多发多收](#3.3 TCP 多发多收)

[1. 服务器端](#1. 服务器端)

[2. 客户端](#2. 客户端)

[3.4 支持多个客户端连接](#3.4 支持多个客户端连接)

[1. 线程处理类](#1. 线程处理类)

[2. 多客户端服务器端](#2. 多客户端服务器端)

运行效果:

[3.5 线程池优化](#3.5 线程池优化)

[1. 线程池优化的多客户端服务器](#1. 线程池优化的多客户端服务器)

核心优势:

线程池核心参数说明

[3.6 BS 架构原理](#3.6 BS 架构原理)

[1. HTTP 协议核心特点](#1. HTTP 协议核心特点)

[2. HTTP 请求 / 响应格式](#2. HTTP 请求 / 响应格式)

[3. 模拟 BS 架构:Java 服务器响应浏览器请求](#3. 模拟 BS 架构:Java 服务器响应浏览器请求)

运行步骤:

[四、TCP vs UDP 核心区别](#四、TCP vs UDP 核心区别)

五、核心知识点总结


一、网络编程基础:

1.1 什么是网络编程?

网络编程的本质是跨设备的程序间数据传输------ 让运行在不同电脑、手机上的 Java 程序,通过网络(局域网 / 互联网)交换数据。

核心目标是:可靠、高效地传输数据

1.2 两大架构:CS vs BS

架构 全称 核心特点 典型例子 优点 缺点 适用场景
CS Client/Server (客户端 / 服务器) 需安装独立客户端;服务器和客户端直接通信;可定制化程度高 QQ、微信、王者荣耀、迅雷 1. 传输速度快; 2. 功能全面; 3. 安全性高 1. 客户端需下载安装,更新麻烦; 2. 开发维护成本高; 3. 占用设备空间 对体验、速度、功能要求高的场景
BS Browser/Server (浏览器 / 服务器) 无需安装客户端;通过浏览器访问;基于 HTTP 协议 淘宝、百度、博客园、在线办公系统 1. 跨平台; 2. 无需更新; 3. 开发维护成本低 1. 依赖浏览器,功能受限; 2. 速度慢; 3. 安全性依赖浏览器和网络 对便捷性要求高、功能相对简单的场景

1.3 核心术语:IP、端口、协议

1.3.1 IP:互联网协议
  • 定义:Internet Protocol,唯一标识网络中的一台设备,相当于现实中的 "家庭住址"。
  • 格式
    • IPv4:32 位二进制数,分成 4 段十进制(0-255),如192.168.1.1
    • IPv6:128 位二进制数,分成 8 段十六进制,如2001:0db8:85a3:0000:0000:8a2e:0370:7334
  • 常用 IP 实战说明
    • 127.0.0.1:本地回环地址,代表 "本机",仅本机可访问,用于本地程序测试(无需联网);
    • 0.0.0.0:绑定本机所有网卡的 IP,服务器用它表示 "监听所有网络接口的请求"(外网、内网都能访问);
    • 内网 IP:如192.168.x.x、10.x.x.x,仅局域网内设备可访问,外网无法直接连接;
    • 外网 IP:全球唯一,需通过路由器、公网服务器分配,外网设备可直接访问。
1.3.2 端口:
  • 定义:唯一标识设备上的一个应用程序,相当于 "家庭住址的门牌号"------ 找到设备(IP)后,通过端口找到具体的程序。
  • 范围:0-65535;
  • 分类与实战注意
    • 知名端口(0-1023):系统 / 常用服务占用,开发时避免使用(如 HTTP 的 80 端口、MySQL 的 3306 端口、SSH 的 22 端口);
    • 动态端口(1024-65535):开发时可自由使用,但需注意:
      1. 避免使用常用软件的默认端口(如 Tomcat 的 8080、Nginx 的 80);
      2. 端口被占用时,会报BindException: Address already in use,需更换端口或关闭占用进程;
    • 一个端口只能被一个应用程序占用,但一个应用可占用多个端口。
1.3.3 协议:

协议是设备间通信的 "约定",规定了数据的格式、传输方式、错误处理等。

Java 网络编程最核心的两个协议:

协议 连接性 可靠性 传输方式 数据大小限制 速度 传输过程 适用场景
UDP 无连接 不可靠(数据可能丢失、乱序) 数据报(数据包独立发送) 单次传输≤64KB 快(无连接开销) 1. 发送端直接封装数据为数据包;2. 无需建立连接,直接发送;3. 接收端接收数据包,不保证一定收到 即时通信(聊天、语音)、视频通话、广播通知、游戏数据(允许少量丢包)
TCP 面向连接 可靠(数据不丢失、不重复、有序) 字节流(数据连续传输) 无限制(理论上) 慢(需连接和确认开销) 1. 三次握手建立连接;2. 字节流传输数据(需应用层处理边界);3. 四次挥手断开连接;4. 丢包重传、超时重连 文件传输、登录注册、网页访问、支付(要求数据可靠)

补充:TCP 三次握手 / 四次挥手

  • 三次握手(建立连接):像打电话 ------"喂,你在吗?"→"我在,你能听到吗?"→"能听到,开始说吧",确保双方收发正常;
  • 四次挥手(断开连接):像挂电话 ------"我说完了"→"我知道你说完了,我再确认下"→"我确认完了"→"好的,挂了",确保数据传输完成。

二、UDP 通信:无连接的 "快速传输"

UDP 核心是 "数据报" 传输,无需建立连接,直接发送,适合对可靠性要求不高、追求速度的场景。下面补充核心类方法解析、缓冲区选择、常见问题等细节。

2.1 UDP 核心类与方法

类名 作用 核心方法 注意事项
DatagramSocket UDP 通信的 "Socket"(端口绑定、发送 / 接收数据包) DatagramSocket():创建发送端 Socket(系统分配端口); DatagramSocket(int port):创建接收端 Socket(绑定指定端口); send(DatagramPacket p):发送数据包; receive(DatagramPacket p):接收数据包(阻塞); close():关闭 Socket,释放资源 1. 接收端必须绑定端口,否则发送端不知道发给哪个应用;2. 发送端可不用绑定端口,系统自动分配临时端口
DatagramPacket UDP 的 "数据包"(封装数据、目标 IP、目标端口) DatagramPacket(byte[] buf, int length):创建接收用数据包(指定缓冲区); DatagramPacket(byte[] buf, int length, InetAddress addr, int port):创建发送用数据包(指定数据、目标 IP、端口); getData():获取数据包中的数据; getLength():获取有效数据长度; getAddress():获取发送端 IP(接收端用); getPort():获取发送端端口(接收端用) 1. 缓冲区大小建议 1024-8192 字节(太小可能截断数据,太大浪费资源);2. 接收时getLength()获取实际数据长度,避免读取空字节

2.2 UDP 一发一收

核心步骤:
  1. 发送端(Client):创建DatagramSocket→ 封装数据为DatagramPacket→ 发送数据;
  2. 接收端(Server):创建DatagramSocket并绑定端口→ 创建DatagramPacket接收数据→ 解析数据。
1. 接收端
java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * UDP接收端:接收发送端的数据
 */
public class UDPServer {
    public static void main(String[] args) throws Exception {
        // 1. 创建接收端Socket,绑定端口(必须指定端口,否则发送端不知道发给哪个应用)
        DatagramSocket socket = new DatagramSocket(8888);

        // 2. 创建数据包,用于接收数据(缓冲区大小1024字节)
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        System.out.println("UDP接收端已启动,等待数据...");
        // 3. 接收数据(阻塞方法,直到收到数据)
        socket.receive(packet);

        // 4. 解析数据
        String data = new String(packet.getData(), 0, packet.getLength());
        System.out.println("收到数据:" + data);
        System.out.println("发送端IP:" + packet.getAddress().getHostAddress());
        System.out.println("发送端端口:" + packet.getPort());

        // 5. 关闭资源
        socket.close();
    }
}
2. 发送端
java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDP发送端:向接收端发送数据
 */
public class UDPClient {
    public static void main(String[] args) throws Exception {
        // 1. 创建发送端Socket(无需指定端口,系统自动分配)
        DatagramSocket socket = new DatagramSocket();

        // 2. 准备发送的数据
        String sendData = "Hello UDP!我是发送端";
        byte[] data = sendData.getBytes();

        // 3. 封装数据包(数据+接收端IP+接收端端口)
        InetAddress serverIP = InetAddress.getByName("127.0.0.1"); // 接收端IP(本地测试)
        int serverPort = 8888; // 接收端端口(必须和接收端绑定的端口一致)
        DatagramPacket packet = new DatagramPacket(data, data.length, serverIP, serverPort);

        // 4. 发送数据
        System.out.println("UDP发送端发送数据:" + sendData);
        socket.send(packet);

        // 5. 关闭资源
        socket.close();
    }
}
运行步骤:
  1. 先运行UDPServer(接收端);
  2. 再运行UDPClient(发送端);
  3. 接收端控制台输出:
java 复制代码
UDP接收端已启动,等待数据...
收到数据:Hello UDP!我是发送端
发送端IP:127.0.0.1
发送端端口:54321(系统自动分配的端口)

2.3 UDP 多发多收

实际场景中,需要持续发送或接收数据,只需在 "一发一收" 的基础上添加循环即可。

1. 接收端
java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPMultiServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(8888);
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        System.out.println("UDP多发多收接收端已启动,持续等待数据...");
        while (true) { // 循环接收
            socket.receive(packet); // 阻塞等待
            String data = new String(packet.getData(), 0, packet.getLength());
            System.out.println("收到[" + packet.getAddress().getHostAddress() + "]的数据:" + data);
            
            // 若收到"exit",退出循环
            if ("exit".equals(data)) {
                System.out.println("接收端关闭...");
                break;
            }
        }
        socket.close();
    }
}
2. 发送端
java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UDPMultiClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();
        InetAddress serverIP = InetAddress.getByName("127.0.0.1");
        int serverPort = 8888;
        Scanner scanner = new Scanner(System.in);

        System.out.println("UDP多发多收发送端已启动,输入exit退出...");
        while (true) { // 循环发送
            System.out.print("请输入发送的数据:");
            String sendData = scanner.nextLine();
            
            // 发送数据
            byte[] data = sendData.getBytes();
            DatagramPacket packet = new DatagramPacket(data, data.length, serverIP, serverPort);
            socket.send(packet);
            
            // 输入"exit",退出循环
            if ("exit".equals(sendData)) {
                System.out.println("发送端关闭...");
                break;
            }
        }
        scanner.close();
        socket.close();
    }
}
运行效果:
  • 接收端持续监听,发送端输入多次数据,接收端实时接收;
  • 输入 "exit",两端都关闭。

三、TCP 通信:可靠的 "面向连接" 传输

TCP 是 "面向连接协议",就像 "打电话"------ 先建立连接(三次握手),再传输数据,数据传输完成后断开连接(四次挥手),保证数据不丢失、不重复,适合对可靠性要求高的场景。

3.1 TCP 核心类与方法

类名 作用 核心方法 注意事项
ServerSocket TCP 服务器端 Socket(绑定端口、监听客户端连接) ServerSocket(int port):绑定端口; accept():监听客户端连接(阻塞); close():关闭服务器 1. 端口被占用时无法启动;2. accept()返回客户端对应的Socket,一个客户端对应一个Socket
Socket TCP 通信的 "连接对象"(客户端 / 服务器端都用,封装流) Socket(String host, int port):客户端连接服务器(IP + 端口); getInputStream():获取输入流(读数据); getOutputStream():获取输出流(写数据); close():关闭连接 1. 客户端创建时直接连接服务器,连接失败抛异常;2. 流关闭后,Socket 也会关闭;3. 必须关闭流和 Socket,释放资源
InputStream/OutputStream 字节流(读写数据) read(byte[] b):读数据到字节数组; write(byte[] b):写字节数组到流; close():关闭流 1. read()阻塞,返回 - 1 表示流结束;2. 字节流读写中文易乱码,建议用字符流包装
BufferedReader/BufferedWriter 字符缓冲流(优化读写效率,支持按行读写) readLine():按行读数据(阻塞); write(String s):写字符串; newLine():换行; flush():刷新缓冲区(必须调用,否则数据可能未发送) 1. 需用InputStreamReader/OutputStreamWriter包装字节流;2. readLine()返回 null 表示流结束;3. 必须调用flush(),否则缓冲区数据不会发送

3.2 TCP 一发一收

核心步骤:
  1. 服务器端(Server):创建ServerSocket绑定端口→ 调用accept()等待客户端连接→ 获得Socket→ 通过流读写数据;
  2. 客户端(Client):创建Socket连接服务器(指定 IP 和端口)→ 通过流读写数据。
1. 服务器端
java 复制代码
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TCP服务器端:接收客户端数据并响应
 */
public class TCPServer {
    public static void main(String[] args) throws Exception {
        // 1. 创建服务器端Socket,绑定端口
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("TCP服务器端已启动,等待客户端连接...");

        // 2. 等待客户端连接(阻塞方法,直到有客户端连接)
        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接:" + socket.getInetAddress().getHostAddress());

        // 3. 通过流接收客户端数据
        InputStream in = socket.getInputStream();
        byte[] buffer = new byte[1024];
        int len = in.read(buffer); // 读取客户端数据
        String clientData = new String(buffer, 0, len);
        System.out.println("收到客户端数据:" + clientData);

        // 4. 响应客户端(可选)
        socket.getOutputStream().write("我已收到你的数据!".getBytes());

        // 5. 关闭资源
        in.close();
        socket.close();
        serverSocket.close();
    }
}
2. 客户端
java 复制代码
import java.io.InputStream;
import java.net.Socket;

/**
 * TCP客户端:向服务器端发送数据并接收响应
 */
public class TCPClient {
    public static void main(String[] args) throws Exception {
        // 1. 创建客户端Socket,连接服务器(IP+端口)
        Socket socket = new Socket("127.0.0.1", 9999);
        System.out.println("TCP客户端已连接服务器...");

        // 2. 向服务器发送数据
        socket.getOutputStream().write("Hello TCP!我是客户端".getBytes());

        // 3. 接收服务器响应
        InputStream in = socket.getInputStream();
        byte[] buffer = new byte[1024];
        int len = in.read(buffer);
        String serverData = new String(buffer, 0, len);
        System.out.println("收到服务器响应:" + serverData);

        // 4. 关闭资源
        in.close();
        socket.close();
    }
}
运行效果:
  • 服务器端输出:
java 复制代码
TCP服务器端已启动,等待客户端连接...
客户端已连接:127.0.0.1
收到客户端数据:Hello TCP!我是客户端
  • 客户端输出:
java 复制代码
TCP客户端已连接服务器...
收到服务器响应:我已收到你的数据!

3.3 TCP 多发多收

和 UDP 类似,TCP 多发多收只需添加循环,但要注意:read()方法是阻塞的,需要约定 "结束标记" 或关闭输出流,避免程序一直阻塞。

1. 服务器端
java 复制代码
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPMultiServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("TCP多发多收服务器端已启动...");
        Socket socket = serverSocket.accept();
        InputStream in = socket.getInputStream();
        byte[] buffer = new byte[1024];

        while (true) {
            int len = in.read(buffer); // 阻塞读取
            if (len == -1) break; // 客户端关闭输出流,len=-1
            String clientData = new String(buffer, 0, len);
            System.out.println("收到客户端数据:" + clientData);
            
            // 响应客户端
            socket.getOutputStream().write(("已收到:" + clientData).getBytes());
        }

        // 关闭资源
        in.close();
        socket.close();
        serverSocket.close();
    }
}
2. 客户端
java 复制代码
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;

public class TCPMultiClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 9999);
        Scanner scanner = new Scanner(System.in);
        InputStream in = socket.getInputStream();
        byte[] buffer = new byte[1024];

        System.out.println("TCP多发多收客户端已启动,输入exit退出...");
        while (true) {
            System.out.print("请输入发送的数据:");
            String sendData = scanner.nextLine();
            
            // 发送数据
            socket.getOutputStream().write(sendData.getBytes());
            
            // 接收响应
            int len = in.read(buffer);
            String serverData = new String(buffer, 0, len);
            System.out.println("服务器响应:" + serverData);
            
            // 输入exit,关闭客户端
            if ("exit".equals(sendData)) {
                System.out.println("客户端关闭...");
                break;
            }
        }

        // 关闭资源(关闭输出流,服务器端read()会返回-1)
        scanner.close();
        in.close();
        socket.close();
    }
}

3.4 支持多个客户端连接

上面的 TCP 服务器端只能处理一个客户端,要支持多个客户端,需要为每个客户端分配一个独立线程 ------ 服务器端调用accept()接收客户端后,启动线程处理该客户端的通信。

1. 线程处理类
java 复制代码
import java.io.InputStream;
import java.net.Socket;

/**
 * 线程类:处理单个客户端的TCP通信
 */
public class ClientHandler implements Runnable {
    private Socket socket;

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

    @Override
    public void run() {
        try {
            String clientIP = socket.getInetAddress().getHostAddress();
            System.out.println("客户端[" + clientIP + "]已连接");

            InputStream in = socket.getInputStream();
            byte[] buffer = new byte[1024];
            while (true) {
                int len = in.read(buffer);
                if (len == -1) break;
                String clientData = new String(buffer, 0, len);
                System.out.println("收到[" + clientIP + "]的数据:" + clientData);
                
                // 响应客户端
                socket.getOutputStream().write(("已收到你的消息:" + clientData).getBytes());
            }

            // 关闭资源
            in.close();
            socket.close();
            System.out.println("客户端[" + clientIP + "]已断开连接");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
2. 多客户端服务器端
java 复制代码
import java.net.ServerSocket;
import java.net.Socket;

public class TCPMultiClientServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("多客户端TCP服务器已启动,等待客户端连接...");

        while (true) { // 循环接收客户端连接
            Socket socket = serverSocket.accept(); // 阻塞等待
            // 启动线程处理该客户端
            new Thread(new ClientHandler(socket)).start();
        }
    }
}
运行效果:
  1. 启动服务器端后,可同时启动多个客户端,每个客户端发送的数据都会被服务器端接收并响应;
  2. 每个客户端的通信独立,互不干扰。

3.5 线程池优化

上面的线程版服务器端,每个客户端对应一个线程,若有 1000 个客户端,就会创建 1000 个线程,导致资源耗尽。线程池能复用线程,控制线程数量,是企业级开发的标准优化方案。

1. 线程池优化的多客户端服务器
java 复制代码
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPThreadPoolServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(9999);
        // 创建固定线程池,核心线程数为5(可根据需求调整)
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        System.out.println("线程池优化的TCP服务器已启动...");

        while (true) {
            Socket socket = serverSocket.accept();
            // 线程池分配线程处理客户端
            threadPool.submit(new ClientHandler(socket));
        }
    }
}
核心优势:
  • **线程复用:**避免频繁创建 / 销毁线程,减少资源开销;
  • **控制线程数量:**防止线程过多导致内存溢出;
  • **提高响应速度:**线程池中有空闲线程时,直接复用处理新客户端。
线程池核心参数说明
  1. 核心线程数 :线程池长期维持的线程数,建议配置为CPU核心数 * 2(如 4 核 CPU 配置 8 个);
  2. 最大线程数:线程池可创建的最大线程数,超过核心线程数的线程会在空闲 60 秒后销毁;
  3. 任务队列:无空闲线程时,新任务会放入队列等待;
  4. 拒绝策略:队列满且达到最大线程数时,新任务的处理方式(如抛异常、丢弃任务)。

3.6 BS 架构原理

BS 架构本质是 "基于 HTTP 协议的 TCP 通信",浏览器是客户端,Web 服务器(如 Tomcat)是服务器端,通信流程如下:

  1. 浏览器(客户端)向 Web 服务器发送 HTTP 请求(基于 TCP);
  2. Web 服务器接收请求,处理后返回 HTTP 响应(包含网页数据);
  3. 浏览器解析响应,展示网页。
1. HTTP 协议核心特点
  • 基于 TCP 协议,面向连接;
  • 无状态(服务器不记住客户端信息,需用 Cookie/Session 维持状态);
  • 请求 - 响应模式(浏览器发请求,服务器回响应)。
2. HTTP 请求 / 响应格式
类型 格式示例 说明
HTTP 请求 GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nUser-Agent: Chrome/114.0.0.0\r\n\r\n 1. 首行:请求方法(GET/POST)+ 请求路径 + 协议版本;2. 请求头:键值对(如 Host、User-Agent);3. 空行:分隔请求头和请求体;4. 请求体:POST 请求才有的数据(如表单提交)
HTTP 响应 HTTP/1.1 200 OK\r\nContent-Length: 100\r\nContent-Type: text/html;charset=UTF-8\r\n\r\n<html>...</html> 1. 首行:协议版本 + 状态码(200 = 成功) + 状态描述;2. 响应头:键值对(如 Content-Type 指定返回数据类型);3. 空行:分隔响应头和响应体;4. 响应体:返回给浏览器的数据(如 HTML、CSS、JS)
3. 模拟 BS 架构:Java 服务器响应浏览器请求
java 复制代码
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;

/**
 * 模拟BS架构:浏览器访问Java服务器,返回HTML页面(详细版)
 */
public class BSServer {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("BS服务器已启动,浏览器访问:http://127.0.0.1:8080");

            while (true) {
                Socket socket = serverSocket.accept();
                // 线程池处理浏览器请求
                Executors.newFixedThreadPool(5).submit(() -> {
                    try {
                        // 1. 解析浏览器请求(简化版:仅读取请求首行)
                        BufferedReader br = new BufferedReader(
                            new InputStreamReader(socket.getInputStream(), "UTF-8")
                        );
                        String requestLine = br.readLine(); // 读取请求首行(如GET / HTTP/1.1)
                        System.out.println("浏览器请求:" + requestLine);

                        // 2. 准备响应数据(HTML页面)
                        String html = "<!DOCTYPE html>" +
                                      "<html lang='zh-CN'>" +
                                      "<head><meta charset='UTF-8'><title>BS测试</title></head>" +
                                      "<body style='text-align:center;margin-top:100px;'>" +
                                      "<h1 style='color:blue;'>Hello BS!</h1>" +
                                      "<p>这是Java服务器返回的HTML页面</p>" +
                                      "<p>请求信息:" + requestLine + "</p>" +
                                      "</body></html>";

                        // 3. 构建HTTP响应(必须符合格式)
                        String response = 
                            "HTTP/1.1 200 OK\r\n" + // 状态码200=成功
                            "Content-Length: " + html.getBytes("UTF-8").length + "\r\n" + // 响应体长度
                            "Content-Type: text/html;charset=UTF-8\r\n" + // 数据类型:HTML,编码UTF-8
                            "Connection: close\r\n" + // 响应后关闭连接
                            "\r\n" + // 空行分隔响应头和响应体
                            html; // 响应体(HTML页面)

                        // 4. 发送响应给浏览器
                        BufferedWriter bw = new BufferedWriter(
                            new OutputStreamWriter(socket.getOutputStream(), "UTF-8")
                        );
                        bw.write(response);
                        bw.flush();

                        // 5. 关闭资源
                        br.close();
                        bw.close();
                        socket.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
运行步骤:
  1. 启动BSServer
  2. 打开浏览器,输入http://127.0.0.1:8080
  3. 浏览器显示蓝色标题的 HTML 页面,服务器控制台打印浏览器的请求信息。

四、TCP vs UDP 核心区别

对比维度 TCP UDP
连接性 面向连接(三次握手建立,四次挥手断开) 无连接(直接发送,无需建立 / 断开)
可靠性 可靠(丢包重传、超时重连、数据有序) 不可靠(无重传、无确认,可能丢包 / 乱序)
传输方式 字节流(数据连续,需应用层处理边界) 数据报(数据包独立,自带边界)
数据大小 无限制(理论上) 单次传输≤64KB
速度 慢(连接、确认、重传开销) 快(无额外开销)
核心类 ServerSocketSocket DatagramSocketDatagramPacket
流类型 字节流(需包装为字符流优化) 无流,直接操作数据包
适用场景 文件传输、登录注册、网页访问、支付 即时聊天、视频通话、广播、游戏数据
常见问题 粘包(需约定数据边界)、连接超时 数据丢失、乱码(需统一编码)

五、核心知识点总结

  1. 网络编程基础
    • 架构:CS(客户端 / 服务器)、BS(浏览器 / 服务器);
    • 核心术语:IP(设备标识)、端口(应用标识)、协议(TCP/UDP);
    • TCP vs UDP:TCP 可靠连接,UDP 快速无连接。
  2. UDP 通信
    • 核心类:DatagramSocket(Socket)、DatagramPacket(数据包);
    • 特点:无连接、不可靠,适合多发多收、广播场景。
  3. TCP 通信
    • 核心类:ServerSocket(服务器)、Socket(客户端);
    • 特点:面向连接、可靠,通过流读写数据;
    • 多客户端:线程 / 线程池处理,线程池是企业级优化方案。
  4. BS 架构:基于 HTTP 协议的 TCP 通信,浏览器是客户端,Web 服务器是服务器端。
相关推荐
猹叉叉(学习版)2 小时前
【ASP.NET CORE】 14. RabbitMQ、洋葱架构
笔记·后端·架构·c#·rabbitmq·asp.net·.netcore
椎4952 小时前
SpringAI+DeepSeek大模型应用开发实战
java
江沉晚呤时2 小时前
基于 AssemblyLoadContext 的 .NET 插件化架构设计与实现
开发语言·c#·.net
顶点多余2 小时前
Linux中基础IO知识全解
linux·服务器·算法
Dynadot_tech2 小时前
如何出售域名自己的域名
网络·域名·dynadot·网站域名
独断万古他化2 小时前
【抽奖系统开发实战】Spring Boot 抽奖系统全链路总结:从架构到落地的实践复盘
java·spring boot·后端·架构·系列总结
编程之升级打怪2 小时前
简单的测试搜索词的分割算法思路
java·算法
码界奇点2 小时前
基于Spring MVC和MyBatis的妖气山视频管理系统设计与实现
java·spring·毕业设计·mvc·mybatis·源代码管理
2501_930707782 小时前
使用C#代码获取PDF文件的页数
开发语言·pdf·c#