c++应用网络编程之十七WebSocket

一、介绍

记得在很早前,写一个用Silverlight实现的前端程序,需要处理一个类似心跳的问题,始终找不到好的方法,后来发现了WebSocket才解决了。后来也间断的使用过,但从来没有在产品中广泛使用过。

可以这样说,对WebSocket,属于入门较早,但经验并不丰富的一种技术。大多是作为一种辅助的实现来完成一些特定的场景下的应用。

二、WebSocket

WebSocket是一种应用层的协议,它是客户端和服务端通过建立一个全双工的持久的连接来进行通信。所以这就意味着建立Websocket连接后,双方就可以实时的进行数据通信了。解决了HTTP协议那样必须每次由客户端发起,服务端才能响应,然后通信完成后就断开的情况。其具体的连接过程如下:

  1. 客户端通过HTTP发起一个特殊的握手请求,其中的头包含类似"Upgrade:websocket Connection:Upgrade Sec-WebSocket-Key Sec-WebSocket-version:13"等,请求升级到WebSocket
  2. 服务器如果支持则响应状态码------101 Switching Protocols,表示可以进行协议转换
  3. 双方开始进行WebSocket协议并利用建立好的TCP连接进行双向通信

有网络开发经验的可能会问,它和Socket通信有什么不同呢?明白了这些不同,其实也就知道了WebSocket的特点。

  1. WebSocket是应用层协议,而Socket是一个接口,它是对传输层协议TCP/IP的通信对象
  2. 它需要发出一个HTTP的特殊请求来进行再转成Socket。而Socket直接发起通信
  3. 它和socket一样都是全双工的长连接。但WebSocket传输的是数据流,而WebSocket传输的是消息或帧
  4. WebSocket通过HTTP发起,浏览器原生支持,防火墙一般不拦截,但Socket则不是,需要防火墙处理

可以把Websocket看成是对Socket在某种特定场景下的封装或者抽象。它的底层仍然是依赖TCP Socket进行数据通信的。

三、和Http与Tcp区别和联系

可以这样说,这三者之间的区别的核心不同在于协议的不同,HTTP和WebSocket基础都是建立在TCP协议之上的。而HTTP和WebSocket又分属于应用层上的不同协议。WebSocket不能直接单独发起,需要由HTTP发起后才能进行连接。看下面的表格说明:

对比项 TCP(传输层) HTTP(应用层) WebSocket(应用层)
协议层级 底层基础协议 基于 TCP 基于 TCP(由HTTP升级)
通信方向 全双工 半双工(单向请求) 全双工
谁先发数据 双方均可(需自行约定) 客户端单向发起 双方均可
头部大小 20~60 字节 一般很大(几百字节) 极小(2~14字节)
连接生命周期 长短均可 短连接或长轮询 长连接
数据边界 字节流(不确定,可能粘包) 报文(边界确定) 帧(边界确定)
防火墙 非标准端口可能被拦 标准端口(80/443)无拦截 标准端口(80/443)无拦截

四、作用和应用场景

在传统的HTTP协议中,一般是使用"请求-响应"模式或"轮询"(Polling)技术。但在实际的应用场景中会出现很多需要长时间连接通信的情况。比如一个页面中有很多个通信元素(如几百图片或JS等),浏览器需要重复建立和断开N次连接,消耗的资源是不可想象的(大量的重复的元数据被传输)。还有,如果想在页面上实现聊天互动,传统的轮询模式,无法解决实时的难和长时间不通信仍然需要定时轮询的情况(严重浪费资源)。虽然后面推出了长轮询,但只解决了一小部分的问题。同样,HTTP的长连接虽然解决了部分的效率问题,但由于其仍然属于半双工,仍然也无法完全解决上面的各种情况。

所以WebSocket作用就是提高实时性、提高通信效率以及降低资源消耗。其具体的应用场景如下:

  1. 实时聊天及社交场景
    比如在线IM,在线客服以及群聊以及直播的弹幕等等
  2. 在线协作
    比如在线文档编辑,一个人修改其它人能即时看到结果;还有其它如在线白板等等
  3. 在线游戏
    游戏强调的就是实时性,否则别人都砍死角色了,对方还没看到。所以游戏的状态同步、广播、操作等等都需要实时性
  4. 在线监控
    既然是监控,就必须是延迟越低越好。保证对监控对象的随时把控
  5. 数据推送
    这个就比较常见了,比如在线的股票期货的行情、比赛的计分以及服务器状态的监控等等,都需要及时的将数据传送到指定的页面上

五、例程

下面给出一个简单的聊天的例子,首先是客户端:

js 复制代码
//客户端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>WebSocket 实时聊天</title>
  <style>
    body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; }
    #messages { border: 1px solid #ccc; height: 300px; overflow-y: auto; padding: 10px; background: #f9f9f9; }
    #input-area { display: flex; margin-top: 10px; }
    #input-area input { flex: 1; padding: 8px; }
    #input-area button { padding: 8px 16px; margin-left: 10px; }
    .msg { margin: 4px 0; }
    .system { color: #888; font-style: italic; }
  </style>
</head>
<body>
  <h2> WebSocket 聊天室</h2>
  <div id="messages"></div>
  <div id="input-area">
    <input type="text" id="input" placeholder="输入消息...">
    <button id="sendBtn">发送</button>
  </div>

  <script>
    // 连接 WebSocket 服务器(注意 ws 协议)
    const ws = new WebSocket('ws://localhost:8080');

    const messagesDiv = document.getElementById('messages');
    const input = document.getElementById('input');
    const sendBtn = document.getElementById('sendBtn');

    // 接收服务器消息
    ws.onmessage = function(event) {
      const msg = event.data;
      const div = document.createElement('div');
      div.className = 'msg';
      // 系统消息添加样式
      if (msg.includes('欢迎')) {
        div.classList.add('system');
      }
      div.textContent = msg;
      messagesDiv.appendChild(div);
      // 自动滚动
      messagesDiv.scrollTop = messagesDiv.scrollHeight;
    };

    // 发送消息
    function sendMessage() {
      const text = input.value.trim();
      if (text === '') return;
      ws.send(text);
      input.value = '';
    }

    sendBtn.addEventListener('click', sendMessage);
    input.addEventListener('keydown', function(e) {
      if (e.key === 'Enter') sendMessage();
    });

    // 连接关闭
    ws.onclose = function() {
      const div = document.createElement('div');
      div.className = 'msg system';
      div.textContent = '连接断开';
      messagesDiv.appendChild(div);
    };
  </script>
</body>
</html>

下面是服务端:

js 复制代码
//服务端
const WebSocket = require('ws');

// 创建WebSocket服务器,监听8080端口
const wss = new WebSocket.Server({ port: 8080 });

console.log('WebSocket服务器启动,监听端口8080');

// 客户端连接
wss.on('connection', function connection(ws) {
  console.log('新客户端连接成功!');

  // 收到消息
  ws.on('message', function incoming(message) {
    // 消息转换为字符串
    const msgStr = message.toString();
    console.log('收到消息:', msgStr);

    // 广播
    wss.clients.forEach(function each(client) {
      if (client.readyState === WebSocket.OPEN) {
        client.send(msgStr);
      }
    });
  });

  // 客户端断开
  ws.on('close', function close() {
    console.log('客户端连接已断开');
  });

  // 发送欢迎消息
  ws.send('欢迎进入聊天室!');
});

这种小程序很多,这里的小程序简单至极,只是一个示例而已。

六、总结

其实很多技术的出现不是说拍脑门出来的,它是实际的应用场景倒逼出来的。比如有了互联网才有了后来的移动互联网。技术始终是围绕着实际的需求展开的。正如诺基亚的广告:科技以人为本!

相关推荐
小白学大数据1 小时前
把 Modbus 轮询塞进 Trio 的异步循环:内存映射与定时采集实战
网络·python·搜索引擎
haolin123.2 小时前
类和对象(下)
c语言·开发语言·c++
皓月斯语2 小时前
P5736 【深基7.例2】质数筛
c++·算法·题解
让学习成为一种生活方式2 小时前
烟草生产功能性 MHC II 蛋白--Plant Biotechnology Journal
网络
weixin_727535622 小时前
HTTP 八股文:从三次握手到浏览器渲染的硬核拆解
网络·网络协议·http
Ivan CloudBay2 小时前
网站为什么会使用缓存?
服务器·网络·缓存·云服务器
鸿芯微控科技2 小时前
MFC质量流量控制器流量不准怎么排查?设定值、反馈值与参考流量对比,附Python代码
c++·python·mfc
拂拉氏2 小时前
【知识讲解】 让指针拥有“自我意识”?--C++智能指针相关内容讲解
c++·智能指针
CVer儿2 小时前
c++二级指针
开发语言·c++