【WebSocket&IndexedDB】node+WebSocket&IndexedDB开发简易聊天室

序幕介绍:

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

讲人话就是说:WebSocket 使得客户端和服务器之间的数据交换变得更加简单,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

WebSocket:

  • 常见方法WebSocket(url):创建WebSocket对象并与指定的URL建立连接
// 创建WebSocket对象并与服务器建立连接
var socket = new WebSocket("ws://example.com");
  • 常见方法send(data):向服务器发送数据
// 向服务器发送数据
function sendData(data) {
   socket.send(data);
   console.log("发送数据:" + data);
}
  • 常见方法close(code, reason):主动关闭WebSocket连接
// 主动关闭WebSocket连接
function closeConnection() {
   socket.close();
   console.log("关闭WebSocket连接");
}
  • 常见事件:onopen:当WebSocket连接成功打开时触发的事件
// 连接成功时触发的事件
socket.onopen = function(event) {
   console.log("WebSocket连接已打开");
   // 发送数据示例
   socket.send("Hello, server!");
};
  • 常见事件:onmessage:当从服务器接收到消息时触发的事件
// 接收到消息时触发的事件
socket.onmessage = function(event) {
   var message = event.data;
   console.log("收到消息:" + message);
};
  • 常见事件:onclose:当WebSocket连接关闭时触发的事件
// 连接关闭时触发的事件
socket.onclose = function(event) {
   console.log("WebSocket连接已关闭");
};
  • 常见事件: onerror:当WebSocket连接发生错误时触发的事件
// 连接错误时触发的事件
socket.onerror = function(error) {
   console.error("WebSocket错误:" + error);
};

效果图:

代码展示:

index.html

<html>
<head>
    <meta charset="UTF-8">
    <title>webrtc demo</title>
</head>

<body>
    <h1>Websocket简易聊天</h1>
    <div id="app">
        <input id="sendMsg" type="text" />
        <button id="submitBtn">发送</button>
    </div>
</body>
<script type="text/javascript">
    //在页面显示聊天内容
    function showMessage(str, type) {
        var div = document.createElement("div");
        div.innerHTML = str;
        if (type == "enter") {
            div.style.color = "blue";
        } else if (type == "leave") {
            div.style.color = "red";
        }
        document.body.appendChild(div);
    }

    //新建一个websocket
    var websocket = new WebSocket("ws://172.21.2.52:8099");
    //连接建立时触发
    websocket.onopen = function () {
        console.log("已经连上服务器----");
        document.getElementById("submitBtn").onclick = function () {
            // 获取输入的内容
            var txt = document.getElementById("sendMsg").value;
            if (txt) {
                //使用连接发送数据
                websocket.send(txt);
            }
        };
    };
    //连接关闭时触发
    websocket.onclose = function () {
        console.log("websocket close");
    };
    //客户端接收服务端数据时触发
    websocket.onmessage = function (e) {
        var mes = JSON.parse(e.data);   // json格式
        // 渲染
        showMessage(mes.data, mes.type);
    };
</script>

</html>

node.js    记得下载:nodejs-websocket依赖

var ws = require("nodejs-websocket")
var port = 8099;
var user = 0;

// 创建一个连接
var server = ws.createServer(function (conn) {
    console.log("创建一个新的连接--------");
    user++;
    // 给连接设置昵称属性
    conn.nickname = "user" + user;
    // 给连接设置文件描述符属性
    conn.fd = "user" + user;
    var mes = {};
    // 消息类型为进入聊天室
    mes.type = "enter";
    // 消息内容为进入提示
    mes.data = conn.nickname + " 进来啦"
    // 广播该消息给所有客户端
    broadcast(JSON.stringify(mes));

    //向客户端推送消息
    conn.on("text", function (str) {
        console.log("回复 " + str)
        // 消息类型为普通消息
        mes.type = "message";
        mes.data = conn.nickname + " 说:    " + str;
        broadcast(JSON.stringify(mes));
    });

    //监听关闭连接操作
    conn.on("close", function (code, reason) {
        console.log("关闭连接");
        mes.type = "leave";
        mes.data = conn.nickname + " 离开了"
        broadcast(JSON.stringify(mes));
    });

    //错误处理
    conn.on("error", function (err) {
        console.log("监听到错误");
        console.log(err);
    });
}).listen(port);

function broadcast(str) {
    server.connections.forEach(function (connection) {
        connection.sendText(str);
    })
}

IndexedDB

IndexedDB(索引数据库)是浏览器提供的一种客户端数据库存储解决方案。它允许 Web 应用程序在用户设备上存储大量结构化数据,并可在离线状态下进行查询和操作。IndexedDB 使用对象存储模型,类似于关系型数据库,但不支持 SQL 查询语言。

  • open(): 打开数据库连接
let request = window.indexedDB.open("myDatabase", 1);

request.onerror = function(event) {
  console.log("Failed to open database");
};

request.onsuccess = function(event) {
  let db = event.target.result;
  console.log("Database opened successfully");
};
  • createObjectStore(): 创建对象存储空间
let objectStore = db.createObjectStore("messages", { keyPath: "id", autoIncrement: true });
  • createIndex(): 创建索引
objectStore.createIndex("type", "user", { unique: false });
  • transaction(): 开启事务
let transaction = db.transaction(["messages"], "readwrite");
  • objectStore.add(): 添加数据到对象存储空间
let objectStore = transaction.objectStore("messages");
let message = { id: 1, type: "text", data: "Hello World", user: "Alice" };

let request = objectStore.add(message);
request.onsuccess = function(event) {
  console.log("Data added successfully");
};
  • objectStore.get(): 通过主键获取数据
let objectStore = transaction.objectStore("messages");
  
let request = objectStore.get(1);
request.onsuccess = function(event) {
  let data = event.target.result;
  console.log(data);
};
  • objectStore.getAll(): 获取所有数据
let objectStore = transaction.objectStore("messages");

let request = objectStore.getAll();
request.onsuccess = function(event) {
  let data = event.target.result;
  console.log(data);
};
  • objectStore.put(): 更新数据
let objectStore = transaction.objectStore("messages");
let message = { id: 1, type: "text", data: "Hello Updated", user: "Alice" };

let request = objectStore.put(message);
request.onsuccess = function(event) {
  console.log("Data updated successfully");
};
  • objectStore.delete(): 删除数据
let objectStore = transaction.objectStore("messages");
  
let request = objectStore.delete(1);
request.onsuccess = function(event) {
  console.log("Data deleted successfully");
};
  • clear(): 清空对象存储空间中的所有数据
let objectStore = transaction.objectStore("messages");

let request = objectStore.clear();
request.onsuccess = function(event) {
  console.log("Object store cleared successfully");
};

WebSocket加IndexedDB完整代码

WebSocket实现聊天,IndexedDB将聊天数据存储起来,防止刷新丢失

html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>webrtc demo</title>
</head>

<body>
    <h1>Websocket简易聊天</h1>
    <div id="app">
        <input id="sendMsg" type="text" />
        <button id="submitBtn">发送</button>
        <div id="leaveMessage"></div>
        <div id="chat"></div>
    </div>

    <script type="text/javascript">
        var db, transaction, objectStore;
        // 在页面显示聊天内容
        function showMessage(str, type, user = null, state = 1) {
            var div = document.createElement("div");

            var spanStr = document.createElement("span");
            spanStr.innerHTML = str;

            if (user != null) {
                var spanUser = document.createElement("span");
                spanUser.innerHTML = user;
                div.appendChild(spanUser);
            }

            if (type == "enter") {
                div.style.color = "blue";
            } else if (type == "leave") {
                div.style.color = "red";
            }

            div.appendChild(spanStr);
            document.getElementById("chat").appendChild(div);
            if (state == 1) {
                document.getElementById("chat").appendChild(div);
            } else {
                document.getElementById("leaveMessage").appendChild(div);
            }
        }

        // 创建或打开 IndexedDB 数据库
        var request = window.indexedDB.open("chatDB", 1);
        request.onerror = function () {
            console.log("无法打开数据库");
        };

        request.onupgradeneeded = function (event) {
            //  获取到数据库对象 db
            db = event.target.result;
            //  创建名为 "messages" 的对象存储,并指定 "id" 为键路径,并自动递增生成
            objectStore = db.createObjectStore("messages", { keyPath: "id", autoIncrement: true });
            // 创建一个名为 "type" 的索引,用于根据消息类型进行检索,允许重复值
            objectStore.createIndex("type", "user", { unique: false });
            // 创建一个名为 "data" 的索引,用于根据消息内容进行检索,允许重复值
            objectStore.createIndex("data", "data", { unique: false });
            objectStore.createIndex("state", "state", { unique: false });
            objectStore.createIndex("user", "user", { unique: false });
        };

        request.onsuccess = function (event) {
            db = event.target.result;
            // 新建一个websocket
            var websocket = new WebSocket("ws://172.21.2.52:8099");

            // 连接建立时触发
            websocket.onopen = function () {
                console.log("已经连上服务器----");
               
                document.getElementById("submitBtn").onclick = function () {
                    var txt = document.getElementById("sendMsg").value;
                    if (txt) {
                        websocket.send(JSON.stringify(txt));
                    }
                };
                // 给输入框添加键盘按下事件监听器
                document.getElementById("sendMsg").addEventListener("keydown", function (event) {
                    // 检查按下的键是否是回车键(键码为13)
                    if (event.keyCode == 13) {
                        // 取消回车键的默认行为(避免表单提交等操作)
                        event.preventDefault();
                        var txt = document.getElementById("sendMsg").value;
                        if (txt) {
                            websocket.send(JSON.stringify(txt));
                        }
                    }
                });
            };

            // 连接关闭时触发
            websocket.onclose = function () {
                console.log("websocket close");
            };

            // 客户端接收服务端数据时触发
            websocket.onmessage = function (e) {
                var mes = JSON.parse(e.data); // json格式
                // 渲染
                if (mes.state == 0) {
                    showMessage(mes.data, mes.type, mes.user);
                    saveMessage(mes)
                    document.getElementById("sendMsg").value = ''
                } else {
                    showMessage(mes.data, mes.type);
                }
            };

            function saveMessage(message) {
                let transaction = db.transaction(["messages"], "readwrite");
                let objectStore = transaction.objectStore("messages");
                var saveRequest = objectStore.add(message);
                saveRequest.onsuccess = function () {
                    console.log("消息已保存到 IndexedDB");
                };
                saveRequest.onerror = function () {
                    console.log("保存消息时发生错误");
                };
            }

            function loadMessages() {
                // 创建一个只读事务,该事务用于操作名为 "messages" 的对象存储
                transaction = db.transaction(["messages"], "readonly");
                // 获取对 "messages" 对象存储的引用,以便进行后续的操作
                objectStore = transaction.objectStore("messages");
                // 回一个获取所有数据的请求
                var getAllRequest = objectStore.getAll();

                // 在获取数据成功时触发
                getAllRequest.onsuccess = function () {
                    var messages = getAllRequest.result;
                    messages.forEach(function (message) {
                        showMessage(message.data, message.type, message.user, 0);
                    });
                };
            }

            // 页面加载完成后加载聊天记录
            window.onload = function () {
                loadMessages();
            };
        };
    </script>
</body>

</html>

node

var ws = require("nodejs-websocket")
var port = 8099;
var user = 0;
 
// 创建一个连接
var server = ws.createServer(function (conn) {
    console.log("创建一个新的连接--------");
    user++;
    // 给连接设置昵称属性
    conn.nickname = "user" + user;
    // 给连接设置文件描述符属性
    conn.fd = "user" + user;
    var mes = {};
    // 消息类型为进入聊天室
    mes.type = "enter";
    // 消息内容为进入提示
    mes.data = conn.nickname + " 进来啦";
    mes.state = 1;
    // 广播该消息给所有客户端
    broadcast(JSON.stringify(mes));
 
    //向客户端推送消息
    conn.on("text", function (str) {
        console.log("回复 " + str)
        // 消息类型为普通消息
        mes.type = "message";
        mes.user = conn.nickname + " 说:";
        mes.data = str;
        mes.state = 0;
        broadcast(JSON.stringify(mes));
    });
 
    //监听关闭连接操作
    conn.on("close", function (code, reason) {
        console.log("关闭连接");
        mes.type = "leave";
        mes.data = conn.nickname + " 离开了"
        broadcast(JSON.stringify(mes));
    });
 
    //错误处理
    conn.on("error", function (err) {
        console.log("监听到错误");
        console.log(err);
    });
}).listen(port);
 
function broadcast(str) {
    server.connections.forEach(function (connection) {
        connection.sendText(str);
    })
}

猜你喜欢

转载自blog.csdn.net/weixin_52479803/article/details/132337529