编译环境是VS+PIO,调用的库是ArduinoWebsockets.h,这个在Libraries里有哈,直接搜索下载即可。
最近有个项目客户服务器是Websocket server,走的是WSS协议,要加密的嘛,我这边用ESP8266连上秒过,ESP32一直连不上,提示
然后就是愉快地找底层,把ESP8266和ESP32一步一步对比,终于发现了问题所在:
原因就是ESP8266和ESP32都没有证书的,那么当客户端的话正常流程就是跳过证书验证就好了,结果ESP8266跳过去了,ESP32没跳过去。
那好,加个函数就行了,下面附上代码:
main.cpp:
#include <Arduino.h>
#include <ArduinoWebsockets.h>
#include <WiFi.h>
const char* ssid = "your_ssid"; //Enter SSID
const char* password = "your_pswd"; //Enter Password
const char* websockets_server_host = "your_host"; //Enter server adress
const uint16_t websockets_server_port = 8080; // Enter server port 我这里端口号是自动判断的
using namespace websockets;
void onMessageCallback(WebsocketsMessage message) {
Serial.print("Got Message: ");
Serial.println(message.data());
}
void onEventsCallback(WebsocketsEvent event, String data) {
if(event == WebsocketsEvent::ConnectionOpened) {
Serial.println("Connnection Opened");
} else if(event == WebsocketsEvent::ConnectionClosed) {
Serial.println("Connnection Closed");
} else if(event == WebsocketsEvent::GotPing) {
Serial.println("Got a Ping!");
} else if(event == WebsocketsEvent::GotPong) {
Serial.println("Got a Pong!");
}
}
WebsocketsClient client;
void setup() {
Serial.begin(115200);
// Connect to wifi
WiFi.begin(ssid, password);
// Wait some time to connect to wifi
for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
Serial.print(".");
delay(1000);
}
Serial.print("ESP Ready! Use 'http://");
Serial.println(WiFi.localIP());
// run callback when messages are received
client.onMessage(onMessageCallback);
// run callback when events are occuring
client.onEvent(onEventsCallback);
// Connect to server
client.connect(websockets_server_host); //注意这里只给了host一个参数,没有给端口号
// Send a message
client.send("HELLO");
// Send a ping
client.ping();
}
void loop() {
client.poll();
}
然后从connect()函数 用F12跳过去,然后找到对于WSS和HTTPS有特有的upgradeToSecuredConnection()函数,从这个函数跳进去,原本的代码是这样的:
//这个函数在ArduinoWebsocket\src\websockets_client.cpp 下
void WebsocketsClient::upgradeToSecuredConnection() {
#ifndef _WS_CONFIG_NO_SSL
auto client = new WSDefaultSecuredTcpClient;
#ifdef ESP8266
if(
this->_optional_ssl_fingerprint
|| (this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key)
|| (this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key)
|| this->_optional_ssl_trust_anchors
|| this->_optional_ssl_known_key
) {
if(this->_optional_ssl_fingerprint) { //这个判断不会进
client->setFingerprint(this->_optional_ssl_fingerprint);
}
if(this->_optional_ssl_trust_anchors) { //这个判断不会进
client->setTrustAnchors(this->_optional_ssl_trust_anchors);
}
if(this->_optional_ssl_known_key) { //这个判断不会进
client->setKnownKey(this->_optional_ssl_known_key);
}
if(this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key) { //这个判断不会进
client->setClientRSACert(this->_optional_ssl_rsa_cert, this->_optional_ssl_rsa_private_key);
}
if(this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key) { //这个判断不会进
client->setClientECCert(this->_optional_ssl_ec_cert, this->_optional_ssl_ec_private_key);
}
} else { //这个判断会进
client->setInsecure();
}
#elif defined(ESP32)
if(this->_optional_ssl_ca_cert) { //这个判断不会进
client->setCACert(this->_optional_ssl_ca_cert);
}
if(this->_optional_ssl_client_ca) { //这个判断不会进
client->setCertificate(this->_optional_ssl_client_ca);
}
if(this->_optional_ssl_private_key) { //这个判断不会进
client->setPrivateKey(this->_optional_ssl_private_key);
}
#endif
this->_client = std::shared_ptr<WSDefaultSecuredTcpClient>(client);
this->_endpoint.setInternalSocket(this->_client);
#endif //_WS_CONFIG_NO_SSL
}
注意#define ESP8266或是#define ESP32无需用户自己定义。
实际上这几个if里判断的寄存器,例如_optional_ssl_ca_cert等,都是赋值了false的,因此这几个判断都不会进,那也就是说ESP8266比ESP32多进了一个 client->setInsecure();
这里就是关键点,我们需要让ESP32也进一次client->setInsecure() 函数,因此我们把这段代码改成:
//这个函数在ArduinoWebsocket\src\websockets_client.cpp 下
void WebsocketsClient::upgradeToSecuredConnection() {
#ifndef _WS_CONFIG_NO_SSL
auto client = new WSDefaultSecuredTcpClient;
#ifdef ESP8266
if(
this->_optional_ssl_fingerprint
|| (this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key)
|| (this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key)
|| this->_optional_ssl_trust_anchors
|| this->_optional_ssl_known_key
) {
if(this->_optional_ssl_fingerprint) { //这个判断不会进
client->setFingerprint(this->_optional_ssl_fingerprint);
}
if(this->_optional_ssl_trust_anchors) { //这个判断不会进
client->setTrustAnchors(this->_optional_ssl_trust_anchors);
}
if(this->_optional_ssl_known_key) { //这个判断不会进
client->setKnownKey(this->_optional_ssl_known_key);
}
if(this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key) { //这个判断不会进
client->setClientRSACert(this->_optional_ssl_rsa_cert, this->_optional_ssl_rsa_private_key);
}
if(this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key) { //这个判断不会进
client->setClientECCert(this->_optional_ssl_ec_cert, this->_optional_ssl_ec_private_key);
}
} else { //这个判断会进!
client->setInsecure();
}
#elif defined(ESP32)
if(
this->_optional_ssl_ca_cert
|| this->_optional_ssl_client_ca
|| this->_optional_ssl_private_key
) {
if(this->_optional_ssl_ca_cert) { //这个判断不会进
client->setCACert(this->_optional_ssl_ca_cert);
}
if(this->_optional_ssl_client_ca) { //这个判断不会进
client->setCertificate(this->_optional_ssl_client_ca);
}
if(this->_optional_ssl_private_key) { //这个判断不会进
client->setPrivateKey(this->_optional_ssl_private_key);
}
}
else client->setInsecure();
#endif
this->_client = std::shared_ptr<WSDefaultSecuredTcpClient>(client);
this->_endpoint.setInternalSocket(this->_client);
#endif //_WS_CONFIG_NO_SSL
}
但是ESP32里client->setInsecure()函数会报错,这是因为对于ESP32的client成员并没有setInsecure();函数,因此我们给他手动添加一个,
首先可以知道在这个函数里client成员是auto client = new WSDefaultSecuredTcpClient;这一句定义的,因此我们直接对WSDefaultSecuredTcpClient 转到定义,然后:
#ifdef ESP8266
#define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
#include <tiny_websockets/network/esp8266/esp8266_tcp.hpp>
#define WSDefaultTcpClient websockets::network::Esp8266TcpClient
#define WSDefaultTcpServer websockets::network::Esp8266TcpServer
#ifndef _WS_CONFIG_NO_SSL
// OpenSSL Dependent
#define WSDefaultSecuredTcpClient websockets::network::SecuredEsp8266TcpClient //先从这里的“SecuredEsp8266TcpClient”跳进去,复制好setInsecure()函数后,看下面注释
#endif //_WS_CONFIG_NO_SSL
#elif defined(ESP32)
#define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
#include <tiny_websockets/network/esp32/esp32_tcp.hpp>
#define WSDefaultTcpClient websockets::network::Esp32TcpClient
#define WSDefaultTcpServer websockets::network::Esp32TcpServer
#ifndef _WS_CONFIG_NO_SSL
// OpenSSL Dependent
#define WSDefaultSecuredTcpClient websockets::network::SecuredEsp32TcpClient //复制好ESP8266那边定义的setInsecure()函数后,在从这里的“SecuredEsp32TcpClient”跳进去
#endif //_WS_CONFIG_NO_SSL
#elif defined(ARDUINO_TEENSY41)
#define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
#define _WS_CONFIG_NO_SSL
#include <tiny_websockets/network/teensy41/teensy41_tcp_client.hpp>
#include <tiny_websockets/network/teensy41/teensy41_tcp_server.hpp>
#define WSDefaultTcpClient websockets::network::Teensy41TcpClient
#define WSDefaultTcpServer websockets::network::Teensy41TcpServer
#endif
从SecuredEsp32TcpClient跳进来之后把刚刚ESP8266那边的setInsecure()函数复制进来,代码变成这样:
//这段代码在ArduinoWebsockets\src\tiny_websockets\network\esp32\esp32_tcp.hpp下
class SecuredEsp32TcpClient : public GenericEspTcpClient<WiFiClientSecure> {
public:
void setCACert(const char* ca_cert) {
this->client.setCACert(ca_cert);
}
void setCertificate(const char* client_ca) {
this->client.setCertificate(client_ca);
}
void setPrivateKey(const char* private_key) {
this->client.setPrivateKey(private_key);
}
void setInsecure() { //这里修改了
this->client.setInsecure();
}
};
这样子就对SecuredEsp32TcpClient 成员添加了setInsecure()函数。
setInsecure()函数实际上是设置了WIFI库里的部分寄存器,如下:
void WiFiClientSecure::setInsecure()
{
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
_use_insecure = true;
}
很庆幸这几个寄存器不但用于ESP8266,而且也用于ESP32,并且功能都一样,因此我们只需要调用一遍这个函数即可解决“ESP32不能像ESP8266那样连上wss的websocket服务器”这个问题。