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

Android与ESP8266 Socket通信单向失败问题求助

Android与ESP8266 Socket通信单向失败问题排查与解决

我搭了个Android和ESP8266的简易Socket通信原型,但只有一半能用——Android发消息到ESP8266完全正常,反过来ESP8266发消息到Android就直接失败了。Android端报java.io.EOFException,ESP8266那边还会触发超时。下面是相关代码片段,需要的话我可以补全完整代码:

Android端代码片段

while (true) { 
    socket = serverSocket.accept(); 
    dataInputStream = new DataInputStream(socket.getInputStream()); 
    dataOutputStream = new DataOutputStream(socket.getOutputStream()); 
    String msgReply = "Android server is alive!"; 
    dataOutputStream.writeUTF(msgReply); 
    dataOutputStream.flush(); 
    message = dataInputStream.readUTF(); 
    MainActivity.this.runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
            msg.setText(message); 
        } 
    }); 
} 

ESP8266端代码片段

String cmd = "Hello from NodeMCU"; 
Serial.print("send to server: "); 
Serial.println(cmd); 
client.print(cmd); 
client.flush(); 
unsigned long timeout = millis(); 
while (client.available() == 0) { 
    if (millis() - timeout > 5000) { 
        Serial.println(">>>> Client Timeout !"); 
        client.stop(); 
        return; 
    } 
} 
while(client.available()){ 
    String line = client.readStringUntil('!'); 
    Serial.print(line); 
} 

我试了各种方法,比如改成client.write('h')发送单个字符,但都没用,恳请大佬指明可行的解决方向!


核心问题:两端数据读写格式不匹配

你的问题根源其实很明确——Android和ESP8266用了完全不兼容的消息编码/读取逻辑:

  • Android这边用的是DataOutputStream.writeUTF()和DataInputStream.readUTF(),这俩是Java的成对API:writeUTF()会先写一个2字节的长度前缀(标记后面字符串的字节数),再写实际的字符串内容;readUTF()必须先读到这个长度前缀,再按长度读取对应内容,否则直接抛出EOFException。
  • 但ESP8266这边用client.print(cmd)直接发普通字符串,根本没有加那个2字节的长度前缀,Android的readUTF()找不到预期的前缀,自然就报错了;反过来,ESP8266在等Android的回复,但Android发的是带长度前缀的writeUTF()内容,ESP8266用readStringUntil('!')去读,根本读不到正确的结束标记,最后超时退出。

可行的解决方向(二选一即可)

方向1:统一用Java标准的UTF格式(推荐,兼容性更好)

需要修改ESP8266的代码,模拟Java的writeUTF()和readUTF()逻辑:

  • 先写个工具函数实现writeUTF的行为:

    void writeUTF(WiFiClient &client, const String &str) {
      uint16_t strLen = str.length();
      // 先写长度的高字节,再写低字节(大端序,和Java一致)
      client.write((strLen >> 8) & 0xFF);
      client.write(strLen & 0xFF);
      // 再发送字符串内容
      client.print(str);
      client.flush();
    }
    

    把原来的client.print(cmd);改成writeUTF(client, cmd);就行。

  • 再写个readUTF函数读取Android的回复,代替原来的readStringUntil('!'):

    String readUTF(WiFiClient &client) {
      // 先检查有没有足够的字节读长度前缀
      if (client.available() < 2) return "";
      // 读取2字节的长度(大端序)
      uint16_t strLen = (client.read() << 8) | client.read();
      String result;
      // 按长度读取字符串内容
      while (strLen-- > 0 && client.available()) {
        result += (char)client.read();
      }
      return result;
    }
    

    然后把读取部分改成String reply = readUTF(client); Serial.print(reply);。

Android端的代码不需要改,保持原来的writeUTF()和readUTF()就行。

方向2:统一用普通字符串+分隔符的方式

如果不想搞复杂的长度前缀,可以两端都用普通字符串,加个固定分隔符(比如换行符\n)来标记消息结束:

  • Android端修改代码:
    把DataInputStream/DataOutputStream换成普通的字符流读写:
    while (true) { 
        socket = serverSocket.accept(); 
        // 替换成字符流
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        
        String msgReply = "Android server is alive!"; 
        writer.write(msgReply + "\n"); // 加换行符当分隔符
        writer.flush(); 
        
        String message = reader.readLine(); // 按换行符读取完整消息
        MainActivity.this.runOnUiThread(new Runnable() { 
            @Override 
            public void run() { 
                msg.setText(message); 
            } 
        }); 
    } 
    
  • ESP8266端修改代码:
    发送消息时加上换行符,读取时按换行符匹配:
    String cmd = "Hello from NodeMCU\n"; // 加换行符
    Serial.print("send to server: "); 
    Serial.println(cmd); 
    client.print(cmd); 
    client.flush(); 
    
    unsigned long timeout = millis(); 
    while (client.available() == 0) { 
        if (millis() - timeout > 5000) { 
            Serial.println(">>>> Client Timeout !"); 
            client.stop(); 
            return; 
        } 
    } 
    
    // 按换行符读取回复
    String line = client.readStringUntil('\n'); 
    Serial.print(line); 
    

额外提醒

  • 确保两端的Socket是长连接,不要发完一次消息就关闭,除非你的业务逻辑就是单次通信的短连接。
  • ESP8266的超时逻辑可以保留,但要注意读取到数据后及时退出循环,避免不必要的等待。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:51:17