每天学习一点点,成功增加一大步

利用 webSocket 建立客服系统(服务端篇)

NodsJS zhanghui 792℃

最近在研究 websocket ,之前也在其它的项目中遇到过它,但当时没有那么在意它,而通过这几天对它深入研究就发现其有一颗十分“心”,它是解决 IM(即时通讯)的需求的一种途径,它能让我们明白现在主流市场上的 IM (即时通讯)工具(如:QQ、微信等)是通过什么技术来实现。

websocket 还有一个与它相似的叫 socket.io,那么它们有什么区别点下面的链接去阅读

https://zhuanlan.zhihu.com/p/346650330

本文将记录如何建立服务端,因这一部分比较难,容易入坑,而客户端的架设相对而言比较简单,网络上也基本就有。

一、基本的服务端建立

var ws = require("nodejs-websocket");
const uuid = require('node-uuid');
const log = require('./log');
const redis = require('./redisHelper');

let cacheKey_conn = '', cacheKey_user, cache_expire_36 = 3600;
let result = (Inquiry, data) => {
        let params = {Inquiry};
        if (data) {
            params = Object.assign(data, params);
        }
        return JSON.stringify(params);
    },
    user = [],
    boardcast = function (key, params) {
        log.info("BOAR:" + cacheKey_user);
        log.info("S----------------");
        log.info("To conn Key:" + key);
        user.forEach(function (item) {
            if (item.key === key) {
                item.sendText(params);
            }
        })
        log.info("E------------------");
        log.info('user size', user.length);
        server.connections.forEach(function (conn) {
            //  .forEach 是调用数组里每个元素  //
            var item = conn.frameBuffer;
            log.info(server.connections.length);
            //if(conn.frameBuffer.name==null){
            //conn.sendText(str)
            //}
        });
    },
    cacheKey = {
        conn: 'conn:',
        online: 'online:',
        guest: 'guest:'
    },
    cacheData = {
        conn: (conn, params) => {
            console.log('conn keys', user.length);
            let exists = false;
            user.forEach(function (item) {
                if (item.hasOwnProperty('key')) {
                    if (item.key === conn.key) {
                        exists = true;
                    }
                }
            });
            cacheKey_conn = cacheKey.conn;
            if (params.hasOwnProperty('UUID')) {
                // 访客上线
                cacheKey_conn += cacheKey.guest;
            }
            cacheKey_conn += conn.key;
            log.info('conn exists:', exists);
            if (exists === false) {
                user.push(conn);
            }
        },
        pingConn: (conn_key, cache_key, fn) => {
            let is_exists = false;
            user.forEach(function (item, index) {
                if (item.hasOwnProperty('key')) {
                    if (item.key === conn_key) {
                        is_exists = true;
                    }
                }
            });
            if (is_exists === false) {
                // 不在线
            }
            fn(is_exists);
        },
        unConn: (conn) => {
            user.forEach(function (item, index) {
                if (item.hasOwnProperty('key')) {
                    if (item.key === conn.key) {
                        user.splice(index, 1);
                    }
                }
            });
        }
    };

console.log("开始建立连接...")

var server = ws.createServer(function (conn) {

    conn.on("text", function (str) {

        log.info("收到的信息为:" + str);
        let params = JSON.parse(str);

        cacheKey_user = cacheKey.guest + cacheKey.online + params.UUID;

        if (params.hasOwnProperty('action')) {
            switch (params.action) {
                case 'online':
                    conn.sendText(result('online', {key: conn.key}));
                    break;
            }
        }

    })

    conn.on("close", function (code, reason) {
        log.info("关闭连接");
        cacheData.unConn(conn);
    });

    conn.on("error", function (code, reason) {
        log.error("异常关闭");
        cacheData.unConn(conn);
    });

}).listen(8001);
console.log("WebSocket建立完毕");

二、带 SSL 协议的服务端

1、 mkcert 的自签证书

网络上的方法有很多,如通过 openSSL 来创建 cert 证书,这种方法都是复制性较多。后来发现了 mkcert 这个家伙是相当的吸引眼球于是拿过来用了。

const mkcert = require('mkcert');
var ws = require("nodejs-websocket");
const Https = require('https');
// const uuid = require('node-uuid');
const log = require('./log');
const redis = require('./redisHelper');

console.log("开始建立连接...")
// create a certificate authority
mkcert.createCA({
    organization: 'Hello CA',
    countryCode: 'NP',
    state: 'Bagmati',
    locality: 'Kathmandu',
    validityDays: 365
}).then(ca => {
    // then create a tls certificate
    mkcert.createCert({
        domains: ['127.0.0.1', 'localhost'],
        validityDays: 365,
        caKey: ca.key,
        caCert: ca.cert
    }).then(cert => {
        // 向server传递key和cert参数
        const app = Https.createServer({
            key: cert.key,
            cert: cert.cert
        }).listen(8001);

// 实例化WebSocket服务器
        const wss = ws.createServer({
            server: app
        });

// 如果有WebSocket请求接入,wss对象可以响应connection事件来处理
        wss.on('connection', (conn) => {
            console.log('服务器已启动,监听中~');

            conn.on("text", function (str) {
                log.info("收到的信息为:" + str);
                let params = JSON.parse(str);

            })

            conn.on("close", function (code, reason) {
                log.info("关闭连接");
            });

            conn.on("error", function (code, reason) {
                log.error("异常关闭");
            });

        });

        console.log("WebSocket建立完毕");
    });
});

MPN mkc

https://www.npmjs.com/package/mkcert

使用了 mkcert 的证书后,还是产生了以下的问题

Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID

搜索之后发现这个问题的原因是

  1. WebSocket地址不能使用IP,必须使用域名。因为证书是针对域名来进行配置的。
  2. 证书必须符合新Chrome规范,否则会出现NET::ERR_CERT_COMMON_NAME_INVALID错误,具体详情见Chrome帮助。如果重新签署后还是出现此问题,需要按下证书中的DNS地址是否包含使用的域名。
  3. 如果是开发环境的自签证书,需要配置到本地证书库中,否则会出现NET::ERR_CERT_AUTHORITY_INVALID错误。

2、找个域名去 SSL 服务商那申请

用 nginx 的证书就可以了

// 需安装ws模块 npm install ws
let WebSocketServer = require('ws').Server;
let https = require("https");
let fs = require("fs");

let server = https.createServer({
    cert: fs.readFileSync('./cert/ca.key').toString(),
    key: fs.readFileSync('./cert/ca.pem').toString()
}, (req, res) => {
    res.writeHead(200);
    res.end("this is a websocket server \n");
}).listen(8001);

let wss = new WebSocketServer({ server: server });

wss.on(
    "connection",
    connection => {
        console.log("has user to connected");
    }
);

NodeJS WS 的 GitHub 分享

https://github.com/websockets/ws

转载请注明:隨習筆記 » 利用 webSocket 建立客服系统(服务端篇)

喜欢 (6)