websocket深入浅出

websocket深入浅出websocket简介websocket是什么答:它是一种网络通信协议,是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。为什么需要websocket?疑问?我们已经有了HTTP协议,为什么还需要另一个协议?它能带来什么好处?答:因为HTTP协议有一个缺陷:通信只能由客户端发起我们都知道轮询的效率低,非常浪费资源(因为必须不停连接,或者HTTP…

大家好,又见面了,我是你们的朋友全栈君。

websocket简介

websocket是什么

答: 它是一种网络通信协议,是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

为什么需要websocket? 疑问? 我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

答:

  • 因为 HTTP 协议有一个缺陷:通信只能由客户端发起
  • 我们都知道轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开), 因此websocket应运而生。

简介

WebSocket用于在Web浏览器和服务器之间进行任意的双向数据传输的一种技术。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程。其目的是在WebSocket应用和WebSocket服务器进行频繁双向通信时,可以使服务器避免打开多个HTTP连接进行工作来节约资源,提高了工作效率和资源利用率。
websocket深入浅出

WebSocket目前支持两种统一资源标志符wswss,类似于HTTP和HTTPS。

实现原理

浏览器发出webSocket的连线请求,服务器发出响应,这个过程称为握手,握手的过程只需要一次,就可以实现持久连接。

握手与连接

浏览器发出连线请求,此时的request如下:

request

通过get可以表明此次连接的建立是以HTTP协议为基础的,返回101状态码。

如果不是101状态码,表示握手升级的过程失败了

101是Switching Protocols,表示服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送这个响应后的空档,将http升级到webSocket

其中UpgradeConnection字段告诉服务端,发起的是webSocket协议

Sec-WebSocket-Key是浏览器经过Base64加密后的密钥,用来和response里面的Sec-WebSocket-Accept进行比对验证

Sec-WebSocket-Version是当前的协议版本

Sec-WebSocket-Extensions是对WebSocket的协议扩展

服务器接到浏览器的连线请求返回结果如下:

response

UpgradeConnection来告诉浏览器,服务已经是基于webSocket协议的了,让浏览器也遵循这个协议

Sec-WebSocket-Accept是服务端确认后并加密后的Sec-WebSocket-Accept

至此,webSocket连接成功,接下来就是webSocket的协议了。

客户端的简单示例

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
}; 

ws.onerror = function(evt) {
  console.log("error!!!"); 
}; 

客户端的 API

  • 以下 API 用于创建 WebSocket 对象
var ws = new WebSocket('ws://echo.websocket.org');
  • websocket 属性
ws.readyState
CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

ws.bufferedAmount
只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
  • WebSocket 事件
事件       事件处理程      描述
open     ws.onopen      连接建立时触发
message  ws.onmessage   客户端接收服务端数据时触发
error    ws.onerror     通信发生错误时触发
close    ws.onclose     连接关闭时触发

如果要指定多个回调函数,可以使用addEventListener方法

ws.addEventListener('open', function (event) { 
   
  ws.send('Hello Server!');
});
ws.addEventListener("close", function(event) { 
   
  ...
  // handle close event
});
ws.addEventListener("message", function(event) { 
   
  var data = event.data;
  ...
  // 处理数据
});
  • websocket 方法
方法                  描述
ws.send()            使用连接发送数据
ws.close()           关闭链接

node 搭建服务器

ws模块

ws是一个websocket库,可以用了创建服务器。

新建server.js 输入以下代码

const WebSocket = require('ws');
 
const wss = new WebSocket.Server({ 
    port: 8181 });
 
wss.on('connection', function connection(ws) { 
   
  ws.on('message', function(message){ 
   
    wss.clients.forEach(function(ws){ 
    // 看这里看这里 wss.clients 拿到所有的连接
          ws.send(message) // 发送消息
      })
  })
});

新建index.html 这里只列举下js代码

<script> var ws = new WebSocket('ws://localhost:8181') ws.onopen = function(e){ 
     console.log('开始连接服务器') $('.btn-primary').click(function(){ 
     ws.send($('.form-control').val()) // 发送消息 }) ws.onmessage = function(msg){ 
     // 监听消息 $('body').append('<p>'+ msg.data +'</p>') } $('.btn-danger').click(function(){ 
     ws.close() // 关闭连接 }) } </script>

完整代码请戳github

这里使用ws模块实现了两个demo,运行server.js后请自行查看。

Socket.io

简介

Socket.io是一个webSocket库,目标是构建不同浏览器和移动设备上使用的实时应用。它会自动根据浏览器从webSocket ajax长轮询 ifrane流等各种方式选择最佳的方式。

特点

Socket.io主要有以下几点:

1、实时分析:将数据推送到客户端,这些客户端会被表示为实时计数器,图表或日志客户
2、实时通讯和聊天:几行代码就可以实现一个简单的聊天室
3、二进制流传输:支持任何形式的二进制文件传输,例如:图片,视频,音频等
4、文档合并:允许多个用户同时编辑一个文档,并且能够看到每个用户做出的修改

聊天室的实现

Socket.io上面有个入门的聊天室demo,基于node-http-server或者express,玩了koa以后,觉得koa很清爽,所以打算用koa来实现聊天室。

首先创建simple-koa-chat文件夹,用来存放我们的代码。

执行npm init -y命令生成package.json文件

执行npm i koa socket.io -D 安装koa和socket.io,并添加到devDependencies依赖

执行mkdir src && cd src && touch index.html创建src文件夹,并在里面创建index.html

执行cd../ && touch app.js,回到根目录下,创建app.js。

执行完后,在编辑器里面打开,此时目录结构如下:

目录结构

编辑app.js文件

	
	const Koa = require('koa')
	const app = new Koa()
	const fs = require('fs')
	const server = require('http').Server(app.callback())
	const io = require('socket.io')(server)
	
	
	app.use(async (ctx, next)=>{ 
   
		ctx.response.type = 'html'
		ctx.body = await fs.createReadStream('./src/index.html')
	})
	
	server.listen(8080, ()=>{ 
   
		console.log('open http://localhost:8080')
	});
	io.on('connection', function(socket){ 
   
		socket.on('chat message', function(msg){ 
   
			io.emit('chat message', mas)
		})
	})

编辑index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Socket.IO chat</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>
  <style> li{ 
     padding: 4px 10px; border: 1px solid #eee; width:fit-content; border-radius: 5px; margin-bottom: 10px; } </style>
  <body>
    <div class="container" style="margin-top:100px">
        <div class="panel panel-default">
            <div class="panel-heading">Socket.IO 简易聊天室</div>
            <div class="panel-body">
                <div class="col-md-12">
                    <ul class="messages pull-letf list-unstyled"></ul>
                    <form action="">
                      <div class="form-group">
                        <textarea class="form-control" placeholder="输入发言内容"></textarea>
                      </div>
                      <button type="button" class="btn btn-primary">发言</button>
                    </form>
                </div>
            </div>
          </div>
    </div>
    
    
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    <script> $(function () { 
     var socket = io(); $('.btn').click(function(){ 
     socket.emit('chat message', $('.form-control').val()); $('.form-control').val(''); }); // 监听 chat message事件  socket.on('chat message', function(msg){ 
     $('.messages').append($('<li>').text(msg)); }); }); </script>
  </body>
</html>

执行npm run start 启动我们的服务。

打开浏览器你可以看到如下的页面

简易聊天室

Socket.io API

Socket.io由两部分组成:

1、服务端 挂载或集成到nodeJS http服务器 socket.io

2、客户端 加载到浏览器的客户端 socket.io-client

先来说下服务端集成,分为简单的两步:

1、引入模块并实例化

// 这里使用koa框架,其他框架原理都一样

const Koa = require('koa')
const app = new Koa()

const http = require('http').Server(app.callback())
const io = require('socket.io')(http)

// 引入`koa`并且初始化,引入`http`模块,将`koa`的回调当作`http.Server`的回调函数,最后将http传入实例化一个`socket.io`。

2、设置连接后的操作

io.on('connection', function(socket){ 
   
  socket.on('chat', function(msg){ 
   
    socket.emit('client', msg)
  })
})

// io.on函数接收'connection'字符串做为客户端发起连接的事件,连接成功后,调用带有 socket参数的回调函数。接收一个chat自定义的事件,使用socket.emit方法发送消息

服务端集成好后,接下来是客户端

</body>标签中添加以下代码

<script src="/socket.io/socket.io.js"></script>
<script> var socket = io() socket.emit('chat', 'hello') socket.on('client', function(data){ 
     console.log(data) }) </script>
  
  <!-- 加载客户端的js文件,调用io() 函数, 初始化socket对象 发送chat事件到服务端,这时候服务端接收到了chat事件,并发出了client事件,浏览器接收到了client事件,将数据打印到了控制台上。 -->

流程大概如下:
流程

我给你,你给我,来而不往非礼也,哲学啊。

emit和on函数

通过上图可以看到,每端总会接收事件和发送事件,socket最关键的就是emit和on两个函数,所有的交互其实就是这两个函数的情感纠葛,你来我往。

emit用来发送一个事件(事件名称用字符串表示),名称可以自定义也可使用默认的事件名称,接着是一个对象,表示发送的内容,如:socket.emit('chat', {'name':'zhangsan'})

on用来接收一个事件(事件名称用字符串表示),然后是响应事件的回调函数,其中函数里面的参数就是收到的数据。如socket.on('chat',function(data){console.log(data)})

服务端默认事件一览

io.on('connection', function(socket){}) socket连接成功时触发,用于初始化

socket.on('message', function(data, callback){}) 接收客户端通过socket.send传送的消息,data是传输的消息,callback是收到消息后要执行的函数

socket.on('anything', function(data){}) 收到任何事件都会触发

socket.on('disconnect', function(){}) socket失去链接时触发,包括关闭浏览器,主动断开,掉线等情况

进阶

1、怎么实现私聊?

每个socket都会有一个唯一的id,私聊的实现方式就是找到这个socket对象,发送事件,浏览器接收事件就实现了私聊。

现在有A、B两个链接,B想发送给A,我们拿到A的id告诉服务器,我要发送给A,浏览器从socket数组里面找到这个对应的socket,然后发送事件。

// client
var obj = { 
   
  toKey:'A的Id',
  message:'我想告诉A'
}
socket.emit('sendOne', obj)

//server 
// 这里我们借助underscore库

socket.on('sendOne', function(obj){ 
   
  var ToSocket = _.findWhere(io.sockets.sockets, { 
   id:obj.id}) // 看这里看这里
  ToSocket.emit('toOne', obj)
})	

// 
socket.on('toOne', function(obj){ 
   
  // 这里写自己的逻辑 obj就是B私聊给A的信息
})

参考上面API,我们可以将聊天室一步步的丰富起来,添加更多的功能,最后它大概长这样
websocket深入浅出

代码请戳

简易聊天室在master分支,丰富后的聊天室在zjx分支,请自行查看

服务器端的实现

常用的 Node 实现有以下三种

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157572.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • MySQL自动化运维平台建设

    MySQL自动化运维平台建设MySQL自动化运维工具Inception一、Inception简介Inception是集审核、执行、回滚于一体的一个自动化运维系统,它是根据MySQL代码修改过来的,用它可以很明确的,详细的,准确的审核MySQL的SQL语句,它的工作模式和MySQL完全相同,可以直接使用MySQL客户端来连接,但不需要验证权限,它相对应用程序(上层审核流程系统等)而言,是一个服务器,在连接…

  • Autoware入门学习(一)——Autoware自动驾驶框架介绍

    Autoware入门学习(一)——Autoware自动驾驶框架介绍Autoware简介Autoware.AI是世界上第一个用于自动驾驶技术的“All-in-One”开源软件。它ROS1操作系统,并在Apache2.0许可下使用。主要包含以下模块:定位(Localization):通过结合GNSS和IMU传感器的3D地图和3D地图、SLAM算法来实现定位。检测(Detection):通过传感器融合算法和深度神经网络使用摄像机和激光雷达完成检测。预测和规划(PredictionandPlanning):基于概率机器人模型和基于规则的系统,部分还使用深度神经

  • 查看redis版本命令_redis如何使用

    查看redis版本命令_redis如何使用Centos7查看redis版本redis安装成功后,查看redis版本命令:redis-server-V即可查看redis版本实际我们查看时都会遇到这个问题:redis-cli:commandnotfound(其实就和window电脑命令提示行中提示的:不是内部命令一个意思,配置环境变量即可使用)以上问题其实就是说明redis-server-V不是linux的全局命令,只需要我们做个软链接即可(类似于win电脑中的环境变量)软链接命令:ln-s/home/redis

  • matlab内建函数怎么不同颜色,matlab分段函数不同颜色绘图

    matlab内建函数怎么不同颜色,matlab分段函数不同颜色绘图Matlab绘制分段函数图像functionfunc_baidu_56568133x=-200:200;y=(x0).*(x.^2+(1-x).^(1/4)-5);figure(1);plot(x,y)fh=@func_baidmatlab绘制分段函数图象k是数组,不能那样比较;逻辑与是&&;j最后应该是一个数组,不是一个数;修改如下:forx=30:1:350if0.015*x…

  • linux drupal 7安装教程,Linux上Drupal7安装教程

    linux drupal 7安装教程,Linux上Drupal7安装教程前言:国内用drupal的并不太多,网上资料也很少。要注意的是drupal尽量别使用apt来安装,特别是UbuntuLinux平台的drupal做出了一定的更改,会妨碍后期的学习和使用。在安装drupal前要先完成LAMP的搭建,如果没有安装可以参照我之前的文章http://www.linuxidc.com/Linux/2016-03/128983.htm或者使用tasksel安装,这里不再…

  • vue引入外部js文件并使用_为什么vue不使用ajax

    vue引入外部js文件并使用_为什么vue不使用ajax在一个组件内部需要引入一个js文件,如果放在index.html,每个组件都会有这个js,所以需要在组件内单独引入。下载静态文件下来后,放入文件夹:组件代码:<template><div><button@click=”compressImage”>点击调用方法</button></div></template><script>importImageCompressorfrom’@/

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号