Java TCP 客户端/服务器开发深度解析
一、客户端代码全注释
java
public class TcpClient {
private Socket socket = null; // TCP通信核心对象
// 构造函数:建立TCP连接
public TcpClient(String ip, int port) throws IOException {
// 创建Socket时会自动进行三次握手
socket = new Socket(ip, port); // 参数1: 服务器IP 参数2: 端口号
}
public void start() throws IOException {
System.out.println("客户端启动");
// try-with-resources 自动关闭资源
try (InputStream in = socket.getInputStream(); // 接收数据的字节流
OutputStream out = socket.getOutputStream(); // 发送数据的字节流
Scanner scannerConsole = new Scanner(System.in); // 控制台输入
Scanner scannerIn = new Scanner(in)) { // 网络输入解析
while (true) {
// 1. 控制台输入处理
System.out.print("-> ");
String request = scannerConsole.next(); // 阻塞等待用户输入
// 2. 发送请求到服务器
PrintWriter printWriter = new PrintWriter(out); // 包装输出流
printWriter.println(request); // 自动添加换行符
printWriter.flush(); // 强制立即发送缓冲区内容
// 3. 接收服务器响应
if (!scannerIn.hasNext()) break; // 检测连接是否关闭
String response = scannerIn.next();
System.out.println("服务器响应: " + response);
}
} // 自动关闭所有资源:socket、流、Scanner
}
}
客户端核心三要素:
- 连接建立 :通过
new Socket()
完成TCP三次握手 - 数据通道 :
getInputStream()
获取接收数据流getOutputStream()
获取发送数据流
- 资源管理:使用try-with-resources自动释放连接
二、服务器代码全注释
java
public class TcpServer {
private ServerSocket serverSocket = null; // 服务端监听套接字
// 初始化服务器
public TcpServer(int port) throws IOException {
serverSocket = new ServerSocket(port); // 绑定指定端口
System.out.println("服务器监听端口: " + port);
}
public void start() throws IOException {
System.out.println("启动服务器主线程");
while (true) {
// 阻塞等待客户端连接(核心方法)
Socket clientSocket = serverSocket.accept();
System.out.println("检测到新连接");
// 为每个客户端创建独立线程
new Thread(() -> {
try {
processConnection(clientSocket); // 处理客户端业务
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
// 处理单个客户端连接
private void processConnection(Socket clientSocket) throws IOException {
// 获取客户端地址信息
String clientInfo = String.format("[%s:%d]",
clientSocket.getInetAddress(),
clientSocket.getPort());
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
Scanner scanner = new Scanner(inputStream);
System.out.println(clientInfo + " 客户端上线");
while (scanner.hasNext()) { // 持续监听客户端请求
// 1. 读取请求
String request = scanner.next();
// 2. 处理业务逻辑
String response = process(request);
// 3. 返回响应
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(response);
printWriter.flush(); // 确保数据立即发送
System.out.println(clientInfo + " 处理请求: " + request);
}
}
System.out.println(clientInfo + " 客户端下线");
}
// 业务处理(示例直接返回原字符串)
private String process(String request) {
return request.toUpperCase(); // 示例:转为大写
}
}
服务器核心三要素:
- 端口监听 :
ServerSocket
绑定指定端口 - 连接接收 :
accept()
方法阻塞等待连接 - 并发处理:为每个客户端创建独立线程
三、关键方法深度解析
3.1 accept()
方法详解
java
Socket clientSocket = serverSocket.accept();
- 阻塞特性:在没有客户端连接时,线程会在此处挂起
- 返回对象 :返回的
Socket
对象包含:- 客户端IP地址(
getInetAddress()
) - 客户端端口(
getPort()
) - 新的数据通道(输入/输出流)
- 客户端IP地址(
- 并发基础:每次accept返回新的Socket,实现多客户端处理
3.2 flush()
的必要性
java
printWriter.flush();
场景 | 无flush | 有flush |
---|---|---|
数据传输时机 | 缓冲区满时自动发送 | 立即强制发送 |
适用场景 | 大数据量传输 | 需要实时交互的场景 |
风险点 | 数据可能长时间滞留 | 增加网络包数量 |
缓冲区原理:
客户端内存:[数据1][数据2][数据3] → 缓冲区填满后自动发送
手动flush: [数据1] → 立即发送 → [数据2] → 立即发送
四、TCP核心组件对比
4.1 ServerSocket vs Socket
特性 | ServerSocket | Socket |
---|---|---|
用途 | 服务端监听端口 | 建立实际数据通道 |
创建时机 | 服务启动时创建 | accept()成功后自动创建 |
生命周期 | 整个服务运行期间存在 | 单个连接周期内存在 |
主要方法 | accept(), bind(), close() | getInputStream(), close() |
4.2 TCP通信全流程
客户端 服务器 SYN SYN-ACK ACK 三次握手完成 发送数据(request) 返回响应(response) FIN ACK FIN ACK 四次挥手断开 客户端 服务器
五、IntelliJ多实例运行配置
5.1 配置步骤演示
-
打开运行配置
shellRun -> Edit Configurations
-
启用多实例
shell选择客户端配置 -> Modify options -> Allow multiple instances
-
运行效果
shell可以同时启动多个客户端进程 每个进程独立连接到服务器
5.2 实现原理
-
JVM进程隔离:每个客户端运行在独立JVM中
-
端口重用:客户端使用临时端口(1024-65535)
-
服务端设计 :
javanew Thread(() -> processConnection(clientSocket)).start();
每个连接使用独立线程处理
六、最佳实践建议
-
资源关闭顺序
java// 正确关闭顺序示例 try (Socket socket = new Socket(...); OutputStream out = socket.getOutputStream(); InputStream in = socket.getInputStream()) { // 使用资源 } // 自动反向关闭:in -> out -> socket
-
异常处理规范
javacatch (IOException e) { System.err.println("连接异常: " + e.getMessage()); // 需要手动关闭资源 try { if (socket != null) socket.close(); } catch (IOException ex) { ex.printStackTrace(); } }
-
性能优化技巧
- 使用线程池代替裸线程
- 设置合理的Socket超时时间
- 采用NIO非阻塞模式处理高并发
通过深入理解TCP通信机制和Java网络API的实现原理,开发者可以构建出稳定高效的网络应用程序。多线程服务器的设计需要特别注意资源竞争和线程安全问题,在实际生产环境中建议使用ExecutorService
等高级并发工具进行线程管理。