Вот 4 реализации обмена мгновенными сообщениями

внешний интерфейс JavaScript WebSocket
Вот 4 реализации обмена мгновенными сообщениями

1. Введение

Может быть, это конец года, эффективность работы у всех относительно низкая, простое рассмотрение требований затянется на 4 часа, что крайне скучно. Воспользуйтесь этой возможностью, чтобы разобраться, о чем вы думали в последнее время. Как сервер отправляет данные в браузер.

Вообще говоря,WebСквозную технологию обмена мгновенными сообщениями всегда было сложно внедрить из-за конструктивных ограничений браузеров. Вот четыре метода.

1. http + ajax

XMLHttpRequestПри взаимодействии с сервером есть четыре состояния.Во многих случаях суждениеreadyStateдля4когда, изresponseПолучите результат ответа сервера. фактическиreadyStateравный3Когда вы можете получить некоторые данные с сервера.

Это свойство можно использовать для реализации push-уведомлений на стороне сервера.

например, использование услугhttpСоздать сервис, каждый интервал1sпри прохождении черезwriteметод возвращает фрагмент текста, но не вызываетendметод.

const http = require('http');
const fs = require('fs');

const app = http.createServer((req, res) => {
    // 设置响应头
    res.setHeader('Content-type', 'application/json; charset=utf-8');
    res.setHeader('Cache-Control', 'max-age=0'); // 没有缓存
    let num = 0;
    // 地柜返回
    const send = () => {
        if (num > 20) {
            res.end();
            return;
        }
        num++;
        const data = Math.random() + '';
        res.write(data, 'utf8');
        setTimeout(send, 1000);
    }
    send();
});

app.listen(8081, () => {
    console.log('127.0.0.1:8081');
})

внешний мониторингXMLHttpRequestизonreadystatechangeСобытие, которое запускается каждый раз, когда сервер возвращает часть данныхonreadystatechangeсобытия, которые могут бытьresponseTextчтобы получить все данные, полученные в настоящее время.

var xhr = new XMLHttpRequest();
xhr.open('GET', '/api');
xhr.timeout = 30000;
xhr.responseType = 'text';
xhr.onreadystatechange = function () {
    if (this.readyState == 3) { // 分段获取服务端返回的数据
        console.log(this.responseText);
    }
    if (this.readyState == 4) {
        if (this.status >= 200 && this.status < 300 || this.status == 304) {
            // this.response
        } else {
            // this.statusText
        }
    }
}
xhr.send()

2. websocket

websocketОн имеет три преимущества: двустороннюю связь, автоматический междоменный доступ и высокую производительность. Главное, что данные можно передавать в самых разных форматах.WebSocketсоглашение в2008год рождения,2011года, чтобы стать международным стандартом. Его уже поддерживают все браузеры, а также это широко используемый протокол обмена мгновенными сообщениями.

websocketдаHTML5добавленAPI, принадлежащий браузеру или интерфейсному содержимому. Задний конец используетsocket,socketИстория протокола довольно давняя, в основном сорок лет назад. существуетH5серединаwebsocketпоставляется с некоторыми мерами безопасности, в то время как роднойsocketБезопасности нет вообще.

Браузер клиента создает экземплярWebsocket, создается адрес входящей службыwebsocketСвязь,messageбудет получать данные, отправленные сервером вsendметод отправки данных на сервер.

const ws = new Websocket('ws://127.0.0.1:8080/api');
// 原生没有emit,自己封装一个
ws.emit = function(name, ...args) {
    ws.send(JSON.stringify({
        name,
        data: [...args]
    }))
}
ws.onopen = function() {
    console.log('链接上了');
    // ws.send('dadadadadasda'); // 发送数据,只有一个参数一个大字符串
    ws.emit('msg', 12, 5, 8);
}; // 已经链接
ws.onmessage = function() {
    console.log('接收到消息了')
}; // 收到数据
ws.onclose = function() {
    console.log('断开链接了')
}; // 断开了

существуетnodeхочу достичьsocketможно использоватьnodeоригинальныйnetмодуль, который является сетевым модулем относительно низкого уровня, представляет собойtcpбиблиотека.netдаhttpНижний слой, многие вещи нужно реализовать самому, например можно использоватьnet.createServerдля создания сервиса.

websocketтакже датьhttpДа, пройти первымhttpЗапрос на обслуживание, будет нестиupgradeдляwebsocketзаголовок запроса, указывающий, что вы хотите перейти наwebsocket, в это время сервис может вернуться101Код состояния, указывающий, что услугу можно обновить.

const http = require('http');
const net = require('net'); // TCP的库,可以理解为原生的Socket
const crypto = require('crypto'); // 借助加密库实现一些安全性

const server = net.createServer(sock=> {
    console.log('链接上了');
    sock.on('end', () => {
        console.log('客户端断开了')
    }); // 断开

    sock.once('data', (data) => {
        console.log('hand shake start...');
        // 最先过来的是http头
        const str = data.toString();
        // 将http头用\r\n切开
        let lines = str.split('\r\n');
        // 删除第一行和最后一行,因为没啥用
        lines = lines.slice(1, lines.length - 2);
        // 将所有请求头通过'分号空格'切开
        const headers = {};
        lines.forEach(line => {
            const [key, value ] = line.split(': ');
            // 将请求头变成小写
            headers[key.toLowerCase()] = val;
        })
        // http协议转websocket会传入upgrade为websocket
        if (headers['upgrade'] != 'websocket') {
            console.log('其他协议,暂不支持');
            sock.end();
        } else if (headers['sec-websocket-version'] != 13) {
            console.log('不兼容不是13的版本');
            sock.end();
        } else {
            const key = headers['sec-websocket-key'];
            // 13版本的源码是258E,可以百度的到
            const mask = '258EAFA5-47DA-95CA-C5AB0DC85B11';
            // 需要把key和mask加在一起,然后用sha1加密,再变成base64,还给客户端
            // sha1(key + mask) -> base64 -> client;
            const hash = crypto.createHash('sha1');
            hash.update(key + mask);
            const tokey = hash.digest('base64');
            // 数据以HTTP发回客户端,因为验证的过程还是http阶段, 状态值为101(正在切换协议,协议升级 Switching Protocols)
            sock.write('HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ' + tokey + '\r\n'); // Upgrade: websocket告诉浏览器升级为websocket,冒号要有空格
            // 至此,握手已经结束了。因为握手的过程只有一次,所以不要用on处理,用once处理
            // 从这里开始,才是真正的数据,以后所有的数据都走这里,所以用on处理
            sock.on('data', data => {
                // 获取到的数据
                // 不过数据是一个buffer的数据包,解析起来比较麻烦。
                console.log(data);
            })
        }
    }); // 有数据过来
}).listen(8080);

Представленный вышеwebsocketПринцип реализации, который можно использовать непосредственно в проектеsocket.ioэта библиотека.

Внешний код выглядит следующим образом:

const sock = io.connect('ws://127.0.0.1:8080/api');
sock.on('connect', () => {
    console.log('已链接');
    sock.emit('aaa', 12, 5,8);
    sock.on('time', (ts) => {
        console.loh(ts);
    })
});
sock.on('disconnect', () => {
    console.log('已断开');
});

Код сервера выглядит следующим образом:

const http = require('http');
const io = require('socket.io');

// 创建http服务,开启8080端口号
const httpServer = http.createServer().listen(8080);
// socket监听http服务
const wsServer = io.listen(httpServer);

// 当有链接的时候
wsServer.on('connection', sock => {
    // 发送
    // sock.emit
    sock.emit('time', Date.now());
    // 接收
    sock.on('aaa', (a, b, c) => {
        console.loh(a, b, c);
    })
})

3. SSE

SSEполное имяServer-Sent Events, что означает, что веб-страница автоматически получает обновления с сервера, то есть автоматически получает данные, передаваемые сервером на веб-страницу.H5свойства, кромеIE, другие стандартные браузеры в основном совместимы.

Реализация чем-то похожа на вторую: сервер объявляет клиенту, что он хочет отправить информацию о потоке, и затем отправляет ее непрерывно. В это время клиент не будет закрывать соединение и всегда будет ждать нового потока данных, отправленного сервером. Например, таким механизмом является медиапоток аудио и видео.

SSEТолько сервер может отправлять данные в браузер, что очень похоже на второй метод, но не так эффективно.websocket,ПреимуществоSSEБолее простой в использовании и основанный наhttpПротокол, совместимость в порядке (конечно, в 2022 году нет ничего, что совместимость невозможна).

H5конечное применениеEventSourceобъект, заполните запрашиваемыйurlАдрес в порядке.

var source = new EventSource('/api', {
    withCredentials: true
});
source.onopen = function () {
    console.log('链接已建立', this.readyState);
}
source.onmessage = function (event) {
    console.log('实时获取的数据', event.data);
}
source.onerror = function () {
    console.log('发生错误');
}
// 关闭
// source.close();

сервер в браузерSSEданные, вы должны сначала установить заголовок ответаContent-typeдляtext/event-stream, а формат кодированияutf-8. Формат возвращаемых данных должен бытьdata: xxxx\n\n. Кромеdataа такжеevent,id,так же какretry, вы можете обратиться кServer-sent_events-mdn.

Код сервера выглядит следующим образом:

const http = require('http');
const fs = require('fs');

const app = http.createServer((req, res) => {
    res.setHeader('Content-type', 'text/event-stream; charset=utf-8');
    res.setHeader('Cache-Control', 'max-age=0'); // 清楚缓存
    res.setHeader('Access-Control-Allow-Origin', 'http:127.0.0.1/');
    let num = 0;
    const send = () => {
        if (num > 20) {
            res.end();
            return;
        }
        num++;
        const data = Math.random() + '';
        res.write(`data: ${data}\n\n`, 'utf8');
        setTimeout(send, 1000);
    }
    send();
});

app.listen(8081, () => {
    console.log('127.0.0.1:8081');
})

4. ajax

ajaxОпрос, по этому поводу нечего сказать, это все так думают, поэтому не буду вводить.

Правильно, это для составления чисел.