WebSocket 学习(三)--用nodejs搭建服务器
前面已经学习了WebSocket API,包括事件、方法和属性。详情:WebSocket(二)--API WebSocket是基于事件驱动,支持全双工通信。下面通过三个简单例子体验一下。
简单开始
1.安装node。https://nodejs.org/en/
2.安装ws模块
ws:是nodejs的一个WebSocket库,可以用来创建服务。 https://github.com/websockets/ws
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的ws创建一个WebSocket服务器。且可以接受客户端的消息。那么下面这个例子演示股票的实时更新。客服端只需要连接一次,服务器端会不断地发送新数据,客户端收数据后更新UI.页面如下,有五只股票,开始和停止按钮测试连接和关闭。
服务端:
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股的颜色是反的,即红跌绿涨)
实时聊天
上面的例子是连接建立之后,服务端不断给客户端发送数据。接下来例子是一个简单的聊天室类的例子。可以建立多个连接。
1.安装node-uuid模块,用来给每个连接一个唯一号。
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按钮用来修改昵称。
- <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);
- }
- }
运行结果:
页面关闭之后,连接马上断开。
这种实时响应的体验简直不能太爽,代码也清爽了,前端体验也更好,客户端不用一直发请求,服务端不用等着被轮询。
小结:上面例子的代码都很好理解,接下来学习WebSocket协议。
源码:http://pan.baidu.com/s/1c2FfKbA
源码:http://pan.baidu.com/s/1o8KRmUQ 加入了socket.io的实现。
API:WebSocket API
相关:websoket使用Protocol Buffers3.0传输
WebSocket 学习(三)--用nodejs搭建服务器的更多相关文章
- nodejs搭建服务器 和 操作数据库
1.express框架:是一个简洁而灵活的 node.js Web应用框架.一般的项目都是基于这个框架开发的.http://www.runoob.com/nodejs/nodejs-express-f ...
- WebSocket 学习--用nodejs搭建服务器
最简单的socket服务端 var net = require("net"); server1 = net.createServer(function(client){ clien ...
- 利用nodejs搭建服务器,测试AJAX
最近学习了AJAX一直没有进行过测试,前今天了解了Noejs搭建本地服务器下就尝试了一下.通过AJAX请求的方式获取HTTP服务器返回数据的代码 首先创建一个serve.js的文件.并写入以下代码. ...
- 使用nodejs搭建服务器显示HTML页面
首先安装express 在命令行输入:npm install express -g 安装完成后可以查看安装情况:npm ls -g 然后创建server.js文件 var express = requ ...
- Swoole学习(三)Swoole之UDP服务器的创建
环境:Centos6.4,PHP环境:PHP7 <?php //创建UCP服务器(UDP服务器相对于TCP服务器通信更可靠些) /** * $host 是swoole需要监听的ip,如果要监听本 ...
- spring学习 三 框架的搭建
1 导入jar包 spring启来最少要5个包,四个核心包和一个依赖的日志包 2 创建配置文件 在dynamic web project下的src目录下,创建一个spring的xml配置文件,名称可以 ...
- 使用NodeJs搭建的小型web应用
原文英文链接:http://www.nodebeginner.org 中文翻译链接:http://www.nodebeginner.org/index-zh-cn.html 学习链接:一本全面的Nod ...
- 用nodejs搭建类似于C++的服务器后台.类似网易pomelo
实际的情况,用nodejs跑业务,非常的快,只要用好其无阻塞和回调这两点,处理速度真的是杠杠的. 从年初开始,我用nodejs搭建了类似C++的服务器后台,也想和做同样的事情的朋友分享,本服务平台因为 ...
- SpringMVC 学习 十 SSM环境搭建(三)springMVC文件配置
SpringMVC文件配置的详细过程,可以查看springMVC环境搭建的注解配置篇<springMVC学习三 注解开发环境搭建> <?xml version="1.0&q ...
随机推荐
- (转载)开始iOS 7中自动布局教程(一)
这篇教程的前半部分被翻译出来很久了,我也是通过这个教程学会的IOS自动布局.但是后半部分(即本篇)一直未有翻译,正好最近跳坑翻译,就寻来这篇教程,进行翻译.前半部分已经转载至本博客,后半部分即本篇.学 ...
- C#_闭包陷阱
如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中. 即将for循环中的变量i修改成了引用闭包对象的公共变量i.这样一来,即使代码执行后离开了原局部变量i的 ...
- python获取当前时间的用法
1.先导入库:import datetime 2.获取当前日期和时间:now_time = datetime.datetime.now() 3.格式化成我们想要的日期:strftime() 比如:“2 ...
- DBImport v3.3 中文版发布:数据库数据互导及文档生成工具(IT人员必备)
前言: 好久没写文了, 距离上一篇文章是3个月前的事了,虽然工作很忙,主要还是缺少写作的内容和激情,所以没怎么动手. 之前有一个来月不断面试不同层次来应聘的人员,很有想写文的冲动,后来还是忍住了. 估 ...
- 【性能为王】从PHP源码剖析array_keys和array_unique
之前在[译]更快的方式实现PHP数组去重这篇文章里讨论了使用array_flip后再调用array_keys函数替换直接调用array_unique函数实现数组去重性能较好.由于原文没有给出源码分析和 ...
- Hadoop学习笔记—21.Hadoop2的改进内容简介
Hadoop2相比较于Hadoop1.x来说,HDFS的架构与MapReduce的都有较大的变化,且速度上和可用性上都有了很大的提高,Hadoop2中有两个重要的变更: (1)HDFS的NameNod ...
- 关于新书《修炼之道:.NET开发要点精讲》的各种说明
索引 新书介绍 新书封面 新书目录 试读章节 原稿试读 网购地址 规格参数 反馈方式 一些感谢 附加说明 1.新书介绍 从2013年年底到2014年9月,历时将近10个月,这本书终于看到了“出版发行” ...
- 企业IT管理员IE11升级指南【7】—— Win7和Win8.1上的IE11功能对比
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 新浪计数业务之Redis
今天听一个同事说新浪使用的是Redis,于是自己将研究的过程整理出来以备后用. 我们都知道微博这玩意儿现在很火,新浪作为国内最早使用redis,并且是国内最大的redis使用者,当然备受人们关注.新浪 ...
- Android开发学习之路-Volley源码解析
从简单的StringRequest入手看看Volley的工作机制. 先简单说下Volley的用法: ① 获取一个RequestQueue mRequestQueue = Volley.newReque ...