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

Codename One真机Socket读取失败问题排查求助

Codename One iOS SocketConnection 真机上read()阻塞无数据问题

我正在使用Codename One开发一款iOS应用,为实现从服务器接收数据,我继承了SocketConnection类,代码如下:

class CustomSocketConnection extends SocketConnection {
    private OutputStream os;
    private InputStream is;
    private InputStreamReader isr;
    private String rawMessage;

    public CustomSocketConnection(){
        os = null;
        is = null;
        isr = null;
        rawMessage = null;
    }

    public synchronized String getRawMessage(){
        return rawMessage;
    }

    @Override
    public void connectionError(int errorCode, String message) {
        rawMessage = null;
        ToastBar.showErrorMessage("Error Connecting. ErrorCode: " + errorCode + " Message: " + message);
        System.out.println(">>>CustomSocketConnection, connectionError. Error Connecting. ErrorCode: " + errorCode + " Message: " + message);
    }

    @Override
    public void connectionEstablished(InputStream is, OutputStream os) {
        System.out.println(">>>CustomSocketConnection, connectionEstablished");
        char termination = '\n';
        int length = 1024;
        char[] buffer = new char[length];
        byte[] bufferByte = new byte[length];
        StringBuilder stringBuilder = new StringBuilder();
        System.out.println(">>>CustomSocketConnection, connectionEstablished, prima del while");
        try {
            InputStreamReader isr = new InputStreamReader(is, "UTF-8");
            System.out.println(">>>CustomSocketConnection, connectionEstablished, prima della read");
            while(true){
                System.out.println(">>>CustomSocketConnection, loop di read, inizio");
                int read = isr.read();
                System.out.println(">>>CustomSocketConnection, loop di read, subito dopo la read");
                char c = (char) read;
                if (read == -1) rawMessage = null;
                System.out.println(">>>CustomSocketConnection, loop di read, letto: " + c);
                while(c != termination){
                    stringBuilder.append(c);
                    read = isr.read();
                    c = (char) read;
                    if (read == -1) rawMessage = null;
                }
                rawMessage = stringBuilder.toString();
                if(rawMessage != null) doActions(rawMessage);
                //System.out.println(">>>CustomSocketConnection, connectionEstablished, ho letto: " + rawMessage + "FINE");
            }
        } catch (IOException ex) {
            System.out.println(">>>CustomSocketConnection, connectionEstablished, errore: " + ex.getMessage());
        }
        System.out.println(">>>CustomSocketConnection, connectionEstablished, dopo il while");
    }

    private void doActions(String msg){
        //do something
    }
}

在模拟器上运行时应用可正常接收数据,但在真机(32位iPad mini,iOS 8.1.1 12B435;64位iPhone 7s,iOS 11.2.5 15D60)上,read方法无法接收服务器数据:可看到read方法前的打印日志,但看不到read方法后的日志。排除服务器端问题(同款服务器的Android应用可正常接收数据),也无防火墙或网络限制(Android应用和Codename One模拟器在同网络及其他网络下均能正常接收),请问问题出在哪里?


问题分析与解决方案

从你的代码和现象来看,问题大概率出在iOS平台上阻塞式单字符读取的实现差异以及未正确处理流终止和换行符这两个点上,下面详细说明:

1. 单字符读取的阻塞问题

你使用isr.read()逐个读取字符,这种方式在模拟器和Android上可以正常工作,但iOS平台的底层Socket实现对单字节/单字符的阻塞读取有不同的处理逻辑,容易导致线程无限阻塞。而且单字符读取效率极低,不适合生产环境使用。

2. 换行符不匹配

服务器可能发送的是\r\n(Windows标准换行)而不是你代码中判断的\n,在模拟器上可能自动处理了这种差异,但iOS上严格按照字符匹配,导致读取到\r后一直卡在内部循环里,无法触发消息处理。

3. 未正确处理流终止

当输入流关闭(read()返回-1)时,你的代码只是设置rawMessage = null,但没有退出循环,这会导致线程一直执行无效的读取操作,陷入死循环。

修复后的代码示例

@Override
public void connectionEstablished(InputStream is, OutputStream os) {
    System.out.println(">>>CustomSocketConnection, connectionEstablished");
    int bufferSize = 1024;
    char[] buffer = new char[bufferSize];
    StringBuilder stringBuilder = new StringBuilder();
    try (InputStreamReader isr = new InputStreamReader(is, "UTF-8")) { // 使用try-with-resources自动关闭流
        int readCount;
        // 批量读取字符,而非单个读取
        while ((readCount = isr.read(buffer)) != -1) {
            System.out.println(">>>CustomSocketConnection, 读取到" + readCount + "个字符");
            for (int i = 0; i < readCount; i++) {
                char c = buffer[i];
                // 处理\r\n或\n的换行符
                if (c == '\n') {
                    rawMessage = stringBuilder.toString().trim(); // 去除可能的\r
                    if (rawMessage != null && !rawMessage.isEmpty()) {
                        doActions(rawMessage);
                    }
                    stringBuilder.setLength(0); // 重置StringBuilder
                } else if (c != '\r') { // 跳过\r字符
                    stringBuilder.append(c);
                }
            }
        }
        // 流关闭后的处理
        rawMessage = null;
        System.out.println(">>>CustomSocketConnection, 输入流已关闭");
    } catch (IOException ex) {
        System.out.println(">>>CustomSocketConnection, connectionEstablished, errore: " + ex.getMessage());
        rawMessage = null;
    }
}

额外建议

  • 避免在回调线程中长时间阻塞:虽然connectionEstablished是在后台线程执行,但如果需要持续监听Socket,建议使用Codename One提供的NetworkManager来管理网络任务,或者单独启动一个线程处理读取逻辑(不过SocketConnection本身应该已经在后台线程)。
  • 添加超时机制:可以考虑使用Socket的超时设置(如果能获取到底层Socket),避免无限阻塞。
  • 日志优化:在批量读取的代码中添加更详细的日志,比如读取到的字符内容,方便排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:01:23