0

0

Java Socket通信中循环读取数据时阻塞问题解析与最佳实践

碧海醫心

碧海醫心

发布时间:2025-11-07 19:23:01

|

811人浏览过

|

来源于php中文网

原创

Java Socket通信中循环读取数据时阻塞问题解析与最佳实践

本文深入探讨java服务器-客户端应用在循环读取utf数据时可能遇到的阻塞问题。通过分析一个具体的代码案例,揭示了将system.in的scanner与网络输入流混用是导致程序意外挂起的主要原因。文章将提供详细的解决方案和避免此类问题的最佳实践,以确保网络通信的顺畅进行。

1. 问题描述:服务器循环阻塞现象

在构建Java服务器-客户端应用程序时,开发者常会遇到各种I/O和并发问题。一个常见且容易被忽视的问题是,服务器在与客户端进行网络通信的循环过程中意外阻塞,导致程序挂起。具体表现为,服务器在从客户端接收一系列任务的描述和状态时,在处理完第一个或第二个任务后,不再继续接收后续任务,而是陷入无限等待状态。

这种现象通常发生在服务器端期望通过网络套接字读取客户端发送的数据,但实际上却在等待本地标准输入(如控制台输入)时。

2. 代码分析:定位阻塞根源

为了深入理解问题,我们分析一个典型的服务器-客户端任务管理应用。服务器(Server.java)旨在接收客户端(Client.java)指定数量的任务,并为每项任务收集描述和状态。

服务器端关键代码片段:

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

// Server.java
public class Server {
    // ... 其他成员变量
    ArrayList tarea = new ArrayList();
    Scanner sc = new Scanner(System.in); // 问题点1:在服务器代码中创建了System.in的Scanner

    public void iniciarServidor() throws IOException {
        while (true) {
            // ... 接受客户端连接,获取输入输出流
            DataOutputStream output = new DataOutputStream(socket.getOutputStream());
            DataInputStream input = new DataInputStream(socket.getInputStream());

            // ... 与客户端交互,获取任务数量
            output.writeUTF("Muy buenas " + nombre + " especifique numero de tareas:");
            int numeroTareas = input.readInt();

            for(int i = 0; i < numeroTareas; i++){                                                        
                output.writeUTF("Especifique descripcion para la tarea "+i+" :");
                String descripcion = input.readUTF(); // 服务器等待客户端发送描述
                output.writeUTF("Especifique el estado 'true o false' para la tarea "+i+" :");
                boolean estado = input.readBoolean(); // 服务器等待客户端发送状态

                Tarea t = new Tarea(descripcion, estado);  
                tarea.add(t);
            }

            sc.nextLine(); // 问题点2:服务器在此等待本地控制台输入,导致阻塞
            // ... 后续发送任务列表给客户端的代码
        }
    }
}

问题分析:

  1. Scanner sc = new Scanner(System.in);:在服务器的类成员中初始化了一个Scanner对象,它绑定到标准输入流System.in。这意味着这个Scanner是用来读取服务器运行环境的控制台输入的。
  2. sc.nextLine();:在服务器的for循环(用于从客户端接收任务描述和状态)结束后,调用了sc.nextLine()。此时,服务器本应继续处理网络通信或等待客户端的下一个网络请求。然而,由于sc是绑定到System.in的,sc.nextLine()会使服务器进程挂起,等待用户在服务器的控制台上手动输入一行文本并按回车。

这正是导致服务器在完成前几次网络数据接收后,突然“挂起”的根本原因。服务器错误地将网络通信流(DataInputStream)与本地控制台输入流(System.in)混淆,导致在不恰当的时机等待本地输入。

Digram
Digram

让Figma更好用的AI神器

下载

3. 客户端代码审视:通信模式的不匹配

除了服务器端的阻塞问题,客户端的通信逻辑也存在与服务器期望不匹配的情况。

客户端关键代码片段:

// Client.java
public class Client {
    // ... 其他成员变量
    public void iniciarCliente() throws IOException {
        // ... 获取输入输出流
        DataInputStream input = new DataInputStream(socket.getInputStream());
        DataOutputStream output = new DataOutputStream(socket.getOutputStream());

        Scanner sc = new Scanner(System.in); // 客户端使用System.in读取本地输入

        // ... 客户端发送姓名
        output.writeUTF(sc.nextLine());

        // 客户端发送任务数量
        System.out.println(input.readUTF()); // 服务器提示
        output.writeInt(sc.nextInt());
        sc.nextLine(); // 消耗nextInt()留下的换行符,这是正确的做法

        // 客户端只发送了一次任务的描述和状态
        System.out.println(input.readUTF()); // 服务器请求描述
        descripcion = sc.next();
        output.writeUTF(descripcion);

        System.out.println(input.readUTF()); // 服务器请求状态
        estado = sc.nextBoolean();
        output.writeBoolean(estado);
        // ... 后续接收服务器确认信息,但只接收了固定的几行
    }
}

问题分析:

  1. 循环不匹配: 服务器端在接收到任务数量numeroTareas后,会进入一个for循环,期望接收numeroTareas次任务的描述和状态。然而,客户端代码中,在发送完任务数量后,只发送了一次任务的描述和状态。
  2. 数据接收不足: 客户端在接收服务器发送的所有任务信息时,也只使用了固定数量的System.out.println(input.readUTF());语句,而不是根据任务数量进行循环接收。

这种服务器期望循环接收而客户端只发送/接收一次的通信模式不匹配,会导致服务器在第一次任务数据接收后,第二次循环中无法收到预期数据(因为客户端没发),从而在等待input.readUTF()或input.readBoolean()时发生阻塞(如果sc.nextLine()的阻塞问题被解决的话)。

4. 解决方案与代码重构

解决上述问题需要遵循两个核心原则:输入流的隔离通信协议的同步

4.1 服务器端 (Server.java) 修改

服务器端应严格区分网络输入和本地输入。在处理客户端请求时,只使用通过Socket.getInputStream()获取的网络输入流。

// Server.java (修正版片段)
package server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket

相关专题

更多
java
java

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

651

2023.06.15

java流程控制语句有哪些
java流程控制语句有哪些

java流程控制语句:1、if语句;2、if-else语句;3、switch语句;4、while循环;5、do-while循环;6、for循环;7、foreach循环;8、break语句;9、continue语句;10、return语句。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

453

2024.02.23

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

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

722

2023.07.05

java自学难吗
java自学难吗

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

725

2023.07.31

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

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

394

2023.08.01

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

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

398

2023.08.02

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

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

441

2023.08.02

java有什么用
java有什么用

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

426

2023.08.02

苹果官网入口直接访问
苹果官网入口直接访问

苹果官网直接访问入口是https://www.apple.com/cn/,该页面具备0.8秒首屏渲染、HTTP/3与Brotli加速、WebP+AVIF双格式图片、免登录浏览全参数等特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

10

2025.12.24

热门下载

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

精品课程

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

共23课时 | 2万人学习

C# 教程
C# 教程

共94课时 | 5.2万人学习

Java 教程
Java 教程

共578课时 | 37.2万人学习

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

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