WebSocket—socket.io简单运用(react+webpack)个人实验

WebSocket 是什么?

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

Websocket是什么样的协议?

Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。

WebSocket 与 HTTP

HTTP 有 1.1 和 1.0 之说,也就是所谓的 keep-alive ,把多个 HTTP 请求合并为一个,但是 Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器,所以在握手阶段使用了 HTTP 。

一图看懂差异:
image.png

为什么需要WebSocket?

用以实现高效的实时Web应用

轮询(polling)

这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。

轮询:客户端按规定时间定时像服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。

 const getting = {
        url:'localhost:8080',
        dataType:'json',
        success:function(res) {
        console.log(res);
  }
};
//关键在这里,Ajax定时访问服务端,不断获取数据 ,这里是1秒请求一次。
window.setInterval(() => {$.ajax(getting)},1000);

长轮询(Long Polling)

长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。

Ajax长轮询属于Ajax轮询的升级版,在客户端和服务端都进行了一些改造,使得消耗更低,速度更快。不间断的通过Ajax查询服务端。

 var getting = {
        url:'localhost:8080',
        dataType:'json',
        success:function(res) {
         console.log(res);
         $.ajax(getting); //关键在这里,回调函数内再次请求Ajax
         }        
        //当请求时间过长,就再次调用ajax长轮询
        error:function(res){
        $.ajax($getting);
      }
};
$.ajax(getting);

流技术方案通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。

WebSocket-简单实例

WebSocket 客户端
在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。

//客户端代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<input id="sendText" type="text"/>
<button id="wsSend">发送</button>
<script>
  // 初始化一个 WebSocket 对象
  var ws = new WebSocket("ws://localhost:8001/");

  function showMessage(msg) {
    var div = document.createElement("div")
    div.innerHTML = msg.msg
    if (msg.type === "enter") {
      div.style.color = "red"
    } else if (msg.type === "left") {
      div.style.color = "blue"
    }
    if (msg.type === "text") {
      div.innerHTML = msg.name + ":" + msg.msg
    }
    document.body.appendChild(div)
  }

  // 建立 web socket 连接成功触发事件
  ws.onopen = function () {
    // 使用 send() 方法发送数据
    document.getElementById("wsSend").onclick = function () {
      var txt = document.getElementById("sendText").value;
      if (txt) {
        ws.send(txt)
      }
    }
  };

  // 接收服务端数据时触发事件
  ws.onmessage = function (msg) {
    var received_msg = JSON.parse(msg.data);
    if (received_msg) {
      showMessage(received_msg);
    }
  };

  // 断开 web socket 连接成功触发事件
  ws.onclose = function () {
    alert("连接已关闭...");
  };


</script>
</body>
</html>
//基于nodejs-websocket
var ws = require("nodejs-websocket")


var clientCount = 0
// Scream server example: "hi" -> "HI!!!"
var server = ws.createServer(function (conn) {    //连接后执行
  console.log("New connection")
  clientCount++;
  conn.name = "user" + clientCount
  var msg = {}
  msg.name = conn.name
  msg.msg = conn.name + "进入了"
  msg.type = "enter"
  broadCast(JSON.stringify(msg))
  conn.on("text", function (str) {    //接收到消息出发
    msg.msg = str
    msg.type = "text"
    broadCast(JSON.stringify(msg))
  })
  conn.on("close", function (code, reason) {  //连接断开后出发
    console.log("Connection closed")
    msg.type = "left"
    msg.msg = conn.name + "离开了"
    broadCast(JSON.stringify(msg))
  })
  conn.on("error", function (err) {     //异常时出发
    console.log("error")
    console.log(err)
  })
}).listen(8001)

function broadCast(str) {
  server.connections.forEach(function (connection) {
    connection.sendText(str)
  })
}

启动服务
npm run server

实现结果
image.png

通过代码不难发现当交互数据较复杂时,单纯使用webSocket存在对数据的大量处理,而这部分逻辑并不建议出现在业务代码中。可使用现有的库处理这些逻辑,这里使用socket.io 来代替单纯使用webSocket。

Socket.io

socket.io是一个跨浏览器支持WebSocket的实时通讯的JS库。https://socket.io/docs/

使用socket.io

以下为官方简单例子
npm install --save socket.io
Server (app.js)

var app = require('http').createServer()
var io = require('socket.io')(app);

app.listen(80);

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

Client (index.html)

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

socket.io使websocket的交互可以直接传递对象,通过标识符区分消息的类型,不再需要在客户端对交互数据进行处理。
使用webpack等打包编译工具时,客户端引入socket.io
npm install --save socket.io-client

import io from "socket.io-client"

个人实验—简单聊天室

以下为个人基于react、webpack、socket.io实现的一个简单聊天室功能。
首先搭建一个react+webpack的项目,这里略过...
npm install --save socket.io-client
在jsx中引入

import * as React from "react";
import io from "socket.io-client"
//实例化socket对象
const socket = io('ws://localhost:8001/'); //连接地址 这里为本地服务地址
//socket核心逻辑代码
initSocket = () => {
    socket.on('enter', (data) => {      //用户进入时触发
      this.showMessage(data, "enter")
    });
    socket.on('message', (data) => {   //接收到消息是触发
      this.showMessage(data, "message")
    });
    socket.on('leave', (data) => {        //用户离开时触发
      this.showMessage(data, "leave")
    });
    socket.on('enterSelf', (data) => {    //用以区分是当前用户发送消息或其他用户
      this.setState({personName: data.name})
    });
  }
send = (inputText) => {
    if (inputText) {
      socket.emit('message', inputText); //发送消息
      this.setState({sendValue: null})
    }
  }

render() {
    const {messageBox} = this.state
    return (<div style={{overflow: "hidden"}}>
        <Chatting>
          <p className="title">J聊天室</p>
          {this.renderMessageBox(messageBox)}
          <Search
            placeholder="请输入要发送的消息"
            enterButton="发送"
            value={this.state.sendValue}
            onChange={(e) => this.setState({sendValue: e.target.value})}
            onSearch={value => this.send(value)}
          />
        </Chatting>
      </div>
    )
  }

以上省略了样式及部分页面渲染代码。

server部分

npm install --save socket.io

var app = require('http').createServer()
var io = require('socket.io')(app);

app.listen(8001);

var clientCount = 0

io.on('connection', function (socket) {
  clientCount++;
  socket.name = 'user' + clientCount
  io.emit('enter', {name: socket.name});     //用户连接成功时触发发送登陆消息
  socket.emit('enterSelf', {name: socket.name})
  socket.on('message', function (msg) {        //服务端接受消息,返回各个客户端消息
    io.emit('message', {name: socket.name, message: msg});
  });
  socket.on("disconnect", function () {       //用户断开连接时,触发离开消息
    io.emit('leave', {name: socket.name});
  })
});

npm run dev,npm run server
服务启动后页面效果如下

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容