• 将js进行到底:node学习7


    Node.js之Websocket技术

    我第一次听说websocket之时,HTML5标准尚未公布,当时只有少数前瞻性浏览器携带了这个API。

    我对websocket最大的印象是,他可以解决我对“在线聊天系统”开发的疑惑(我一直想不通http如何保持长连接),这样我们无需使用轮询ajax和php无限循环去模拟,还记得2014年初那会我写了一个在线聊天室,那时候我真的应该用websocket技术的,php无限轮询的方式,只要3-4个人在线就可以让linux+apache服务器崩溃。

    很可惜,我没有在php上使用过Websocket API,今天是我第一次将这个技术用来实践,使用node.js实现

    引入模块

    package.json

    {
        "name":"chat-websocket",
        "version":"0.0.1",
        "description":"use websocket to create a char server",
        "dependencies":{
            "express":"latest",
            "express-ws":"latest"
        }
    }
    

    在引入http必备的express框架后,再引入基于express的中间件——express-ws

    express-ws是express上的websocket中间件,为express提供了websocket请求处理功能

    另外注意:《了不起的node.js》一书中使用的是websocket.io模块,两个东西原理,方法都差不多,我个人觉得io那个老了点,我选择了express-ws模块!主要是因为其与express配合效果更佳,专门为express而设计的中间件,何乐而不用呢?

    做个测试

    先来看看,node中使用express-ws的基本套路:

    index.js

    var express = require("express");
    
    //创建express下的http服务
    var app = express();
    //关联express-ws中间件
    var expressWs = require("express-ws")(app);
    
    //express托管静态文件
    app.use(express.static(__dirname+'/views',{'index':'index.html'}));
    
    
    app.ws('/ws',function(ws,req){
        ws.on('message',function(msg){
            console.log(msg);
        })
    });;
    
    app.listen(80);
    

    这个index.js是服务端代码

    解析:

    • 先引入express模块,并使用express()方法获得app对象
    • 引入express-ws并构造对象,构造函数传入的是app对象,意思大概是绑定到express服务器上
    • 通常接受http请求使用的是app.use(),而websocket请求则使用app.ws(),该方法就是中间件扩展的
    • message事件监听客户端发送的数据,并打印到终端上

    views/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试</title>
    </head>
    <body>
        <input type="text" value="" id="msg">
        <button id="submit">提交</button>
    </body>
    <script>
        window.onload=function(){
            var ws = new WebSocket('ws://localhost/ws')
            var sub = document.getElementById("submit");
            var msg = document.getElementById("msg")
            sub.addEventListener("click",function(){
                ws.send(msg.value);
            });
            ws.addEventListener("open",function(){
                alert("WebSocket has been opened!");
            })
        }
    </script>
    </html>
    

    这些为前端代码:实现了一个简单输入框,输入后主动通过websocket发送给服务端,服务端那边会打印再console中

    效果:

    这里写图片描述

    测试成功,每一次点击提交都会显示!

    WebSocket开发在线聊天室

    功能点

    1. 用户进入输入用户名可开始聊天
    2. 登入后提示当前在线用户
    3. 聊天内容会广播给聊天室其他在线用户
    4. 用户进入后提示xxx用户进入聊天室
    5. 用户关闭后提示xxx用户退出聊天室

    package.json同上不变,具体设计逻辑参考另一篇博客,我用TCP API实现的聊天室程序http://www.cnblogs.com/devilyouwei/p/8423961.html

    前端:index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试</title>
        <style>
            #inchat{
                display:none;
            }
            ul{
                list-style-type:none;
            }
        </style>
    </head>
    <body>
        <div id="inputname">
            <input type="text" value="" id="msg" placeholder="输入一个聊天昵称">
            <button id="submit">提交</button>
        </div>
        <div id="inchat">
            <ul id="chat-content">
            </ul>
            <textarea id="sendContent" value="">
            </textarea>
            <button id="send">发送</button>
        </div>
    </body>
    <script>
        window.onload=function(){
            var ws = new WebSocket('ws://localhost/ws');//需要修改为相应地址
            var sub = document.getElementById("submit");
            var msg = document.getElementById("msg")
            var ul  = document.getElementById("chat-content");
            var send = document.getElementById("send");
            var sendContent = document.getElementById("sendContent");
            sendContent.value="";
            sub.addEventListener("click",function(){
                ws.send(msg.value);
            });
            send.addEventListener("click",function(){
                ws.send(sendContent.value);
                sendContent.value=""
            });
            ws.addEventListener("open",function(){
                alert("WebSocket has been opened!");
            })
            ws.addEventListener("message",function(e){
                var res = JSON.parse(e.data);
                //如果正在聊天中
                if(res.ischat){
                    var li = document.createElement("li");
                    li.innerText = res.info;
                    ul.appendChild(li);
                }else{
                    if(res.status == 1){
                        document.getElementById("inputname").style.display="none";
                        document.getElementById("inchat").style.display="block";
                    }else{
                        alert(res.info);
                    }
                }
            });
        }
    </script>
    </html>
    

    注意:放到公网访问需要把localhost改成ip或者网址!

    后端:index.js

    var express = require("express");
    
    //创建express下的http服务
    var app = express();
    //关联express-ws中间件
    var expressWs = require("express-ws")(app);
    
    var users = {};
    var count = 0;
    
    //express托管静态文件
    app.use(express.static(__dirname+'/views',{'index':'index.html'}));
    
    //用ws方法而不是use方法
    app.ws('/ws',function(ws,req){
        var username = null;
        ws.on('message',function(msg){
            if(!username){
                if(users[msg]){
                    ws.send(JSON.stringify({status:0,info:"用户名重复请重试",ischat:false}));
                }else if(msg == ""){
                    ws.send(JSON.stringify({status:0,info:"用户名不能为空",ischat:false}));
                }else{
                    username = msg;
                    count++;   //用户+1
                    users[msg] = ws;
                    ws.send(JSON.stringify({status:1,info:"注册成功,欢迎"+username,ischat:false}));
                    console.log(username+"用户加入聊天室!当前在线:"+count);
                    broadcast(username+"用户加入聊天室!当前在线:"+count);
                }
            }else{
                broadcast(username+":"+msg);
                console.log(username+":"+msg);
            }
        });
    
        ws.on('close',function(){
            delete users[username];
            count--;
            console.log(username+"用户退出聊天室!当前在线:"+count);
            broadcast(username+"用户退出聊天室!当前在线:"+count);
        })
    });;
    
    app.listen(80);
    
    //需要广播给所有人(不排除自己)
    function broadcast(msg){
        for(var i in users)
            users[i].send(JSON.stringify({status:1,info:msg,ischat:true}));
    }
    

    注意1:express-ws中的ws.send()方法只能发送字符串,并没有express http的res.send()那么强大,故而我在传入js对象时,手动使用JSON.stringify()将对象转换为json字符串,待前端收到后再使用JSON.parse()转换回js对象。

    注意2:WebSocket API中几个最重要的事件:open,close,message,error,对应了连接过程中的打开连接,关闭连接,消息传递,错误事件,无论前端还是后端都需要对这几个事件进行绑定监听,传入回掉函数做必要的处理

    注意3:设计逻辑再讲一遍:users变量存储每一个连接引用,username作为局部变量,再每一次连接域内部,每一个客户端连入都会创建一个,单独的broadcast()方法遍历所有socket连接发送消息,最后再提醒一遍node.js开发一定要特别注意作用域范围!

    效果

    这里写图片描述

  • 相关阅读:
    Python 自省指南(原文http://www.ibm.com/developerworks/cn/linux/l-pyint/#ibm-pcon)
    PyDev for Eclipse 简介
    (转)盘点前 10 名的免费跨浏览器测试工具
    使用 JMeter 完成常用的压力测试
    Python 单元测试框架 —— PyUnit
    runtime实现对象存储型数据库——LHDB
    从零实现一个基于UDP的iOS聊天程序(一)-- GCDAsyncUdpSocket源码解析
    hadoop实战随笔_070818
    hadoop实战笔记_170816
    hadoop实战随笔_170814
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/8447287.html
Copyright © 2020-2023  润新知