复用Socket向ESP32服务器发送多HTTP请求时程序停滞问题
问题原因分析
- 协议版本不匹配:客户端发送
HTTP/2请求,但ESP32服务器返回HTTP/1.1响应——多数ESP32内置HTTP服务器仅支持HTTP/1.1,协议不兼容会导致后续处理异常。 - 响应读取逻辑错误:你用
while ((line = reader.readLine()) != null)循环读取响应,readLine()会一直阻塞直到读到换行符或输入流关闭。服务器返回的响应包含Content-Length: 2的响应体(2个字节、无换行),读完响应头的空行后,readLine()会持续等待换行,直接导致程序停滞。 - 服务器不支持长连接:服务器返回
Connection: close,说明它会在本次请求后主动关闭连接,即使客户端指定Connection: keep-alive也无法复用该Socket,后续请求自然无法正常执行。
修复方案
1. 修正HTTP协议版本
将请求中的HTTP/2改为HTTP/1.1,同时严格遵循HTTP协议规范用\r\n作为头信息换行符:
output.write( String.format( "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: keep-alive\r\n" + "\r\n", x, hostname ) );
2. 正确读取HTTP响应
不能依赖readLine()一直读到流结束,需要按HTTP响应结构分步读取:先读取响应头并解析Content-Length,再读取对应长度的响应体,读完即停止循环避免阻塞:
System.out.println("h2"); String line; int contentLength = 0; // 读取响应头,空行代表头信息结束 while ((line = reader.readLine()) != null) { System.out.println(line); // 解析响应体长度 if (line.startsWith("Content-Length:")) { contentLength = Integer.parseInt(line.split(":")[1].trim()); } if (line.isEmpty()) { break; } } // 读取指定长度的响应体 if (contentLength > 0) { char[] buffer = new char[contentLength]; int read = reader.read(buffer, 0, contentLength); System.out.println(new String(buffer, 0, read)); } System.out.println("h3");
3. 适配服务器关闭连接的情况
如果服务器始终返回Connection: close,说明它不支持长连接,此时无法复用Socket,需在每次请求前重新创建Socket和流:
for(int i=1; i<50; i++) { String x = i%2==0 ? "on" : "off"; System.out.println("h1"); // 每次请求重新初始化Socket与流 socket = new Socket(hostname, port); PrintWriter output = new PrintWriter(socket.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); output.write( String.format( "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "\r\n", x, hostname ) ); output.flush(); System.out.println("h2"); // 用修正后的逻辑读取响应 String line; int contentLength = 0; while ((line = reader.readLine()) != null) { System.out.println(line); if (line.startsWith("Content-Length:")) { contentLength = Integer.parseInt(line.split(":")[1].trim()); } if (line.isEmpty()) { break; } } if (contentLength > 0) { char[] buffer = new char[contentLength]; int read = reader.read(buffer, 0, contentLength); System.out.println(new String(buffer, 0, read)); } System.out.println("h3"); // 关闭当前请求的资源 reader.close(); output.close(); socket.close(); Thread.sleep(200); }
4. 优化ESP32服务器支持长连接(可选)
如果想实现Socket复用,需修改ESP32服务器代码,确保它支持Connection: keep-alive并在响应中返回该字段,而非Connection: close。例如在Arduino环境下,确保server.handleClient()持续处理客户端连接,不主动关闭Socket。
内容的提问来源于stack exchange,提问作者Maurice
相关产品推荐
相关产品推荐

