初步探索websocket

492 阅读3分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

websocket是什么?

websocket是HTML5一种新的协议,实现了浏览器与服务器全双工通信。其本质是:先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,服务端与客户端通过此TCP连接进行实时通信。

websocket最大的优点在于——服务器和浏览器可以在给定的时间范围内的任意时刻,互相推送消息。在建立连接之后,服务器可以主动传送数据给浏览器。Ajax与websocket最大的不同在于: Ajax需要客户端发起请求,websocket服务器和客户端可以互相实时推送消息。

websocket的应用场景

决定是否需要使用websocket技术的方法其实很简单:

你的产品需要提供多个用户相互交流的功能吗? 你的产品需要展示服务器端经常变动的数据吗? 如果回答都是肯定的,那么你的产品中就要应用websocket技术了。

常用的应用场景如下:

1.社交类应用

对社交类的应用的一个好处之处就是——能够即时的知道你的朋友正在做什么?用户是不会想要在数分钟之后,才能知道一个家庭成员在群里发送的红包或者一个朋友给你发的消息。用户是在线的,所以用户收到的消息应该是实时的。

2.股票基金类应用的价格

金融界瞬息万变——几乎是每毫秒都在发生变化,过时的信息就能导致损失。当我们打开一个股票或基金类应用时,我们想要知道产品实时的价格,而不是10秒前的数据。使用websocket可以实时更新这些数据变化而不需要等待。

3.基于位置的应用

越来越多的基于位置的应用,都是借用移动设备的GPS功能来实现的。如果一直记录用户的位置,就可以收集到更加细致化的数据。如果需要实时的更新网络数据仪表盘(比如:说运动员的教练需要查看这些数据),借用websocket可以做到让数据实时刷新。

4.在线教育类应用

在线教育是学习的不错方式,可以和老师以及其他同学一起交流。websocket技术可以实现多媒体聊天、文字聊天等功能。

我们可以封装事件监听的类:

class EventedClass {
    constructor() {
        this._eventMappings = {};
    }

    on(eventName, eventSource, handler) {
        if (typeof eventName !== "string") {
            throw new Error(`'eventName' must be string and cannot be null.`);
        }
        if (handler == null && typeof eventSource === "function") {
            handler = eventSource;
            eventSource = null;
        }
        if (eventSource == null) {
            eventSource = "";
        }
        if (typeof eventSource !== "string") {
            throw new Error(`'eventSource' must be string.`);
        }
        if (typeof handler !== "function") {
            throw new Error(`'handler' must be function.`);
        }
        let eventNameResult = parseEventName(eventName);

        if (this._eventMappings[eventNameResult.eventName] == null) {
            this._eventMappings[eventNameResult.eventName] = {};
        }

        let eventNamespaces = this._eventMappings[eventNameResult.eventName];
        if (eventNamespaces[eventNameResult.namespace] == null) {
            eventNamespaces[eventNameResult.namespace] = {};
        }

        let eventSources = eventNamespaces[eventNameResult.namespace];
        if (eventSources[eventSource] == null) {
            eventSources[eventSource] = [];
        }

        let eventHandlers = eventSources[eventSource];
        eventHandlers.push(handler);
    }

    off(eventName, eventSource, handler) {
        if (typeof eventName !== "string") {
            throw new Error(`'eventName' must be string and cannot be null.`);
        }
        if (handler == null && typeof eventSource === "function") {
            handler = eventSource;
            eventSource = null;
        }
        if (eventSource == null) {
            eventSource = "";
        }
        if (typeof eventSource !== "string") {
            throw new Error(`'eventSource' must be string.`);
        }
        if (handler != null && typeof handler !== "function") {
            throw new Error(`'handler' must be function.`);
        }

        let eventNameResult = parseEventName(eventName);
        if (this._eventMappings[eventNameResult.eventName]) {
            let namespaces = [];
            if (eventNameResult.namespace) {
                namespaces.push(eventNameResult.namespace);
            } else {
                namespaces = Object.keys(this._eventMappings[eventNameResult.eventName]);
            }

            for (let namespace of namespaces) {
                let eventHandlers = this._eventMappings[eventNameResult.eventName][namespace][eventSource];
                if (handler != null) {
                    let matchIndex = eventHandlers.indexOf(handler);
                    if (matchIndex !== -1) {
                        eventHandlers.splice(matchIndex, 1);
                    }
                } else {
                    delete this._eventMappings[eventNameResult.eventName][namespace];
                    if (Object.keys(this._eventMappings[eventNameResult.eventName]).length === 0) {
                        delete this._eventMappings[eventNameResult.eventName];
                    }

                }
            }
        }
    }

    trigger(eventName, eventSource, args) {
        if (typeof eventName !== "string") {
            throw new Error(`'eventName' must be string and cannot be null.`);
        }
        if (args == null && Array.isArray(eventSource)) {
            args = eventSource;
            eventSource = null;
        }
        if (eventSource == null) {
            eventSource = "";
        }
        if (typeof eventSource !== "string") {
            throw new Error(`'eventSource' must be string.`);
        }

        if (args == null) {
            args = [];
        }
        if (!Array.isArray(args)) {
            args = [args];
        }

        let eventNameResult = parseEventName(eventName);
        if (this._eventMappings[eventNameResult.eventName]) {
            let namespaces = Object.keys(this._eventMappings[eventNameResult.eventName]);
            for (let namespace of namespaces) {
                let eventHandlers = [];
                if (eventSource.length > 0) {
                    eventHandlers = this._eventMappings[eventNameResult.eventName][namespace][eventSource] || [];
                    eventHandlers = eventHandlers.concat(this._eventMappings[eventNameResult.eventName][namespace][""] || []);
                } else {
                    let eventSources = this._eventMappings[eventNameResult.eventName][namespace];
                    for (let key of Object.keys(eventSources)) {
                        eventHandlers.push(...eventSources[key]);
                    }
                }
                for (let handler of eventHandlers) {
                    handler.apply(null, args);
                }
            }
        }
    }

    getMergedPrototype() {
        let methodDescriptors = Object.getOwnPropertyDescriptors(this.constructor.prototype);
        Object.keys(methodDescriptors).forEach((key) => {
            methodDescriptors[key].enumerable = true;
        });
        return Object.assign({}, this, Object.create({}, methodDescriptors));
    }
}

以上代码则可以使用在websocket的自定义的监听事件。