предисловие
Я не публиковал статьи на прошлой неделе, поэтому я здесь, чтобы извиниться перед всеми. Сегодня давайте разработаем чат с нуля. Хорошо, давайте начнем сейчас.
Узнать о веб-сокетах
Для разработки чатов нам нужно использовать WebSocket, сетевой протокол связи, так зачем его использовать?
Сначала процитируем абзац из статьи Жуань Ифэн:
Любой, кто новичок в WebSockets, задаст один и тот же вопрос: у нас уже есть протокол HTTP, зачем нам еще один протокол? Какую пользу это может принести?
Ответ прост, потому что протокол HTTP имеет изъян: связь может инициировать только клиент.
Например, если мы хотим узнать сегодняшнюю погоду, клиент может только отправить запрос на сервер, а сервер возвращает результат запроса. Протокол HTTP не может позволить серверу активно передавать информацию клиенту.
Характеристики этого одностороннего запроса обречены быть очень неприятными для клиента, чтобы узнать, есть ли у сервера непрерывные изменения состояния. Мы можем использовать только «опрос»: время от времени отправляется запрос, чтобы узнать, есть ли на сервере новая информация. Самый типичный сценарий — чат.
Опрос неэффективен и тратит ресурсы впустую (потому что вам нужно постоянно подключаться, иначе HTTP-соединение всегда открыто). Поэтому инженеры задумались, а есть ли способ лучше. Так был изобретен WebSocket.
Давайте позаимствуем официальное введение на веб-сайте MDN, чтобы резюмировать:
WebSockets — это передовая технология. Он может открыть интерактивный сеанс связи между браузером пользователя и сервером. Используя этот API, вы можете отправлять сообщения на сервер и получать ответы, управляемые событиями, без необходимости запрашивать ответ у сервера.
Протокол WebSocket родился в 2008 году и стал международным стандартом в 2011 году. Все браузеры уже поддерживают его.
Возможности веб-сокета
- Сервер может активно передавать информацию клиенту, а клиент также может активно отправлять информацию серверу.Это настоящий двусторонний равноправный диалог и относится к разновидности серверной технологии push.
- На основе протокола TCP реализация на стороне сервера относительно проста.
- Он имеет хорошую совместимость с протоколом HTTP. Порты по умолчанию также 80 и 443, а протокол HTTP используется на этапе рукопожатия, поэтому его нелегко экранировать во время рукопожатия, и он может проходить через различные прокси-серверы HTTP.
- Формат данных относительно легкий, производительность невелика, а связь эффективна.
- Текст может быть отправлен, или двоичные данные могут быть отправлены.
- Ограничений по одному и тому же источнику нет, и клиенты могут взаимодействовать с любым сервером.
- Идентификатор протокола
ws(если зашифровано,wss),которыйwsвести перепискуhttp,wssвести перепискуhttps. URL-адрес сервера — это URL-адрес. которыйws://www.xx.comилиwss://www.xx.com
Websocket Client часто использует API
WebSocketОбъект предоставляет API для создания и управления соединениями WebSocket, а также для отправки и получения данных через это соединение.
использовать WebSocket()конструктор для создания WebSocket.
Атрибуты
-
WebSocket.onopenИспользуется для указания функции обратного вызова после успешного подключения.
-
WebSocket.onmessageИспользуется для указания функции обратного вызова при получении информации с сервера.
-
WebSocket.oncloseИспользуется для указания функции обратного вызова после закрытия соединения.
-
WebSocket.onerrorИспользуется для указания функции обратного вызова после сбоя соединения.
метод
WebSocket.close()
Закройте текущую ссылку.
WebSocket.send(data)
Клиент отправляет данные на сервер, ставя их в очередь для передачи.
Пример клиента
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080'); // 这里的地址是服务器的websocket服务地址
// Connection opened
socket.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
// Listen for messages
socket.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
socket.close();
};
// Connection closed
socket.onclose = function(evt) {
console.log("Connection closed.");
};
Веб-сокет-сервер
Здесь на сервере мы используемNode.js, Вот несколько часто используемых библиотек.
wssocket.ionodejs-websocket
Для конкретного использования вы можете просмотреть подробную документацию в Интернете, и я не буду представлять их здесь по отдельности. Но в этой статье. Я буду использовать его для всехwsа такжеnodejs-websocketЭти два модуля используются для разработки проекта соответственно.
Представлены как клиент, так и сервер! Давайте действовать сейчас!
Разработайте локальный (или локальный сетевой) чат (первый тип)
мы будем основываться наVue.js@3.0Чаты развиваются, причина в том, чтобы охватить новые технологии. Как построить vue scaffolding, я не буду здесь представлять, думаю, все это сделают. Перейдем непосредственно к коду.
клиент
<template>
<div class="home">
<div class="content">
<div class="chat-box" ref="chatBox">
<div
v-for="(item, index) in chatArr"
:key="index"
class="chat-item"
>
<div v-if="item.name === name" class="chat-msg mine">
<p class="msg mineBg">{{ item.txt }}</p>
<p class="user" :style="{ background: bg }">
{{ item.name.substring(item.name.length - 5, item.name.length) }}
</p>
</div>
<div v-else class="chat-msg other">
<p class="user" :style="{ background: item.bg }">
{{ item.name.substring(item.name.length - 5, item.name.length) }}
</p>
<p class="msg otherBg">{{ item.txt }}</p>
</div>
</div>
</div>
</div>
<div class="footer">
<textarea
placeholder="说点什么..."
v-model="textValue"
autofocus
ref="texta"
@keyup.enter="send"
></textarea>
<div class="send-box">
<p class="send active" @click="send">发送</p>
</div>
</div>
</div>
</template>
<script>
import { onMounted, onUnmounted, ref, reactive, nextTick } from "vue";
export default {
name: "Home",
setup() {
let socket = null;
const path = "ws://localhost:3000/"; // 服务器地址,服务器代码在下方
const textValue = ref("");
const chatBox = ref(null);
const texta = ref(null);
const name = new Date().getTime().toString();
const bg = randomRgb();
const chatArr = reactive([]);
// WebSocket初始化
function init() {
if (typeof WebSocket === "undefined") {
alert("您的浏览器不支持socket");
} else {
socket = new WebSocket(path);
socket.onopen = open;
socket.onerror = error;
socket.onclose = closed;
socket.onmessage = getMessage;
window.onbeforeunload = function(e) {
e = e || window.event;
if (e) {
e.returnValue = "关闭提示";
socket.close();
}
socket.close();
return "关闭提示";
};
}
}
function open() {
alert("socket连接成功");
}
function error() {
alert("连接错误");
}
function closed() {
alert("socket关闭");
}
// 监听信息
async function getMessage(msg) {
const obj = JSON.parse(msg.data);
chatArr.push(obj);
await nextTick(); // 异步更新DOM
chatBox.value.scrollTop = chatBox.value.scrollHeight; // 保持滚动条在底部
}
// 随机获取头像背景
function randomRgb() {
let R = Math.floor(Math.random() * 130 + 110);
let G = Math.floor(Math.random() * 130 + 110);
let B = Math.floor(Math.random() * 130 + 110);
return "rgb(" + R + "," + G + "," + B + ")";
}
// 发送消息
function send() {
if (textValue.value.trim().length > 0) {
const obj = {
name: name,
txt: textValue.value,
bg: bg,
};
socket.send(JSON.stringify(obj));
textValue.value = "";
texta.value.focus();
}
}
function close() {
alert("socket已经关闭");
}
onMounted(() => {
init();
});
onUnmounted(() => {
socket.onclose = close;
});
return {
send,
textValue,
chatArr,
name,
bg,
chatBox,
texta,
randomRgb
};
},
};
</script>
Что касается файла стиля, я также разместил его здесь.
html,body{
background-color: #e8e8e8;
user-select: none;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
display: none;
}
::-webkit-scrollbar-thumb {
background-color: #D1D1D1;
border-radius: 3px;
-webkit-border-radius: 3px;
border-left: 2px solid transparent;
border-top: 2px solid transparent;
}
*{
margin: 0;
padding: 0;
}
.mine {
justify-content: flex-end;
}
.other {
justify-content: flex-start;
}
.mineBg {
background: #98e165;
}
.otherBg {
background: #fff;
}
.home {
position: fixed;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
height: 100%;
min-width: 360px;
min-height: 430px;
box-shadow: 0 0 24px 0 rgb(19 70 80 / 25%);
}
.count{
height: 5%;
display: flex;
justify-content: center;
align-items: center;
background: #EEEAE8;
font-size: 16px;
}
.content {
width: 100%;
height: 80%;
background-color: #f4f4f4;
overflow: hidden;
}
.footer {
position: fixed;
bottom: 0;
width: 100%;
height: 15%;
background-color: #fff;
}
.footer textarea {
width: 100%;
height: 50%;
background: #fff;
border: 0;
box-sizing: border-box;
resize: none;
outline: none;
padding: 10px;
font-size: 16px;
}
.send-box {
display: flex;
height: 40%;
justify-content: flex-end;
align-items: center;
}
.send {
margin-right: 20px;
cursor: pointer;
border-radius: 3px;
background: #f5f5f5;
z-index: 21;
font-size: 16px;
padding: 8px 20px;
}
.send:hover {
filter: brightness(110%);
}
.active {
background: #98e165;
color: #fff;
}
.chat-box {
height: 100%;
padding:0 20px;
overflow-y: auto;
}
.chat-msg {
display: flex;
align-items: center;
}
.user {
font-weight: bold;
color: #fff;
position: relative;
word-wrap: break-word;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 60px;
height: 60px;
line-height: 60px;
border-radius:8px ;
text-align: center;
}
.msg {
margin: 0 5px;
max-width: 74%;
white-space: normal;
word-break: break-all;
color: #333;
border-radius: 8px;
padding: 10px;
text-align: justify;
font-size: 16px;
box-shadow: 0px 0px 10px #f4f4f4;
}
.chat-item {
margin: 20px 0;
animation: up-down 1s both;
}
@keyframes up-down {
0% {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
100% {
opacity: 1;
transform: none;
}
}
Сервер
Здесь используется Node.js.
nodejs-websocket: модуль nodejs для сервера и клиента веб-сокета.
const ws = require("nodejs-websocket");
const server = ws.createServer((conn) => {
conn.on("text", (str) => {
broadcast(str);
});
conn.on("error", (err) => {
console.log(err);
});
});
server.listen(3000, function () {
console.log("open");
});
// 群发消息
function broadcast(data) {
server.connections.forEach((conn) => {
conn.sendText(data);
});
}
Список проектов
Количество людей в сети равно нулю.Это не баг, потому что на локалке в то время это не делалось, а только на нем был поставлен этот раздел. Однако я поместил эту функцию на стороне облачного сервера. Что ж, давайте посмотрим.
Разработать облачный чат (второй тип)
клиент
<template>
<div class="home">
<div class="count">
<p>在线人数:{{ count }}</p>
</div>
<div class="content">
<div class="chat-box" ref="chatBox">
<div
v-for="(item, index) in chatArr"
:key="index"
class="chat-item"
>
<div v-if="item.name === name" class="chat-msg mine">
<p class="msg mineBg">{{ item.txt }}</p>
<p class="user" :style="{ background: bg }">
{{ item.name.substring(item.name.length - 5, item.name.length) }}
</p>
</div>
<div v-else class="chat-msg other">
<p class="user" :style="{ background: item.bg }">
{{ item.name.substring(item.name.length - 5, item.name.length) }}
</p>
<p class="msg otherBg">{{ item.txt }}</p>
</div>
</div>
</div>
</div>
<div class="footer">
<textarea
placeholder="说点什么..."
v-model="textValue"
autofocus
ref="texta"
@keyup.enter="send"
></textarea>
<div class="send-box">
<p class="send active" @click="send">发送</p>
</div>
</div>
</div>
</template>
<script>
import { onMounted, onUnmounted, ref, reactive, nextTick } from "vue";
export default {
name: "Home",
setup() {
let socket = null;
const path = "wss:/xxx.com/wsline/"; // 这个网址只是测试网址,这里只是说明云服务地址
const textValue = ref("");
const chatBox = ref(null);
const texta = ref(null);
const count = ref(0);
const name = new Date().getTime().toString();
const bg = randomRgb();
const chatArr = reactive([]);
function init() {
if (typeof WebSocket === "undefined") {
alert("您的浏览器不支持socket");
} else {
socket = new WebSocket(path);
socket.onopen = open;
socket.onerror = error;
socket.onclose = closed;
socket.onmessage = getMessage;
window.onbeforeunload = function(e) {
e = e || window.event;
if (e) {
e.returnValue = "关闭提示";
socket.close();
}
socket.close();
return "关闭提示";
};
}
}
function open() {
alert("socket连接成功");
}
function error() {
alert("连接错误");
}
function closed() {
alert("socket关闭");
}
async function getMessage(msg) {
if (typeof JSON.parse(msg.data) === "number") {
console.log(JSON.parse(msg.data));
count.value = msg.data;
} else {
const obj = JSON.parse(msg.data);
chatArr.push(obj);
}
await nextTick();
chatBox.value.scrollTop = chatBox.value.scrollHeight;
}
function randomRgb() {
let R = Math.floor(Math.random() * 130 + 110);
let G = Math.floor(Math.random() * 130 + 110);
let B = Math.floor(Math.random() * 130 + 110);
return "rgb(" + R + "," + G + "," + B + ")";
}
function send() {
if (textValue.value.trim().length > 0) {
const obj = {
name: name,
txt: textValue.value,
bg: bg,
};
socket.send(JSON.stringify(obj));
textValue.value = "";
texta.value.focus();
}
}
function close() {
alert("socket已经关闭");
}
onMounted(() => {
init();
});
onUnmounted(() => {
socket.onclose = close;
});
return {
send,
textValue,
chatArr,
name,
bg,
chatBox,
texta,
randomRgb,
count,
};
},
};
</script>
Файл стиля такой же, как и локальный стиль, вы можете просмотреть код выше.
Сервер
Здесь я использовалwsмодуль, а также я построил https-сервер и использовал более безопасный протокол wss. Далее посмотрим, как это работает.
const fs = require("fs");
const httpServ = require("https");
const WebSocketServer = require("ws").Server; // 引用Server类
const cfg = {
port: 3456,
ssl_key: "../../https/xxx.key", // 配置https所需的文件2
ssl_cert: "../../https/xxx.crt", // 配置https所需的文件1
};
// 创建request请求监听器
const processRequest = (req, res) => {
res.writeHead(200);
res.end("Websocket linked successfully");
};
const app = httpServ
.createServer(
{
// 向server传递key和cert参数
key: fs.readFileSync(cfg.ssl_key),
cert: fs.readFileSync(cfg.ssl_cert),
},
processRequest
)
.listen(cfg.port);
// 实例化WebSocket服务器
const wss = new WebSocketServer({
server: app,
});
// 群发
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
client.send(data);
});
};
// 如果有WebSocket请求接入,wss对象可以响应connection事件来处理
wss.on("connection", (wsConnect) => {
console.log("Server monitoring");
wss.broadcast(wss._server._connections);
wsConnect.on("message", (message) => {
wss.broadcast(message);
});
wsConnect.on("close", function close() {
console.log("disconnected");
wss.broadcast(wss._server._connections);
});
});
Запускаем команду на облачном сервисе.
Успешно стартовал!
Это еще не все, потому что вы используете порт IP-адреса, который должен быть перенаправлен на доменное имя. Поэтому я использую nginx для переадресации и настраиваю следующие параметры.
location /wsline/ {
proxy_pass https://xxx:3456/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
Затем перезапустите облачный сервер и посмотрите на результат.
Список проектов
Итак, вот облачный чат, который может отображать количество людей онлайн в режиме реального времени, чтобы вы могли знать, сколько людей общается с вами здесь.
Эпилог
Спасибо за прочтение, надеюсь, я не зря потратил ваше время. Прочитав статью, поспешите создать собственный чат.
Пригласите друзей издалека.
- Добро пожаловать, чтобы обратить внимание на мой общедоступный номер
前端历劫之路- ключевые слова ответа
电子书, вы можете получить 12 популярных электронных книг.- ключевые слова ответа
红宝书第4版Чтобы получить последний «JavaScript Advanced Program Design» (четвертый издание) электронная книга.- Я создал техническую группу обмена и совместную группу. Существует множество лидеров крупных производителей крупных производителей. После выполнения официального счета щелкните меню ниже, чтобы узнать больше, и добавьте меня на WeChat. Я с нетерпением жду вашего присоединения Отказ