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免费学习笔记(深入)”;

Yodayo
Yodayo

一个专为动漫迷和vTuber打造的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 分配一个独立的线程进行处理。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

254

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1089

2024.03.01

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

107

2023.09.25

java中break的作用
java中break的作用

本专题整合了java中break的用法教程,阅读专题下面的文章了解更多详细内容。

120

2025.10.15

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

261

2025.10.24

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

377

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

32

2026.01.21

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

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

精品课程

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

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.2万人学习

Java 教程
Java 教程

共578课时 | 81.3万人学习

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

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