使用cURL创建WebSocket连接的小实践

2,627 阅读4分钟

背景:WebSocket是一种用于在客户端和服务器之间建立持久连接的协议,适用于实时通信场景。而cURL,作为一个功能强大的命令行工具和库,为我们提供了一种便捷的方式来创建和测试WebSocket连接。


最近因为工作需要,要使用cURL来创建一个WebSocket连接。因为自己之前没有了解过相关的内容,所以开始到处狂学,并且想要把自己了解到的记录一下。(破碎的打工吗喽在线哭泣😭)

cURL 简介

cURL(Client URL)是一个用于传输数据的命令行工具和库。它支持多种协议,包括HTTP、HTTPS、FTP、SFTP、SCP、TELNET等,它的强大之处在于它的灵活性和广泛的支持,几乎可以用于任何网络通信需求。

在终端输入 'curl --version' 就可以查看当前cURL的版本信息啦。

11_01.png

curl 的基础用法非常简单,只需在命令行中输入curl命令并指定URL:

curl http://example.com

11_02.png

有相关的使用问题可以参考 curl 的 github文档

创建 WebSocket 连接

WebSocket 是一种全双工通信协议,通常在 HTTP/HTTPS 协议上进行握手后升级为 WebSocket 协议。在使用 curl 创建 WebSocket 连接时,我们可以指定 WebSocket 协议的 URL(以 ws:// 或 wss:// 开头)并进行连接。

WebSocket 连接的创建还可以使用 curl 的 --upgrade 参数,例如:

curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: example.com:80" \
     --header "Origin: http://example.com" \
     --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
     --header "Sec-WebSocket-Version: 13" \
     http://example.com/
  • --include 参数表示将响应头信息也一并输出
  • --no-buffer 参数表示禁用缓冲
  • --header 参数表示添加请求头信息
  • --upgrade 参数表示升级协议为WebSocket
  • --header 参数表示添加请求头信息

执行此命令后,如果服务器支持WebSocket,应该会看到一个101 Switching Protocols的响应,表示WebSocket连接已经建立,就像下面这样啦👇👇

11_03.png

测试 Demo

虽然命令行工具很有用,但在实际开发中,通常需要在项目中集成WebSocket功能。curl库提供了C API,可以用于在程序中实现WebSocket通信。

通过封装cURL的WebSocket功能,可以实现一个简单的 websocket 测试类,提供以下主要方法:

  • 构造函数和析构函数
  • Connect():建立WebSocket连接
  • Send():发送数据
  • Close():关闭连接

此外,还有两个回调函数:WriteCallback和CloseSocketCallback,用于处理接收到的数据和连接关闭事件。

初始化

WebSocket::WebSocket(const std::string& url)
    : curl_(nullptr), url_(url) {
  curl_global_init(CURL_GLOBAL_ALL);
  curl_ = curl_easy_init();
}

这里调用 curl_global_init 来初始化 cURL 库,然后使用 curl_easy_init 创建一个 cURL 句柄。

建立连接

Connect 方法设置了必要的cURL选项来建立WebSocket连接:

    curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str());
    curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl_, CURLOPT_WRITEDATA, this);
    curl_easy_setopt(curl_, CURLOPT_CLOSESOCKETFUNCTION, CloseSocketCallback);
    curl_easy_setopt(curl_, CURLOPT_CLOSESOCKETDATA, this);

使用 curl_easy_setopt 配置 WebSocket 连接的选项,包括设置 URL 、处理接收数据的回调函数、关闭套接字的回调函数等。

(我在这里设置了接收数据的回调函数,根据文档,还可以设置 CURLOPT_CONNECT_ONLY 为 2L,之后可以使用 curl_ws_recv() 和 curl_ws_send() 来接收和发送 WebSocket 帧,与服务器进行通信。)

还可以设置很多其他的选项,例如 CURLOPT_VERBOSE、CURLOPT_HEADER、CURLOPT_SSL_VERIFYPEER 等。

    CURLcode res = curl_easy_perform(curl_);
    if (res != CURLE_OK) {
        std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        return -1;
    }

curl_easy_perform 用于发起连接请求,并处理握手过程。如果返回的 HTTP 状态码不是 101 Switching Protocols,说明 WebSocket 升级失败。

发送数据

Send方法使用curl_easy_send来发送数据:

    CURLcode res = curl_easy_send(curl_, data.c_str(), data.length(), 0);
    if (res!= CURLE_OK) {
        std::cerr << "curl_easy_send() failed: " << curl_easy_strerror(res) << std::endl;
        return -1;
    }

关闭连接

在析构函数和Close方法中,清理cURL资源,curl_easy_cleanup 释放 curl 句柄,curl_global_cleanup 则用于全局清理 curl 库。

    curl_easy_cleanup(curl_);
    curl_global_cleanup();

数据接收回调

size_t WriteCallback(void* ptr, size_t size, size_t nmemb, void* userdata) {
    WebSocket* ws = static_cast<WebSocket*>(userdata);
    size_t total_size = size * nmemb;

    // 处理接收到的数据

    return total_size;
}

总结

通过使用cURL,就可以轻松地(并不!!😭😭)创建和管理WebSocket连接啦。无论如何,理解cURL如何处理WebSocket连接可以帮助我更好地理解WebSocket协议的工作原理,为我在实际开发中选择和使用WebSocket库提供有价值的洞察。