ESP32 Websocket客户端断开时C#服务器OnClose方法未触发问题
问题:WebSocketSharp服务器无法检测ESP32设备离线
我用WebSocketSharp库在C#中搭建了WebSocket服务器,电脑和浏览器客户端连接时服务器运行正常。核心需求是连接ESP32实现远程控制,目前ESP32客户端能成功连接C#服务器,正常收发消息,但ESP32断开连接时,服务器的OnClose方法无法触发,也就是服务器检测不到ESP32离线。我试过多个ESP客户端库,只有一个能成功连接,但还是存在这个问题。
C#服务器代码
using System; using WebSocketSharp; using WebSocketSharp.Server; namespace web_socket { public class websocser : WebSocketBehavior { protected override void OnOpen() { Console.WriteLine("Device Was Connected. ID: "+ID); base.OnOpen(); } protected override void OnClose(CloseEventArgs e) { Console.WriteLine("Device Was Disconnected. ID: "+ID); base.OnClose(e); } protected override void OnMessage(MessageEventArgs e) { Console.WriteLine(e.Data); base.OnMessage(e); } } }
ESP32客户端代码
#include <Arduino.h> #include <WiFi.h> #include <WebSocketsClient.h> WebSocketsClient webSocket; const char *ssid = "#####"; const char *password = "#####"; unsigned long messageInterval = 5000; bool connected = false; #define DEBUG_SERIAL Serial void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { const uint8_t* src = (const uint8_t*) mem; DEBUG_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); for(uint32_t i = 0; i < len; i++) { if(i % cols == 0) { DEBUG_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); } DEBUG_SERIAL.printf("%02X ", *src); src++; } DEBUG_SERIAL.printf("\n"); } void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: DEBUG_SERIAL.printf("[WSc] Disconnected!\n"); connected = false; break; case WStype_CONNECTED: { DEBUG_SERIAL.printf("[WSc] Connected to url: %s\n", payload); connected = true; // send message to server when Connected DEBUG_SERIAL.println("[WSc] SENT: Connected"); // webSocket.sendTXT("Connected"); } break; case WStype_TEXT: DEBUG_SERIAL.printf("[WSc] RESPONSE: %s\n", payload); break; case WStype_BIN: DEBUG_SERIAL.printf("[WSc] get binary length: %u\n", length); hexdump(payload, length); break; case WStype_PING: // pong will be send automatically DEBUG_SERIAL.printf("[WSc] get ping\n"); break; case WStype_PONG: // answer to a ping we send DEBUG_SERIAL.printf("[WSc] get pong\n"); break; case WStype_ERROR: case WStype_FRAGMENT_TEXT_START: case WStype_FRAGMENT_BIN_START: case WStype_FRAGMENT: case WStype_FRAGMENT_FIN: break; } } void setup() { DEBUG_SERIAL.begin(115200); // DEBUG_SERIAL.setDebugOutput(true); DEBUG_SERIAL.println(); DEBUG_SERIAL.println(); DEBUG_SERIAL.println(); for(uint8_t t = 4; t > 0; t--) { DEBUG_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); DEBUG_SERIAL.flush(); delay(1000); } WiFi.begin(ssid, password); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); DEBUG_SERIAL.print ( "." ); } DEBUG_SERIAL.print("Local IP: "); DEBUG_SERIAL.println(WiFi.localIP()); // server address, port and URL webSocket.begin("192.168.1.34", 1005, "/ExEsp"); // event handler webSocket.onEvent(webSocketEvent); } unsigned long lastUpdate = millis(); void loop() { webSocket.loop(); // if (connected && lastUpdate+messageInterval<millis()){ // DEBUG_SERIAL.println("[WSc] SENT: Simple js client message!!"); // webSocket.sendTXT("Simple js client message!!"); // lastUpdate = millis(); // } }
解决方案
原因分析
问题出在TCP连接的断开检测机制上:当ESP32突然断电或网络异常断开时,不会主动发送WebSocket关闭帧,而WebSocketSharp默认不会主动检测死连接,导致服务器无法触发OnClose事件。浏览器客户端正常触发是因为浏览器会在关闭时主动发送关闭帧。
修复步骤
在C#服务器端启用Ping/Pong心跳检测
修改WebSocket服务器的启动配置,开启自动Ping并设置超时时间,服务器会定期向客户端发送Ping,若客户端在超时时间内未回复Pong,就会判定连接断开并触发OnClose。补充服务器启动代码:
var wssv = new WebSocketServer("ws://0.0.0.0:1005"); wssv.AddWebSocketService<websocser>("/ExEsp"); // 配置心跳:每10秒发送一次Ping,30秒未回复则断开 wssv.WaitTime = TimeSpan.FromSeconds(30); wssv.PingInterval = TimeSpan.FromSeconds(10); wssv.Start();确保ESP32客户端正常回复Ping
你的ESP32代码中已经持续调用webSocket.loop(),WebSocketsClient库会自动回复Ping,这部分无需额外修改,只需保证loop()不被长时间阻塞即可。主动断开时发送关闭帧
如果ESP32需要主动断开连接,要显式调用webSocket.close(),这样会发送标准关闭帧,服务器能立即触发OnClose:// 主动断开时调用 webSocket.close();
验证方法
- 断开ESP32电源,等待服务器超时时间(30秒),查看控制台是否输出断开日志。
- 在ESP32代码中主动调用
webSocket.close(),查看服务器是否立即触发OnClose。
内容的提问来源于stack exchange,提问作者ogzerg
相关产品推荐
相关产品推荐

