#include <WiFi.h>
#include <ESPAsyncWebServer.h>
const char *ssid = "********";
const char *password = "********";
String indexhtml = String("") +
"<!DOCTYPE html> \n" +
"<html> \n" +
"<head> \n" +
" <meta charset=\"UTF-8\"> \n" +
" <title>WebSocket Test</title> \n" +
" <script> \n" +
" var ws; \n" +
" window.onload = function () { \n" +
" if (\"WebSocket\" in window) { \n" +
" ws = new WebSocket(\"ws://\" + window.location.host + \"/\"); // 建立WebSocket连接 \n" +
" ws.onopen = function () { // 连接建立成功时触发 \n" +
" document.getElementById(\"info\").innerHTML += \"WebSocket连接成功!\" + \"<br>\"; \n" +
" ws.send(\"connect ok!\"); // 向服务器发送数据 \n" +
" }; \n" +
" ws.onmessage = function (evt) { // 收到服务器数据时触发 \n" +
" document.getElementById(\"info\").innerHTML += evt.data + \"<br>\"; \n" +
" }; \n" +
" ws.onerror = function () { // 发生错误时触发 \n" +
" document.getElementById(\"info\").innerHTML += \"通讯发送错误!\" + \"<br>\"; \n" +
" }; \n" +
" ws.onclose = function () { // 连接关闭时触发 \n" +
" document.getElementById(\"info\").innerHTML += \"WebSocketTest连接已关闭!\" + \"<br>\"; \n" +
" }; \n" +
" } \n" +
" else { \n" +
" document.getElementById(\"info\").innerHTML = \"浏览器不支持 WebSocket!\"; \n" +
" } \n" +
" }; \n" +
" function send() { \n" +
" console.log(\'error\'); \n" +
" ws.send(\"hahaha~~~\"); // 向服务器发送数据 \n" +
" } \n" +
" </script> \n" +
"</head> \n" +
"<body> \n" +
" <button οnclick=\"send()\">点击向服务器发送数据</button> \n" +
" <div id=\"info\"></div> \n" +
"</body> \n" +
"</html> \n";
AsyncWebServer server(80);
AsyncWebSocket ws("/");
void onEventHandle(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
if (type == WS_EVT_CONNECT)
Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
client->printf("Hello Client %u !", client->id());
client->ping();
else if (type == WS_EVT_DISCONNECT)
Serial.printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
else if (type == WS_EVT_ERROR)
Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data);
else if (type == WS_EVT_PONG)
Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : "");
else if (type == WS_EVT_DATA)
AwsFrameInfo *info = (AwsFrameInfo *)arg;
Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len);
data[len] = 0;
Serial.printf("%s\n", (char *)data);
void setup()
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
delay(500);
Serial.print(".");
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
ws.onEvent(onEventHandle);
server.addHandler(&ws);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/html", indexhtml);
});
server.begin();
Serial.println("Web server started");
void loop()
delay(2000);
ws.textAll("lalala~~~");
ws.cleanupClients();
上面的代码直接拷贝可能网页的字符串部分代码会出现编码问题导致无法正确运行,该部分最好删除重新手打。如果想偷懒的话可以试试只删除 onclick 这个词的 o 然后手打输入。

上面就是一个使用WebSocket进行双向通讯的例子,所有的关键步骤都在上面代码中写了注释。可以看到使用WebSocket进行数据交互比传统的用Ajax的方式要方便多了。
WebSocket用于双向通讯,服务器端接收来自客户端的消息上面已经进行了演示,是在事件回调函数中进行的,同时在回调函数中也可以通过AsyncWebSocketClient对象发送消息,主要方式如下:
- 格式化输出方式:
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); // ESP32特有 - 文本数据输出方式:
void text(const char * message, size_t len);
void text(const char * message);
void text(uint8_t * message, size_t len);
void text(char * message);
void text(const String &message);
void text(const __FlashStringHelper *data);
void text(AsyncWebSocketMessageBuffer *buffer); - 二进制数据输出方式:
void binary(const char * message, size_t len);
void binary(const char * message);
void binary(uint8_t * message, size_t len);
void binary(char * message);
void binary(const String &message);
void binary(const __FlashStringHelper *data, size_t len);
void binary(AsyncWebSocketMessageBuffer *buffer);
AsyncWebSocketClient对象除了可以用来发送数据,还可以获得一些信息 IPAddress remoteIP(); 、 uint16_t remotePort();,可以使用 void ping(uint8_t *data=NULL, size_t len=0); 方法向客户端发送ping信息,可以使用 void keepAlivePeriod(uint16_t seconds) 设置定时自动发送ping。最后可以用 void close(uint16_t code=0, const char * message=NULL); 方法关闭当前建立的连接。
除了上面客户端对象可以用来发送数据,AsyncWebSocket对象自身也可以发送数据,发送方法和客户端对象差不多,只不过第一个参数新增了客户端id:
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
……void text(uint32_t id, const char * message, size_t len);
……void binary(uint32_t id, const char * message, size_t len);
……
此外AsyncWebSocket对象还可以同时向全体已连接的客户端发送消息,方法和上面的差不多,主要是不需要id,然后方法名后跟上All:
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
……void textAll(const char * message, size_t len);
……void binaryAll(const char * message, size_t len);
……
AsyncWebSocket对象除了上面这些发送消息的功能外还有一些其它方法,可以用 size_t count() const; 获取已连接客户端数量,可以用 bool hasClient(uint32_t id){ return client(id) != NULL; } 判断是否有指定客户端,可以用 void ping(uint32_t id, uint8_t *data=NULL, size_t len=0); 、 void pingAll(uint8_t *data=NULL, size_t len=0); 发送ping信息。最后可以用 void close(uint32_t id, uint16_t code=0, const char * message=NULL); 、 void closeAll(uint16_t code=0, const char * message=NULL); 关闭客户端连接。
另外对于AsyncWebSocket对象还有一个特殊的方法:void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);。该方法上面演示中也有用到,依据建立事件,从最早的开始关闭超出设定数量的客户端连接。(主要是芯片性能有限,无限制的建立长连接可能就无法正常工作了。默认情况下ESP32可以建立32个连接、ESP8266可以建立8个连接。)