Java案例如何实现TCP通信?完整指南与代码实战
目录导读
TCP通信核心原理与Java实现基础
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议,在Java中实现TCP通信主要依赖java.net包下的ServerSocket和Socket类。核心逻辑是:服务端创建ServerSocket监听固定端口,客户端通过Socket连接服务端IP和端口,建立连接后双方通过输入输出流进行数据交换。

关键类说明:
ServerSocket:运行在服务端,用于监听客户端连接请求Socket:代表通信端点,客户端和服务端各持有一个InputStream/OutputStream:用于读取和写入数据
服务端实现:ServerSocket全面解析
1 基础服务端代码
import java.io.*;
import java.net.*;
public class TcpServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("服务端已启动,等待客户端连接...");
Socket socket = serverSocket.accept(); // 阻塞等待连接
System.out.println("客户端已连接:" + socket.getInetAddress());
// 获取输入流读取客户端数据
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 获取输出流发送数据给客户端
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream()), true);
String line;
while ((line = reader.readLine()) != null) {
System.out.println("收到客户端消息:" + line);
writer.println("服务端已收到:" + line); // 回复确认
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
关键点解析:
accept()方法会阻塞直到有客户端连接BufferedReader包装InputStreamReader实现按行读取PrintWriter的autoFlush参数设为true确保数据立即发送
2 多线程服务端设计
为了处理多个客户端,需要为每个连接创建新线程:
public class MultiThreadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new ClientHandler(socket)).start();
}
}
}
class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) { this.socket = socket; }
@Override
public void run() {
// 处理逻辑与单客户端相同
}
}
客户端实现:Socket连接与数据交互
1 基础客户端代码
import java.io.*;
import java.net.*;
public class TcpClient {
public static void main(String[] args) {
try (Socket socket = new Socket("127.0.0.1", 8888)) {
// 获取输入输出流
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream()), true);
// 发送消息并接收服务端回复
writer.println("Hello Server!");
String response = reader.readLine();
System.out.println("服务端回复:" + response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
连接注意事项:
- 确保服务端IP地址和端口号正确
0.0.1表示本机,实际部署需替换为服务器IP- 使用try-with-resources自动关闭资源
2 命令行交互客户端
实现用户输入消息并实时发送:
public class InteractiveClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 8888);
BufferedReader console = new BufferedReader(
new InputStreamReader(System.in));
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
String userInput;
while ((userInput = console.readLine()) != null) {
writer.println(userInput);
System.out.println("服务端回复:" + reader.readLine());
if ("bye".equalsIgnoreCase(userInput)) break;
}
}
}
完整案例:多线程聊天程序
1 服务端代码(支持多客户端广播)
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
private static Set<PrintWriter> clients = new HashSet<>();
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("聊天服务端启动,端口:12345");
while (true) {
Socket socket = serverSocket.accept();
new Thread(() -> handleClient(socket)).start();
}
}
private static void handleClient(Socket socket) {
try {
PrintWriter writer = new PrintWriter(
socket.getOutputStream(), true);
clients.add(writer);
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String message;
while ((message = reader.readLine()) != null) {
System.out.println("收到消息:" + message);
// 广播给所有客户端
for (PrintWriter client : clients) {
client.println(message);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 清理资源
clients.remove(writer);
}
}
}
2 客户端代码(支持消息收发)
import java.io.*;
import java.net.*;
public class ChatClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 12345);
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(">> " + line);
}
} catch (IOException e) { e.printStackTrace(); }
}).start();
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader console = new BufferedReader(
new InputStreamReader(System.in));
String userInput;
while ((userInput = console.readLine()) != null) {
writer.println(userInput);
}
}
}
案例特点:
- 服务端维护客户端列表实现广播
- 客户端两个线程:一个读取键盘输入,一个读取服务端消息
- 注意:此案例为简化版,实际生产需考虑线程安全和异常处理
常见问题问答(FAQ)
Q1:TCP通信中服务端如何处理多个客户端?
A:使用多线程模型,为每个客户端连接创建一个新的线程,推荐使用线程池优化性能,避免无限创建线程导致资源耗尽,核心代码是ExecutorService executor = Executors.newFixedThreadPool(10);,将客户端处理任务提交给线程池。
Q2:为什么客户端无法连接服务端?
A:常见原因包括:
- 服务端未启动或端口被占用(可使用
netstat -ano检查) - 防火墙阻止了该端口通信
- IP地址配置错误(本地测试用
0.0.1或localhost) - 服务端
accept()前客户端已连接(需确保服务端先运行)
Q3:发送中文数据出现乱码怎么办?
A:统一字符编码,推荐使用UTF-8,修改代码:
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"));
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
Q4:如何解决客户端断线后服务端报错问题?
A:在读取循环中添加异常捕获,并在finally块中清理资源,同时服务端应移除已断开的客户端输出流:
try {
// 读取逻辑
} catch (SocketException e) {
System.out.println("客户端断开连接");
} finally {
clients.remove(writer);
// 关闭socket
}
Q5:TCP通信与UDP通信如何选择?
A:TCP适用于需要可靠传输的场景,如文件传输、网页访问、聊天消息,UDP适用于实时性要求高、可以容忍少量丢包的场景,如视频直播、游戏帧同步,Java中UDP通信使用DatagramSocket和DatagramPacket类。
拓展阅读:实际生产环境中,建议使用Netty框架简化TCP通信开发,它提供了完善的线程模型和编解码器,可显著提升开发效率和系统性能,对于企业级应用,还需考虑心跳检测、消息序列化、断线重连等高级特性。