You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java聊天室开发问题:Scanner输入字符串写入DataOutputStream异常

我仔细看了你的代码,发现几个关键问题导致Scanner获取的字符串无法正常和ID一起发送到服务器,咱们一步步来修复:

1. 重复创建DataOutputStream引发的流异常

你的connect方法里先创建了一个DataOutputStream但没使用,之后sendPacket又重新创建了一个新的流包装同一个Socket输出流。虽然底层是同一个流,但重复包装会导致内部缓冲状态混乱,数据可能被拆分或延迟发送。

修复方案:把DataOutputStream作为Client类的成员变量,只初始化一次:

public class Client { 
    // ... 其他成员变量
    private static DataOutputStream dos; // 新增成员变量,复用流

    private static void connect() { 
        try { 
            client = new Socket("localhost",9999);
            dos = new DataOutputStream(client.getOutputStream()); // 只初始化一次
            String sl = sc.nextLine().trim();
            // 确保昵称不为空,避免空字符串问题
            while (sl.isEmpty()) {
                System.out.println("昵称不能为空,请重新输入:");
                sl = sc.nextLine().trim();
            }
            sendPacket(new Greetings(sl)); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 

    private static void sendPacket(Packet packet) { 
        try { 
            dos.writeInt(packet.getId()); // 复用成员变量的流
            packet.send(dos);
            dos.flush(); // 统一刷新流,确保数据立即发送
        } catch (SocketException se) {
            System.out.println("连接已断开,无法发送数据包");
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    }
}

同时把Greetings类send方法里的dos.flush()去掉,避免重复刷新。

2. 服务器端读取逻辑的致命错误

你的ClientHandler的run方法里,finally块每次循环都会调用end(),这意味着服务器读取一次数据后就直接关闭Socket并中断线程了!这肯定会导致后续无法正常接收数据。

修复方案:调整服务器读取逻辑,用try-with-resources管理流,移除错误的finally调用,并且不要用available()判断数据(这个方法只是估计可用字节数,非常不可靠):

public void run() {
    try (DataInputStream dis = new DataInputStream(socket.getInputStream())) {
        while (!isInterrupted()) {
            // readInt()会自动阻塞等待数据,不需要提前判断
            int packetId = dis.readInt();
            Packet p = PacketManager.getPacketById(packetId);
            if (p != null) {
                p.receive(dis);
                p.handle();
            }
        }
    } catch (SocketException se) {
        System.out.println("客户端连接断开");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        end(); // 只有当连接异常或循环结束时才关闭资源
    }
}

3. Scanner.nextLine()的小细节优化

虽然你说硬编码没问题,但nextLine()可能会读取到用户不小心输入的空字符串(比如换行),所以加个非空判断能避免不必要的问题,上面的客户端代码已经包含了这个处理。

最后验证

修改完这些点后,再测试一下:

  • 客户端输入昵称后,sendPacket会先发送ID(120),然后发送昵称的UTF编码
  • 服务器会阻塞等待数据,读取ID后获取对应的Greetings数据包,再读取昵称字符串
  • 整个流状态是一致的,不会出现数据拆分或连接提前关闭的问题

内容的提问来源于stack exchange,提问作者Dice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:46:09