用nodejs搭建websocket服务器


Posted in NodeJs onJanuary 23, 2017

 简单开始

1.安装node。https://nodejs.org/en/ 

2.安装ws模块

ws:是nodejs的一个WebSocket库,可以用来创建服务。 https://github.com/websockets/ws

用nodejs搭建websocket服务器

3.server.js

在项目里面新建一个server.js,创建服务,指定8181端口,将收到的消息log出来。

var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
  console.log('client connected');
  ws.on('message', function (message) {
    console.log(message);
  });
});

4.建立一个client.html。

在页面上建立一个WebSocket的连接。用send方法发送消息。

var ws = new WebSocket("ws://localhost:8181");
  ws.onopen = function (e) {
    console.log('Connection to server opened');
  }
  function sendMessage() {
    ws.send($('#message').val());
  }

页面: 

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>WebSocket Echo Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <link href="../bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" />
  <script src="../js/jquery-1.12.3.min.js"></script>
  <script src="../js/jquery-1.12.3.min.js"></script>
  <script src="../bootstrap-3.3.5/js/bootstrap.min.js"></script>
  <script>
  var ws = new WebSocket("ws://localhost:8181");
  ws.onopen = function (e) {
    console.log('Connection to server opened');
  }
  function sendMessage() {
    ws.send($('#message').val());
  }
  </script>
</head>

<body >
  <div class="vertical-center">
    <div class="container">
      <p> </p>
      <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
        <div class="form-group">
          <input class="form-control" type="text" name="message" id="message"
              placeholder="Type text to echo in here" value="" />
        </div>
        <button type="button" id="send" class="btn btn-primary"
            onclick="sendMessage();">
          Send!
        </button>
      </form>
    </div>
  </div>
</body>
</html>

运行之后如下,服务端即时获得客户端的消息。

用nodejs搭建websocket服务器

模拟股票

上面的例子很简单,只是为了演示如何运用nodejs的ws创建一个WebSocket服务器。且可以接受客户端的消息。那么下面这个例子演示股票的实时更新。客服端只需要连接一次,服务器端会不断地发送新数据,客户端收数据后更新UI.页面如下,有五只股票,开始和停止按钮测试连接和关闭。

用nodejs搭建websocket服务器

 服务端:

 1.模拟五只股票的涨跌。

var stocks = {
  "AAPL": 95.0,
  "MSFT": 50.0,
  "AMZN": 300.0,
  "GOOG": 550.0,
  "YHOO": 35.0
}
function randomInterval(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
var stockUpdater;
var randomStockUpdater = function() {
  for (var symbol in stocks) {
    if(stocks.hasOwnProperty(symbol)) {
      var randomizedChange = randomInterval(-150, 150);
      var floatChange = randomizedChange / 100;
      stocks[symbol] += floatChange;
    }
  }
  var randomMSTime = randomInterval(500, 2500);
  stockUpdater = setTimeout(function() {
    randomStockUpdater();
  }, randomMSTime);
}
randomStockUpdater();

2.连接建立之后就开始更新数据

wss.on('connection', function (ws) {
  var sendStockUpdates = function (ws) {
    if (ws.readyState == 1) {
      var stocksObj = {};
      for (var i = 0; i < clientStocks.length; i++) {
       var symbol = clientStocks[i];
        stocksObj[symbol] = stocks[symbol];
      }
      if (stocksObj.length !== 0) {
        ws.send(JSON.stringify(stocksObj));//需要将对象转成字符串。WebSocket只支持文本和二进制数据
        console.log("更新", JSON.stringify(stocksObj));
      }
      
    }
  }
  var clientStockUpdater = setInterval(function () {
    sendStockUpdates(ws);
  }, 1000);
  ws.on('message', function (message) {
    var stockRequest = JSON.parse(message);//根据请求过来的数据来更新。
    console.log("收到消息", stockRequest);
    clientStocks = stockRequest['stocks'];
    sendStockUpdates(ws);
  });

客户端:

建立连接:

var ws = new WebSocket("ws://localhost:8181");

onopen直接只有在连接成功后才会触发,在这个时候将客户端需要请求的股票发送给服务端。

var isClose = false;
  var stocks = {
    "AAPL": 0, "MSFT": 0, "AMZN": 0, "GOOG": 0, "YHOO": 0
  };
  function updataUI() {
    ws.onopen = function (e) {
      console.log('Connection to server opened');
      isClose = false;
      ws.send(JSON.stringify(stock_request));
      console.log("sened a mesg");
    }
    //更新UI
    var changeStockEntry = function (symbol, originalValue, newValue) {
      var valElem = $('#' + symbol + ' span');
      valElem.html(newValue.toFixed(2));
      if (newValue < originalValue) {
        valElem.addClass('label-danger');
        valElem.removeClass('label-success');
      } else if (newValue > originalValue) {
        valElem.addClass('label-success');
        valElem.removeClass('label-danger');
      }
    }
    // 处理受到的消息
    ws.onmessage = function (e) {
      var stocksData = JSON.parse(e.data);
      console.log(stocksData);
      for (var symbol in stocksData) {
        if (stocksData.hasOwnProperty(symbol)) {
          changeStockEntry(symbol, stocks[symbol], stocksData[symbol]);
          stocks[symbol] = stocksData[symbol];
        }
      }
    };
  }

  updataUI();

运行效果如下:只需要请求一次,数据就会不断的更新,效果是不是很赞,不用轮询,也不用长连接那么麻烦了。文章末尾会附上所有源码。

(美股的涨跌和A股的颜色是反的,即红跌绿涨)

用nodejs搭建websocket服务器

实时聊天

上面的例子是连接建立之后,服务端不断给客户端发送数据。接下来例子是一个简单的聊天室类的例子。可以建立多个连接。

1.安装node-uuid模块,用来给每个连接一个唯一号。

用nodejs搭建websocket服务器

2.服务端消息发送

消息类型分notification和message两种,前者是提示信息,后者是聊天内容。消息还包含一个id、昵称和消息内容。在上一节有学习到readyState有四个值,OPEN表示连接建立可以发送消息。如果页面关闭了,为WebSocket.CLOSE。

function wsSend(type, client_uuid, nickname, message) {
  for (var i = 0; i < clients.length; i++) {
    var clientSocket = clients[i].ws;
    if (clientSocket.readyState === WebSocket.OPEN) {
      clientSocket.send(JSON.stringify({
        "type": type,
        "id": client_uuid,
        "nickname": nickname,
        "message": message
      }));
    }
  }
}

3.服务端处理连接

每新增加一个连接,都会发送一条匿名用户的加入的提示消息,如果消息中带有“/nick” 认为这一个修改昵称的消息。然后更新客户端的昵称。其他都会当做聊天消息处理。

wss.on('connection', function(ws) {
  var client_uuid = uuid.v4();
  var nickname = "AnonymousUser" + clientIndex;
  clientIndex += 1;
  clients.push({ "id": client_uuid, "ws": ws, "nickname": nickname });
  console.log('client [%s] connected', client_uuid);
  var connect_message = nickname + " has connected";
  wsSend("notification", client_uuid, nickname, connect_message);
  console.log('client [%s] connected', client_uuid);
  ws.on('message', function(message) {
    if (message.indexOf('/nick') === 0) {
      var nickname_array = message.split(' ');
      if (nickname_array.length >= 2) {
        var old_nickname = nickname;
        nickname = nickname_array[1];
        var nickname_message = "Client " + old_nickname + " changed to " + nickname;
        wsSend("nick_update", client_uuid, nickname, nickname_message);
      }
    } else {
      wsSend("message", client_uuid, nickname, message);
    }
  });

处理连接关闭:

var closeSocket = function(customMessage) {
    for (var i = 0; i < clients.length; i++) {
      if (clients[i].id == client_uuid) {
        var disconnect_message;
        if (customMessage) {
          disconnect_message = customMessage;
        } else {
          disconnect_message = nickname + " has disconnected";
        }
        wsSend("notification", client_uuid, nickname, disconnect_message);
        clients.splice(i, 1);
      }
    }
  };
  ws.on('close', function () {
    closeSocket();
  });

4.客户端

 没有启动时,页面如下,change按钮用来修改昵称。

用nodejs搭建websocket服务器

<div class="vertical-center">
    <div class="container">
      <ul id="messages" class="list-unstyled"></ul>
      <hr/>
      <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
        <div class="form-group">
          <input class="form-control" type="text" id="message" name="message"
              placeholder="Type text to echo in here" value="" autofocus/>
        </div>
        <button type="button" id="send" class="btn btn-primary"
            onclick="sendMessage();">
          Send Message
        </button>

      </form>
      <div class="form-group"><span>nikename:</span><input id="name" type="text" /> <button class="btn btn-sm btn-info" onclick="changName();">change</button></div>
    </div>
  </div>

js:

//建立连接
    var ws = new WebSocket("ws://localhost:8181");
    var nickname = "";
    ws.onopen = function (e) {
      console.log('Connection to server opened');
    }
    //显示
    function appendLog(type, nickname, message) {
      if (typeof message == "undefined") return;
      var messages = document.getElementById('messages');
      var messageElem = document.createElement("li");
      var preface_label;
      if (type === 'notification') {
        preface_label = "<span class=\"label label-info\">*</span>";
      } else if (type == 'nick_update') {
        preface_label = "<span class=\"label label-warning\">*</span>";
      } else {
        preface_label = "<span class=\"label label-success\">"
        + nickname + "</span>";
      }
      var message_text = "<h2>" + preface_label + "  "
      + message + "</h2>";
      messageElem.innerHTML = message_text;
      messages.appendChild(messageElem);
    }
    //收到消息处理
    ws.onmessage = function (e) {
      var data = JSON.parse(e.data);
      nickname = data.nickname;
      appendLog(data.type, data.nickname, data.message);
      console.log("ID: [%s] = %s", data.id, data.message);
    }
    ws.onclose = function (e) {
      appendLog("Connection closed");
      console.log("Connection closed");
    }
    //发送消息
    function sendMessage() {
      var messageField = document.getElementById('message');
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(messageField.value);
      }
      messageField.value = '';
      messageField.focus();
    }
    //修改名称
    function changName() {
      var name = $("#name").val();
      if (ws.readyState === WebSocket.OPEN) {
        ws.send("/nick " + name);
      }
    }

 运行结果:

 页面关闭之后,连接马上断开。

用nodejs搭建websocket服务器

 这种实时响应的体验简直不能太爽,代码也清爽了,前端体验也更好,客户端不用一直发请求,服务端不用等着被轮询。

 小结:上面例子的代码都很好理解,接下来学习WebSocket协议。

demo下载:http://xiazai.3water.com/201701/yuanma/websocket_3water.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
nodejs获取本机内网和外网ip地址的实现代码
Jun 01 NodeJs
轻松创建nodejs服务器(10):处理上传图片
Dec 18 NodeJs
nodejs微信公众号支付开发
Sep 19 NodeJs
nodejs redis 发布订阅机制封装实现方法及实例代码
Dec 15 NodeJs
详解nodeJS中读写文件方法的区别
Mar 06 NodeJs
nodejs+express实现文件上传下载管理网站
Mar 15 NodeJs
详解nodejs微信公众号开发——2.自动回复
Apr 10 NodeJs
nodejs socket实现的服务端和客户端功能示例
Jun 02 NodeJs
详解nodejs的express如何自动生成项目框架
Jul 12 NodeJs
nodejs实现的连接MySQL数据库功能示例
Jan 25 NodeJs
Nodejs模块的调用操作实例分析
Dec 25 NodeJs
nodejs通过钉钉群机器人推送消息的实现代码
May 05 NodeJs
NodeJS遍历文件生产文件列表功能示例
Jan 22 #NodeJs
nodejs实现发出蜂鸣声音(系统报警声)的方法
Jan 18 #NodeJs
nodejs的压缩文件模块archiver用法示例
Jan 18 #NodeJs
nodejs 实现钉钉ISV接入的加密解密方法
Jan 16 #NodeJs
简单实现nodejs上传功能
Jan 14 #NodeJs
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
Jan 12 #NodeJs
NodeJS实现客户端js加密
Jan 09 #NodeJs
You might like
PHP中str_replace函数使用小结
2008/10/11 PHP
ThinkPHP3.1新特性之动态设置自动完成及自动验证示例代码
2014/06/23 PHP
php获取指定范围内最接近数的方法
2015/06/02 PHP
PHP封装的HttpClient类用法实例
2015/06/17 PHP
php设计模式之工厂方法模式分析【星际争霸游戏案例】
2020/01/23 PHP
php 多进程编程父进程的阻塞与非阻塞实例分析
2020/02/22 PHP
jQuery 版元素拖拽原型代码
2011/04/25 Javascript
javascript动态加载实现方法一
2012/08/22 Javascript
删除条目时弹出的确认对话框
2014/06/05 Javascript
JavaScript中的值类型详细介绍
2014/12/29 Javascript
JavaScript中的toLocaleLowerCase()方法使用详解
2015/06/06 Javascript
js表单提交和submit提交的区别实例分析
2015/12/10 Javascript
又一款js时钟!transform实现时钟效果
2016/08/15 Javascript
浅谈js中function的参数默认值
2017/02/20 Javascript
详解angularJs模块ui-router之状态嵌套和视图嵌套
2017/04/28 Javascript
微信小程序倒计时功能实现代码
2017/11/09 Javascript
vue自定义指令的创建和使用方法实例分析
2018/12/04 Javascript
jQuery实现模拟搜索引擎的智能提示功能简单示例
2019/01/27 jQuery
解决LayUI加上form.render()下拉框和单选以及复选框不出来的问题
2019/09/27 Javascript
js实现自定义右键菜单
2020/05/18 Javascript
JavaScript实现HTML导航栏下拉菜单
2020/11/25 Javascript
[05:03]2018DOTA2亚洲邀请赛主赛事首日回顾
2018/04/04 DOTA
[02:16]完美世界DOTA2联赛PWL S3 集锦第三期
2020/12/21 DOTA
[04:40]DOTA2-DPC中国联赛1月26日Recap集锦
2021/03/11 DOTA
Python中的列表知识点汇总
2015/04/14 Python
Python中函数及默认参数的定义与调用操作实例分析
2017/07/25 Python
python 读取二进制 显示图片案例
2020/04/24 Python
css3打造一款漂亮的卡哇伊按钮
2013/03/20 HTML / CSS
LocalStorage记住用户和密码功能
2017/07/24 HTML / CSS
家得宝官网:The Home Depot(全球最大的家居装饰专业零售商)
2018/12/17 全球购物
波兰在线杂货店:Polski Koszyk
2019/11/02 全球购物
英语导游词
2015/02/13 职场文书
英文产品推荐信
2015/03/27 职场文书
2015年社区妇联工作总结
2015/04/21 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL
解决Jenkins集成SonarQube遇到的报错问题
2021/07/15 Java/Android