Arduino ESP32做Websocket client 连接WSS协议的服务器

编译环境是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服务器”这个问题。

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值