WebSocket

一、什么是WebSocket?

WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。(维基百科)

WebSocket本质上一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。

WebSocket 协议在2008年诞生,2011年成为国际标准。现在最新版本浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

WebSocket 的其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

二、WebSocket工作原理

握手阶段

浏览器先通过一个特殊的 HTTP 请求(带有 Upgrade: websocket 头)向服务器发起连接请求。

如果服务器支持 WebSocket,就会返回一个确认响应(101 Switching Protocols),表示协议升级成功。

建立连接

握手完成后,HTTP 协议升级为 WebSocket 协议。

此时,客户端与服务器之间形成一个持久连接,双方都可以随时主动发送数据。

双向通信

客户端可以向服务器主动发送消息。

服务器也可以主动向客户端推送数据。

所有消息都通过这个持续存在的连接传输,无需频繁建立连接。

关闭连接

当一方发送关闭信号时,连接被终止。

三、WebSocket与HTTP的区别

HTTP(传统方式)

一次请求 → 一次响应 → 连接关闭

必须客户端先发请求

适合:网页加载、接口请求

WebSocket(实时通信)

建立连接后一直保持

客户端和服务器都可以主动发消息

低延迟、高效率

WebSocket 的特点

双向通信(客户端 ⇄ 服务器)

实时性强(延迟低)

长连接(不会频繁断开)

减少开销(不需要反复建立连接)

对比 HTTP WebSocket
通信方式 请求响应 双向通信
是否实时
连接方式 短连接 长连接
延迟 较高 很低
开销
开发复杂度
适合 CRUD 实时系统

四、WebSocket的优点和缺点

WebSocket 的优点

真正实时通信(最大优点)

HTTP:

客户端请求一次

服务器回复一次

WebSocket:

服务器可以主动发消息

例如:

聊天消息

机器人状态

游戏同步

都能实时推送。

双向通信

HTTP:

只能客户端主动请求

WebSocket:

客户端 ⇄ 服务端

双方都能主动发送

这是本质区别。

长连接(性能高)

HTTP 每次都:

建立TCP

发送

断开

WebSocket:

建立一次

一直保持

所以:

延迟低

CPU 消耗小

网络开销小

非常适合高频通信

比如:

位置更新 30 次

机器人每秒:

如果用 HTTP:

30 次请求

太浪费。

WebSocket:

一个连接持续发

效率极高。

支持二进制数据

不仅能传字符串。

还能传:

图片

音频

视频

protobuf

激光雷达数据

延迟非常低

因为:

不用频繁握手

所以特别适合:

游戏

机器人控制

实时监控

WebSocket的缺点

长连接占资源(最大缺点)

HTTP:

请求结束连接关闭

WebSocket:

连接一直存在

如果:

10万个客户端

服务器就要维护:

10万个 socket

这很吃:

内存

文件描述符

CPU

服务器开发复杂

HTTP:

请求 → 响应

很简单。

WebSocket:

需要处理:

长连接

心跳

重连

断线检测

并发

异步IO

复杂很多。

五、用C++写基础的WebSocket服务端和客户端

Websocket的服务端

server.cpp

cpp 复制代码
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>

#include <iostream>

typedef websocketpp::server<websocketpp::config::asio> server;

using websocketpp::connection_hdl;

class WebSocketServer
{
public:
    WebSocketServer()
    {
        // 初始化 ASIO
        m_server.init_asio();

        // 设置消息回调
        m_server.set_message_handler(
            [this](connection_hdl hdl, server::message_ptr msg)
            {
                on_message(hdl, msg);
            });
    }

    void run(uint16_t port)
    {
        // 监听端口
        m_server.listen(port);

        // 开始接受连接
        m_server.start_accept();

        std::cout << "WebSocket Server start at port "
                  << port << std::endl;

        // 启动服务
        m_server.run();
    }

private:
    server m_server;

    void on_message(connection_hdl hdl,
                    server::message_ptr msg)
    {
        std::cout << "收到消息: "
                  << msg->get_payload()
                  << std::endl;

        // 回复客户端
        m_server.send(hdl,
                      "服务器收到消息: " + msg->get_payload(),
                      websocketpp::frame::opcode::text);
    }
};

int main()
{
    WebSocketServer server;

    server.run(9002);

    return 0;
}

Websocket的客户端

Client.cpp

cpp 复制代码
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

#include <iostream>

typedef websocketpp::client<websocketpp::config::asio_client> client;

using websocketpp::connection_hdl;

class WebSocketClient
{
public:
    WebSocketClient()
    {
        m_client.init_asio();

        // 连接成功回调
        m_client.set_open_handler(
            [this](connection_hdl hdl)
            {
                on_open(hdl);
            });

        // 消息回调
        m_client.set_message_handler(
            [this](connection_hdl hdl,
                   client::message_ptr msg)
            {
                on_message(hdl, msg);
            });
    }

    void connect(const std::string& uri)
    {
        websocketpp::lib::error_code ec;

        auto con = m_client.get_connection(uri, ec);

        if (ec)
        {
            std::cout << "连接失败: "
                      << ec.message()
                      << std::endl;
            return;
        }

        m_client.connect(con);

        m_client.run();
    }

private:
    client m_client;

    void on_open(connection_hdl hdl)
    {
        std::cout << "连接成功" << std::endl;

        // 发送消息
        m_client.send(hdl,
                      "你好,我是客户端",
                      websocketpp::frame::opcode::text);
    }

    void on_message(connection_hdl hdl,
                    client::message_ptr msg)
    {
        std::cout << "收到服务端消息: "
                  << msg->get_payload()
                  << std::endl;
    }
};

int main()
{
    WebSocketClient client;

    client.connect("ws://localhost:9002");

    return 0;
}
相关推荐
念何架构之路14 小时前
DNS和HTTP DNS
网络·网络协议·http
慕木沐15 小时前
【DNS 代理】:TUN 模式下的网络原理
网络协议
你觉得脆皮鸡好吃吗15 小时前
XSS渗透 Session
前端·网络·xss·网络安全学习
liux352815 小时前
Nginx 安全升级方案:1.22.0 → 1.30.1
网络·nginx·安全
艾莉丝努力练剑15 小时前
【QT】常用控件(三)Qt布局管理器(网格/表单/间隔器)
java·linux·运维·服务器·开发语言·网络·qt
梦奇不是胖猫16 小时前
[ 计算机网络 | 第三章 ] 数据链路层 04 交换式以太网
运维·服务器·网络·网络协议·计算机网络
艾莉丝努力练剑16 小时前
【Linux网络】Linux 网络编程:传输层TCP(一)
linux·运维·服务器·网络·tcp/ip·计算机网络
lys0796200016 小时前
kali linux 配置固定IP
linux·网络·tcp/ip