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

Android客户端Socket读取服务器消息时readText阻塞问题求助

问题分析

你的问题出在BufferedReader.readText()方法上——它会一直阻塞直到输入流到达末尾(EOF),也就是只有当C++服务器关闭Socket连接时,这个方法才会返回。这就是为什么你看到只有关闭服务器才能读取到消息的核心原因。

解决方案

针对你的场景(后续还要传输图像缓冲区),推荐两种可靠的消息读取方式:

1. 基于换行符的文本消息分隔(适合文本通信)

如果服务器发送的是文本消息,约定每条消息末尾加上换行符\n,客户端用readLine()读取,它会在遇到换行符时立即返回,不会阻塞等待流关闭。

修改listenForServerMessage方法:

fun listenForServerMessage() {
    Log.d("ServerMain ", "listenForServerMessage")
    Thread(Runnable {
        try {
            Log.d("ServerMain ","letsgo:"+singleton_ServerInfos.end);

            val bufReader = BufferedReader(InputStreamReader(singleton_ServerInfos.clientSocket.getInputStream()))
            while (!singleton_ServerInfos.end) {
                Log.d("ServerMain ", "waiting for message")
                // 用readLine替换readText,读取到换行符就返回
                val message = bufReader.readLine()
                if (message != null) {
                    Log.d("ServerMain read: ", "Received: $message")
                    // 切换到UI线程更新界面
                    requireActivity().runOnUiThread {
                        // 这里添加UI更新逻辑
                    }
                } else {
                    // 流已关闭,退出循环
                    break
                }
            }
        } catch (e: Exception) {
            Log.d("ServerMain: ", "exception: ${e.message}")
        } finally {
            // 确保关闭Socket资源
            singleton_ServerInfos.clientSocket?.close()
        }
    }).start()
}

同时需要修改C++服务器,发送消息时在末尾添加\n,示例:

// C++服务器发送文本消息示例
std::string message = "Hello from server\n";
send(clientSocket, message.c_str(), message.size(), 0);

2. 基于长度前缀的二进制通信(适合图像等二进制数据)

如果后续要传输图像缓冲区这类二进制数据,更可靠的方式是先发送消息的长度,再发送实际数据。这种方式能准确控制读取的字节数,避免阻塞。

客户端修改示例:

fun listenForServerMessage() {
    Log.d("ServerMain ", "listenForServerMessage")
    Thread(Runnable {
        try {
            val inputStream = singleton_ServerInfos.clientSocket.getInputStream()
            val dataInputStream = DataInputStream(inputStream)
            while (!singleton_ServerInfos.end) {
                // 先读取4字节的长度(用int存储,对应Java的int类型)
                val dataLength = dataInputStream.readInt()
                if (dataLength <= 0) break
                
                // 读取对应长度的字节数据
                val buffer = ByteArray(dataLength)
                dataInputStream.readFully(buffer)
                
                // 处理数据:文本转字符串,图像则解析为Bitmap
                val textMessage = String(buffer)
                Log.d("ServerMain read: ", "Received data length: $dataLength, content: $textMessage")
                
                // 处理图像示例:
                // val bitmap = BitmapFactory.decodeByteArray(buffer, 0, dataLength)
            }
        } catch (e: Exception) {
            Log.d("ServerMain: ", "exception: ${e.message}")
        } finally {
            singleton_ServerInfos.clientSocket?.close()
        }
    }).start()
}

C++服务器对应发送逻辑:

// 发送数据前先发送长度(4字节int类型,注意字节序统一为大端序)
std::string data = "Hello from server";
int dataLength = data.size();
dataLength = htonl(dataLength); // 转换为网络字节序(大端)
send(clientSocket, &dataLength, sizeof(int), 0);
// 发送实际数据
send(clientSocket, data.c_str(), data.size(), 0);
额外注意点
  • 不要用available()做读取判断:available()仅返回当前可读取的字节数,不能作为消息是否完全到达的可靠依据,容易出现数据读取不完整的情况。
  • 资源释放要及时:在finally块中关闭Socket和流,避免资源泄漏。
  • UI操作必须在主线程:所有更新界面的逻辑必须通过runOnUiThread执行,不能在子线程直接操作UI。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:41:47