0

0

Java TCP通信:实现连续数据传输与“停止”指令控制

碧海醫心

碧海醫心

发布时间:2025-09-25 10:00:21

|

185人浏览过

|

来源于php中文网

原创

Java TCP通信:实现连续数据传输与“停止”指令控制

本文详细阐述了如何在Java TCP客户端-服务器架构中实现连续的数据传输,并引入了基于特定指令(如“stop”)的优雅连接终止机制。通过修正服务器端循环逻辑和客户端对连接关闭的判断,确保双方能高效通信并按需关闭连接,避免单次通信后连接意外中断或服务器僵死。

java中构建tcp客户端-服务器应用程序时,实现连续的数据交换直至特定条件满足(例如收到“stop”指令或连接关闭)是一个常见的需求。原始代码中存在一个常见误区,即服务器在处理完一个客户端请求后,外层循环会立即尝试接受新的客户端连接,而非持续与当前客户端通信。这导致服务器无法处理同一客户端的后续请求,而客户端则会因服务器未响应而挂起。

服务器端逻辑优化

原服务器代码的 while(true) 循环用于不断接受新的客户端连接。一旦 welcomeSocket.accept() 返回一个 Socket 对象,服务器会读取一行数据并发送响应,然后立即回到 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());

            BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());

            // 内层循环:与当前客户端进行持续通信
            while (true) {
                try {
                    clientSentence = inFromClient.readLine();
                    if (clientSentence == null) { // 客户端关闭连接
                        System.out.println("客户端断开连接:" + connectionSocket.getInetAddress());
                        break; // 跳出内层循环,等待新客户端
                    }

                    System.out.println("收到来自客户端[" + connectionSocket.getInetAddress() + "]的消息: " + clientSentence);

                    if (clientSentence.equalsIgnoreCase("stop")) {
                        System.out.println("收到'stop'指令,关闭当前客户端连接。");
                        outToClient.writeBytes("Server received 'stop'. Connection closing.\n"); // 可选:通知客户端
                        break; // 收到"stop"指令,跳出内层循环,关闭当前连接
                    }

                    capitalizedSentence = clientSentence.toUpperCase() + '\n';
                    outToClient.writeBytes(capitalizedSentence);
                } catch (SocketException e) {
                    System.out.println("客户端连接异常断开:" + connectionSocket.getInetAddress() + " - " + e.getMessage());
                    break; // 捕获连接异常,跳出内层循环
                } catch (IOException e) {
                    System.err.println("读取或写入客户端数据时发生错误:" + e.getMessage());
                    break; // 其他IO错误,跳出内层循环
                }
            }
            // 关闭当前客户端连接的资源
            connectionSocket.close();
            System.out.println("客户端连接已关闭。");
        }
        // 注意:如果服务器需要完全终止,则 welcomeSocket 也需要关闭,但通常服务器会一直运行。
        // welcomeSocket.close();
    }
}

服务器端逻辑说明:

  1. 外层循环 (while(true) for welcomeSocket.accept()): 负责接受新的客户端连接。
  2. 内层循环 (while(true) for inFromClient.readLine()): 负责与当前已连接的客户端进行持续的数据交换。
  3. “stop”指令处理: 在内层循环中,如果 clientSentence 等于(忽略大小写)"stop",服务器会发送一个可选的确认消息给客户端,然后 break 出内层循环,从而关闭当前的 connectionSocket。外层循环会继续执行,等待下一个客户端连接。
  4. 客户端断开检测: inFromClient.readLine() 返回 null 表示客户端已关闭其输出流(即连接已关闭)。此时服务器也应 break 内层循环并关闭 connectionSocket。
  5. 异常处理: 增加了 try-catch 块来处理 SocketException 和其他 IOException,确保在客户端异常断开时服务器能优雅地处理,避免程序崩溃。

客户端逻辑优化

客户端需要持续发送用户输入,并接收服务器的响应。关键在于如何检测服务器何时关闭了连接,以便客户端也能优雅地终止。当服务器关闭其输出流时,客户端的 inFromServer.readLine() 方法将返回 null。

立即学习Java免费学习笔记(深入)”;

北极象沉浸式AI翻译
北极象沉浸式AI翻译

免费的北极象沉浸式AI翻译 - 带您走进沉浸式AI的双语对照体验

下载

修正后的客户端代码示例:

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;
        DataOutputStream outToServer = null;
        BufferedReader inFromServer = null;

        try {
            clientSocket = new Socket("127.0.0.1", 6789);
            outToServer = new DataOutputStream(clientSocket.getOutputStream());
            inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            System.out.println("已连接到服务器。输入消息,输入'stop'结束会话。");

            // 客户端持续发送和接收数据
            while (true) {
                System.out.print("请输入消息: ");
                sentence = inFromUser.readLine();

                if (sentence == null) { // 用户输入流关闭(如Ctrl+D),终止客户端
                    System.out.println("用户输入流已关闭,客户端即将退出。");
                    break;
                }

                outToServer.writeBytes(sentence + '\n'); // 发送数据到服务器

                // 接收服务器响应
                modifiedSentence = inFromServer.readLine();
                if (modifiedSentence == null) { // 服务器关闭了连接
                    System.out.println("服务器已关闭连接。客户端即将退出。");
                    break;
                }
                System.out.println("来自服务器的响应: " + modifiedSentence);

                if (sentence.equalsIgnoreCase("stop")) {
                    System.out.println("已发送'stop'指令,等待服务器响应并关闭连接。");
                    // 理论上,服务器收到'stop'后会关闭连接,导致下一次readLine返回null
                    // 这里的break是多余的,但为了清晰性可以保留,或者依赖modifiedSentence == null来退出
                    break; 
                }
            }
        } catch (ConnectException e) {
            System.err.println("无法连接到服务器: " + e.getMessage());
        } catch (SocketException e) {
            System.err.println("网络连接异常: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("客户端IO操作发生错误: " + e.getMessage());
        } finally {
            // 确保关闭所有资源
            try {
                if (inFromUser != null) inFromUser.close();
                if (outToServer != null) outToServer.close();
                if (inFromServer != null) inFromServer.close();
                if (clientSocket != null) clientSocket.close();
                System.out.println("客户端资源已释放。");
            } catch (IOException e) {
                System.err.println("关闭客户端资源时发生错误: " + e.getMessage());
            }
        }
    }
}

客户端逻辑说明:

  1. 持续通信循环: while(true) 循环负责从用户读取输入,发送到服务器,并接收服务器的响应。
  2. 检测服务器关闭: inFromServer.readLine() 返回 null 是服务器关闭连接的明确信号。客户端检测到此情况后,应 break 出循环并关闭自身资源。
  3. 发送“stop”指令: 客户端发送“stop”指令后,通常会等待服务器响应(可能是一个确认消息),然后服务器会关闭连接。客户端会通过 inFromServer.readLine() == null 检测到服务器的关闭,从而退出循环。
  4. 资源管理: 使用 try-catch-finally 结构确保无论发生何种异常或正常退出,所有的输入/输出流和 Socket 资源都能被正确关闭,防止资源泄露。

服务器完全终止的场景

如果服务器不仅要关闭当前客户端连接,而且在收到“stop”指令后要完全终止运行(即不再接受任何新的连接),则需要修改服务器的结构,移除外层 while(true) 循环,并在处理完一个客户端后关闭 ServerSocket。

服务器完全终止代码示例:

import java.io.*;
import java.net.*;

public class SingleClientTerminatingTCPServer {
    public static void main(String[] args) throws IOException {
        String clientSentence;
        String capitalizedSentence;
        ServerSocket welcomeSocket = null;
        Socket connectionSocket = null;

        try {
            welcomeSocket = new ServerSocket(6789);
            System.out.println("服务器已启动,等待单个客户端连接...");

            // 接受一个客户端连接
            connectionSocket = welcomeSocket.accept();
            System.out.println("客户端已连接:" + connectionSocket.getInetAddress());

            BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());

            // 与当前客户端进行持续通信
            while (true) {
                try {
                    clientSentence = inFromClient.readLine();
                    if (clientSentence == null) {
                        System.out.println("客户端断开连接:" + connectionSocket.getInetAddress());
                        break;
                    }

                    System.out.println("收到来自客户端[" + connectionSocket.getInetAddress() + "]的消息: " + clientSentence);

                    if (clientSentence.equalsIgnoreCase("stop")) {
                        System.out.println("收到'stop'指令,服务器即将完全终止。");
                        outToClient.writeBytes("Server received 'stop'. Server terminating.\n");
                        break; // 收到"stop"指令,跳出内层循环
                    }

                    capitalizedSentence = clientSentence.toUpperCase() + '\n';
                    outToClient.writeBytes(capitalizedSentence);
                } catch (SocketException e) {
                    System.out.println("客户端连接异常断开:" + connectionSocket.getInetAddress() + " - " + e.getMessage());
                    break;
                } catch (IOException e) {
                    System.err.println("读取或写入客户端数据时发生错误:" + e.getMessage());
                    break;
                }
            }
        } catch (IOException e) {
            System.err.println("服务器启动或运行过程中发生错误: " + e.getMessage());
        } finally {
            // 确保关闭所有资源
            try {
                if (connectionSocket != null) connectionSocket.close();
                if (welcomeSocket != null) welcomeSocket.close();
                System.out.println("服务器资源已释放,服务器已关闭。");
            } catch (IOException e) {
                System.err.println("关闭服务器资源时发生错误: " + e.getMessage());
            }
        }
    }
}

总结与注意事项

  • 循环结构是关键: 服务器端需要一个外层循环来接受新连接,一个内层循环来处理与单个客户端的持续通信。如果服务器只处理一个客户端并随后终止,则可以省略外层循环。
  • 优雅终止: 客户端通过 readLine() 返回 null 来检测服务器的关闭,服务器通过检查特定指令(如“stop”)或 readLine() 返回 null 来检测客户端的意图或断开。
  • 资源管理: 始终使用 try-catch-finally 结构来确保所有 Socket 和流资源在不再需要时被正确关闭,这是避免资源泄露和程序不稳定的最佳实践。
  • 异常处理: 网络通信中,各种异常(如 ConnectException, SocketException, IOException)很常见。妥善的异常处理能够提高程序的健壮性。
  • 协议设计: 在实际应用中,通信双方需要约定一个明确的协议,包括如何发送数据、如何识别命令、如何处理错误以及如何终止连接。本教程中的“stop”指令即是一个简单的协议示例。
  • 并发考虑: 上述服务器示例是单线程的,一次只能处理一个客户端。对于需要同时处理多个客户端的场景,需要引入多线程或线程池机制来为每个 connectionSocket 分配一个独立的线程进行处理。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

837

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

741

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

736

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

3

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

C# 教程
C# 教程

共94课时 | 7万人学习

Java 教程
Java 教程

共578课时 | 47.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号