[一、什么是 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.2 线程池核心优势](#4.2 线程池核心优势)
[五、总结: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 运行说明
- 先启动 SocketServer.java,服务器进入阻塞状态,等待客户端连接;
- 再启动 SocketClient.java,客户端与服务器建立连接并发送消息;
- 服务器控制台会打印「收到客户端消息: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: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 线程池核心优势
- 线程复用:线程池中的线程处理完一个客户端后,会被复用处理下一个客户端,避免频繁创建/销毁线程的性能损耗;
- 并发控制:通过`newFixedThreadPool(10)`设置核心线程数为10,限制最大并发线程数,防止系统资源耗尽;
- 任务队列:当客户端连接数超过线程池核心数时,新任务会进入队列等待,保证服务的稳定性。
4.3线程池执行示意图

五、总结:Socket 通信的优化演进
从基础的单客户端通信到线程池优化,Socket 服务器的演进核心是解决并发问题,具体流程如下:
- 基础版:仅支持单客户端,服务器处理完一个连接后关闭,适用于入门学习;
- 子线程版:为每个客户端创建独立线程,实现多客户端并发,但高并发下线程资源开销大;
- 线程池版:通过线程池复用线程,控制并发数,是生产环境中高并发 Socket 服务器的标准实现。
Socket 是 Java 网络编程的基础,无论是 Tomcat 等 Web 服务器,还是即时通讯、文件传输等网络应用,底层都基于 Socket 实现。掌握 Socket 通信的核心逻辑与优化方式,能为后续学习分布式、微服务等高级技术奠定基础。
|------|---------------|------------------|---------------------|
| 版本类型 | 核心实现方式 | 优势 | 不足 |
| 基础版 | 单线程处理单个客户端连接 | 代码简单,易理解 | 仅支持单客户端,无并发能力 |
| 子线程版 | 为每个客户端创建独立子线程 | 实现多客户端并发通信 | 高并发下线程资源开销大、上下文切换频繁 |
| 线程池版 | 线程池复用线程,控制并发数 | 线程资源可控、复用性高、性能稳定 | 需手动配置线程池参数 |
下面这个链接是我的有道云笔记http://【有道云笔记】12.2day13手写tomcat SOCKET重要 https://share.note.youdao.com/s/3yzLe2O5