Java Socket 网络编程实战:从基础通信到线程池优化

引言

[一、什么是 Socket?](#一、什么是 Socket?)

[二、基础实现:Socket 客户端与服务器通信](#二、基础实现:Socket 客户端与服务器通信)

[2.1 服务器端(Server)](#2.1 服务器端(Server))

[2.2 客户端(Client)](#2.2 客户端(Client))

[2.3 运行说明](#2.3 运行说明)

三、引入子线程:实现多客户端并发通信

[3.1 线程任务类(处理单个客户端通信)](#3.1 线程任务类(处理单个客户端通信))

[3.2 优化后的服务器端(支持多客户端)](#3.2 优化后的服务器端(支持多客户端))

[3.3 运行效果](#3.3 运行效果)

四、线程池优化:高效管理线程资源

[4.1 线程池版](#4.1 线程池版)

4.1.1服务器端

4.1.2创建线程池

[4.2 线程池核心优势](#4.2 线程池核心优势)

4.3线程池执行示意图

[五、总结:Socket 通信的优化演进](#五、总结:Socket 通信的优化演进)


引言

在网络编程中,Socket(套接字) 是实现不同设备间通信的核心技术,基于 TCP/IP 协议完成客户端与服务器的双向数据传输。本文将按照「Socket 基础概念 → 客户端-服务器通信实现 → 子线程优化 → 线程池进阶」的逻辑,从零搭建一套完整的 Socket 通信体系。


一、什么是 Socket?

Socket 是应用层与传输层之间的接口,本质是一个编程抽象,用于描述 IP 地址和端口的组合,实现不同主机上进程间的网络通信。

核心作用:封装 TCP/IP 协议的底层细节,让开发者无需关注网络传输的底层原理,只需通过 Socket API 即可实现数据收发。

通信模型:基于 TCP 协议的 Socket 通信是面向连接的(三次握手建立连接,四次挥手断开连接),保证数据传输的可靠性。

核心组件:

ServerSocket:运行在服务器端,监听指定端口,等待客户端连接;

Socket:运行在客户端,主动向服务器发起连接,同时也作为服务器与客户端通信的载体。


二、基础实现:Socket 客户端与服务器通信

先实现最基础的单客户端-服务器通信,服务器端监听端口并接收客户端消息,客户端发送消息给服务器。

2.1 服务器端(Server)

Socket服务器端:监听端口,接受客户端连接并读取数据

java 复制代码
package com.qcby;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.net.ServerSocket;

import java.net.Socket;

/**

* Socket服务器端:监听端口,接收客户端连接并读取消息

*/

public class SocketServer {

public static void main(String[] args) {

ServerSocket serverSocket = null;

Socket socket = null;

try {

// 1. 创建ServerSocket,监听8877端口

serverSocket = new ServerSocket(8877);

System.out.println("服务器已启动,监听端口8877,等待客户端连接...");

// 2. 阻塞等待客户端连接(accept()方法会阻塞,直到有客户端连接)

socket = serverSocket.accept();

System.out.println("客户端已连接:" + socket.getInetAddress());

// 3. 获取客户端输入流,读取消息

InputStream is = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(is));

String msg;

while ((msg = br.readLine()) != null) {

System.out.println("收到客户端消息:" + msg);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 4. 关闭资源

try {

if (socket != null) socket.close();

if (serverSocket != null) serverSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

2.2 客户端(Client)

Socket客户端:向服务器发起连接并发送消息

java 复制代码
package com.qcby;

import java.io.IOException;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.Socket;

/**

* Socket客户端:向服务器发起连接并发送消息

*/

public class SocketClient {

public static void main(String[] args) {

Socket socket = null;

try {

// 1. 创建Socket,连接服务器(localhost为本地地址,8877为服务器端口)

socket = new Socket("127.0.0.1", 8877);

System.out.println("客户端已连接服务器");

// 2. 获取输出流,向服务器发送消息

OutputStream os = socket.getOutputStream();

PrintWriter pw = new PrintWriter(os);

pw.write("Hello Socket Server!");

pw.flush(); // 刷新缓冲区,确保消息发送

pw.close();

} catch (IOException e) {

e.printStackTrace();

} finally {

// 3. 关闭Socket

try {

if (socket != null) socket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

2.3 运行说明

  1. 先启动 SocketServer.java,服务器进入阻塞状态,等待客户端连接;
  2. 再启动 SocketClient.java,客户端与服务器建立连接并发送消息;
  3. 服务器控制台会打印「收到客户端消息:Hello Socket Server!」。

问题:该基础版本仅支持单客户端连接,服务器处理完一个客户端后就会关闭,无法同时处理多个客户端请求。


三、引入子线程:实现多客户端并发通信

为了让服务器同时处理多个客户端的请求,需要为**每个客户端连接创建一个独立的子线程**,主线程继续监听新的客户端连接。

3.1 线程任务类(处理单个客户端通信)

java 复制代码
package com.qcby;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.net.Socket;

/**

* 线程任务类:处理单个客户端的消息交互

*/

public class ClientHandler implements Runnable {

private Socket socket; // 与客户端的Socket连接

public ClientHandler(Socket socket) {

this.socket = socket;

}

@Override

public void run() {

try {

// 读取客户端消息

InputStream is = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(is));

String msg;

while ((msg = br.readLine()) != null) {

System.out.println("收到客户端[" + socket.getInetAddress() + "]消息:" + msg);

}

} catch (IOException e) {

System.out.println("客户端[" + socket.getInetAddress() + "]断开连接");

} finally {

// 关闭当前客户端的Socket

try {

if (socket != null) socket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

3.2 优化后的服务器端(支持多客户端)

java 复制代码
package com.qcby;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

/**

* 多线程Socket服务器:支持并发处理多个客户端

*/

public class ThreadSocketServer {

public static void main(String[] args) {

ServerSocket serverSocket = null;

try {

serverSocket = new ServerSocket(8877);

System.out.println("多线程服务器已启动,监听端口8877...");

while (true) { // 循环监听新的客户端连接

// 阻塞等待客户端连接

Socket socket = serverSocket.accept();

System.out.println("客户端[" + socket.getInetAddress() + "]已连接");

// 为每个客户端创建独立线程处理通信

new Thread(new ClientHandler(socket)).start();

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (serverSocket != null) serverSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

3.3 运行效果

启动 `ThreadSocketServer` 后,可同时启动多个 `SocketClient`,服务器会为每个客户端创建子线程,独立处理消息,实现并发通信。

问题:

  1. 每次接受到客户端发来的数据,就会创建一个新的线程,线程的创建和销毁都会消耗计算机资源
  2. 客户端的访问量增多时,服务器端和客户端的线程比是1:1,访问量大则线程增多,线程多了可能导致当前线程创建失败最终导致服务器宕机

四、线程池优化:高效管理线程资源

线程池可以复用线程,避免频繁创建和销毁线程的开销,同时控制并发线程数,是高并发场景下 Socket 服务器的最优解。

4.1 线程池版

4.1.1服务器端

java 复制代码
package qcby;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
/**
 * 服务器端
 */
public class Server {
    // 服务器监听的端口(常量建议用基本类型,避免空指针)
    public static final int SERVER_PORT = 4477;

    public static void main(String[] args) throws Exception {
        // 创建服务器端
        ServerSocket serverSocket = new ServerSocket(SERVER_PORT);//创建服务器端
        System.out.println("服务器启动成功,等待客户端连接...");
        //定义线程池
        HandleSocketServerPool socketServerPool = new HandleSocketServerPool(5,
                10,10,120, TimeUnit.SECONDS);
        while (true) {
            Socket socket = serverSocket.accept(); //阻塞监听
            Runnable task = new ServerThreadReader(socket); //将socket对象封装成任务
            socketServerPool.execute(task); //提交任务,让线程池执行
        }
    }
}

4.1.2创建线程池

java 复制代码
package qcby;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//创建线程池(固定)
public class HandleSocketServerPool {
    private ExecutorService executorService; //线程池对象 jdk提供的线程池对象
    /**
     * 创建线程池
     * @param corePoolSize
     * @param maxThreadNum
     * @param queueSize
     * @param keepAliveTime
     * @param unit
     */
    public HandleSocketServerPool(Integer corePoolSize, int maxThreadNum, int queueSize,
                                  Integer keepAliveTime, TimeUnit unit){
        executorService = new ThreadPoolExecutor(
                corePoolSize,
                maxThreadNum,
                keepAliveTime,
                unit,
                new ArrayBlockingQueue<Runnable>(queueSize)
        );
    }

    /**
     * 任务提交的方法
     * @param task
     */
    public void execute(Runnable task){
        executorService.execute(task);
    }
}

4.2 线程池核心优势

  1. 线程复用:线程池中的线程处理完一个客户端后,会被复用处理下一个客户端,避免频繁创建/销毁线程的性能损耗;
  2. 并发控制:通过`newFixedThreadPool(10)`设置核心线程数为10,限制最大并发线程数,防止系统资源耗尽;
  3. 任务队列:当客户端连接数超过线程池核心数时,新任务会进入队列等待,保证服务的稳定性。

4.3线程池执行示意图


五、总结:Socket 通信的优化演进

从基础的单客户端通信到线程池优化,Socket 服务器的演进核心是解决并发问题,具体流程如下:

  1. 基础版:仅支持单客户端,服务器处理完一个连接后关闭,适用于入门学习;
  2. 子线程版:为每个客户端创建独立线程,实现多客户端并发,但高并发下线程资源开销大;
  3. 线程池版:通过线程池复用线程,控制并发数,是生产环境中高并发 Socket 服务器的标准实现。

Socket 是 Java 网络编程的基础,无论是 Tomcat 等 Web 服务器,还是即时通讯、文件传输等网络应用,底层都基于 Socket 实现。掌握 Socket 通信的核心逻辑与优化方式,能为后续学习分布式、微服务等高级技术奠定基础。

|------|---------------|------------------|---------------------|
| 版本类型 | 核心实现方式 | 优势 | 不足 |
| 基础版 | 单线程处理单个客户端连接 | 代码简单,易理解 | 仅支持单客户端,无并发能力 |
| 子线程版 | 为每个客户端创建独立子线程 | 实现多客户端并发通信 | 高并发下线程资源开销大、上下文切换频繁 |
| 线程池版 | 线程池复用线程,控制并发数 | 线程资源可控、复用性高、性能稳定 | 需手动配置线程池参数 |

下面这个链接是我的有道云笔记http://【有道云笔记】12.2day13手写tomcat SOCKET重要 https://share.note.youdao.com/s/3yzLe2O5

相关推荐
2201_7578308732 分钟前
反射的概念
java·开发语言
Pocker_Spades_A33 分钟前
DeepCore:大模型统一网关,Claude 免费体验与跨模型技术洞察
java·服务器·数据库
Arva .37 分钟前
Spring Boot自动配置原理
java·spring boot·后端
寻星探路39 分钟前
Java EE初阶启程记15---文件操作和IO
java·java-ee
阿巳helloWorld39 分钟前
SpringMVC底层流程解析
java·开发语言
heartbeat..40 分钟前
介绍java中常用于处理 Excel 文件的Apache POI
java·apache·excel·poi
路边草随风41 分钟前
java 实现 flink 读 kafka 写 iceberg
java·flink·kafka
路边草随风42 分钟前
java 实现 flink cdc 读 mysql binlog 按表写入kafka不同topic
java·大数据·mysql·flink
jinxinyuuuus42 分钟前
局域网文件传输:WebSockets信令、ICE协议栈与P2P连接的生命周期管理
服务器·网络协议·p2p