
在Java TCP通信中,常见的需求是客户端和服务器之间能够持续交换数据,直到一方发出终止信号。然而,初学者在实现此类功能时,常遇到只能读取一行数据后通信中断的问题。这通常是由于服务器端的循环逻辑不正确导致的。
考虑以下初始的客户端和服务器代码:
客户端 (Klient.java)
import java.io.*;
import java.net.*;
public class Klient {
public static void main(String[] args) throws UnknownHostException, IOException {
String sentence;
String modifiedSentence;
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
Socket clientSocket = new Socket("127.0.0.1", 6789);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while (true){ // 客户端尝试持续发送和接收
sentence = inFromUser.readLine();
outToServer.writeBytes(sentence + '\n');
modifiedSentence = inFromServer.readLine(); // 问题可能发生在这里
System.out.println(modifiedSentence);
}
// clientSocket.close(); // 未执行的关闭操作
}
}服务器 (TCPServer.java)
立即学习“Java免费学习笔记(深入)”;
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) throws IOException {
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);
while (true){ // 服务器外层循环
Socket connectionSocket = welcomeSocket.accept(); // 每次循环都接受新连接
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
clientSentence = inFromClient.readLine(); // 只读取一行
capitalizedSentence = clientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence);
// 连接在处理完一行后未被持续使用,而是等待下一个新连接
}
}
}上述服务器代码的问题在于,while (true)循环的每次迭代都会调用 welcomeSocket.accept() 来接受一个新的客户端连接。这意味着,在处理完一个客户端发送的一行数据并返回响应后,服务器会立即回到循环的开始,并等待 新的 客户端连接。对于当前的客户端而言,服务器在处理完第一行后就“不再理会”它,导致客户端尝试读取后续响应时可能阻塞或遇到错误,因为它所连接的服务器端逻辑已经转去等待新连接了。
为了使服务器能够与单个客户端进行持续通信,我们需要在接受连接后引入一个内部循环来处理该客户端的所有消息,直到该客户端发出终止信号。
服务器应该在接受一个连接后,在一个新的内部循环中持续读取来自该客户端的数据,直到收到特定的“停止”指令。
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) throws IOException {
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);
System.out.println("服务器已启动,等待客户端连接...");
while (true) { // 外层循环:接受新的客户端连接
Socket connectionSocket = welcomeSocket.accept();
System.out.println("客户端 " + connectionSocket.getInetAddress() + ":" + connectionSocket.getPort() + " 已连接。");
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
// 内部循环:处理当前客户端的所有消息
while (true) {
clientSentence = inFromClient.readLine(); // 持续读取客户端发送的行
if (clientSentence == null) { // 客户端关闭连接时 readLine() 返回 null
System.out.println("客户端 " + connectionSocket.getInetAddress() + " 已断开连接。");
break; // 退出内部循环,处理下一个客户端
}
if (clientSentence.equalsIgnoreCase("stop")) { // 检查终止指令
System.out.println("收到客户端 " + connectionSocket.getInetAddress() + " 的终止指令 'stop'。");
outToClient.writeBytes("服务器已收到停止指令,连接关闭。\n"); // 可选:发送确认消息
connectionSocket.close(); // 关闭当前客户端连接
break; // 退出内部循环,等待下一个客户端
}
capitalizedSentence = clientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence);
System.out.println("收到: " + clientSentence + ", 发送: " + capitalizedSentence.trim());
}
}
// welcomeSocket.close(); // 如果服务器需要完全终止,则在此处关闭ServerSocket
}
}说明:
如果服务器的设计是只处理一个客户端会话,然后就完全终止,可以移除外层 while(true) 循环,并在处理完客户端后关闭 welcomeSocket。
import java.io.*;
import java.net.*;
public class TCPServerSingleClient {
public static void main(String[] args) throws IOException {
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);
System.out.println("服务器已启动,等待单个客户端连接...");
try {
Socket connectionSocket = welcomeSocket.accept();
System.out.println("客户端 " + connectionSocket.getInetAddress() + ":" + connectionSocket.getPort() + " 已连接。");
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
while (true) { // 内部循环:处理当前客户端的所有消息
clientSentence = inFromClient.readLine();
if (clientSentence == null || clientSentence.equalsIgnoreCase("stop")) {
System.out.println("收到客户端 " + connectionSocket.getInetAddress() + " 的终止指令或连接已断开。");
outToClient.writeBytes("服务器已收到停止指令或连接已断开,会话结束。\n");
connectionSocket.close();
break;
}
capitalizedSentence = clientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence);
System.out.println("收到: " + clientSentence + ", 发送: " + capitalizedSentence.trim());
}
} finally {
welcomeSocket.close(); // 确保ServerSocket在所有操作完成后关闭
System.out.println("服务器已关闭。");
}
}
}客户端也需要相应的修改,以便在服务器关闭连接时能够检测到并优雅地退出循环。BufferedReader.readLine() 方法在流的末尾(即服务器关闭连接)时会返回 null。
import java.io.*;
import java.net.*;
public class Klient {
public static void main(String[] args) throws UnknownHostException, IOException {
String sentence;
String modifiedSentence;
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
Socket clientSocket = null; // 初始化为 null
try {
clientSocket = new Socket("127.0.0.1", 6789);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("已连接到服务器。输入消息,输入 'stop' 结束会话。");
while (true) {
System.out.print("请输入消息: ");
sentence = inFromUser.readLine(); // 读取用户输入
outToServer.writeBytes(sentence + '\n'); // 发送给服务器
if (sentence.equalsIgnoreCase("stop")) { // 如果用户输入 "stop",则客户端也准备退出
System.out.println("发送 'stop' 指令,准备关闭连接...");
break; // 退出循环
}
modifiedSentence = inFromServer.readLine(); // 读取服务器响应
if (modifiedSentence == null) { // 如果服务器关闭连接,readLine() 返回 null
System.out.println("服务器已关闭连接。");
break; // 退出循环
}
System.out.println("来自服务器: " + modifiedSentence);
}
} catch (IOException e) {
System.err.println("客户端发生错误: " + e.getMessage());
} finally {
if (clientSocket != null && !clientSocket.isClosed()) {
clientSocket.close(); // 确保关闭客户端套接字
System.out.println("客户端连接已关闭。");
}
}
}
}说明:
通过上述优化,我们的Java TCP客户端-服务器应用现在能够实现持续的双向通信,并且能够通过发送特定指令("stop")或检测连接关闭来优雅地终止会话。
关键点回顾:
遵循这些原则,可以构建出更健壮、更专业的TCP通信应用程序。
以上就是Java TCP通信中实现持续数据读写及优雅终止连接的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号