1、websocket简介

传统http协议,是基于请求和响应的,无法直接做到客户端向客户端发送消息。 websocket协议是基于tcp的一种新的网络协议。实现了浏览器与服务器全双工通信。全双工:客户端可以主动给服务器发送消息,服务器也可以主动给客户端发送消息。 websocket是一种持久协议,http是非持久的 传统http协议实现即使聊天,必须通过ajax轮询,就是客户端会一直向服务器发送请求来确认是否有消息,浪费性能和资源。

2、使用websocket

image.png

2.1、websocket的基本使用

客户端:index.html
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>WebSocket测试</title>
  6. </head>
  7. <body style="text-align: center">
  8. <h2>WebSocket测试</h2>
  9. <div>
  10. <input type="text" id="txt" />
  11. <button onclick="sendWebSocket()">发送</button><br />
  12. <button onclick="checkWebSocket()">测试WebSocket</button>
  13. <button onclick="connectWebSocket()">连接WebSocket</button>
  14. <button onclick="closeWebSocket()">关闭WebSocket</button><br />
  15. <hr />
  16. <div id="message"></div>
  17. </div>
  18. </body>
  19. <script type="text/javascript">
  20. let websocket = null;
  21. // 检查是不是支持websocket
  22. function checkWebSocket() {
  23. if ("WebSocket" in window) {
  24. setMessageInnerHTML("您的浏览器支持 WebSocket!");
  25. } else {
  26. setMessageInnerHTML("您的浏览器不支持 WebSocket!");
  27. }
  28. }
  29. // 打印信息
  30. function setMessageInnerHTML(innerHTML) {
  31. document.getElementById("message").innerHTML += innerHTML + "<br/>";
  32. }
  33. // 连接WebSocket
  34. function connectWebSocket() {
  35. //1、打开一个websocket
  36. websocket = new WebSocket("ws://localhost:3000/");
  37. websocket.onopen = function () {
  38. setMessageInnerHTML("websocket 已连接...");
  39. };
  40. websocket.onmessage = function (evt) {
  41. var received_msg = evt.data;
  42. setMessageInnerHTML("收到消息:" + received_msg);
  43. };
  44. websocket.onclose = function () {
  45. setMessageInnerHTML("WebSocket 已关闭...");
  46. };
  47. }
  48. // 向服务端发消息
  49. function sendWebSocket() {
  50. if (websocket) {
  51. if (websocket.readyState == websocket.OPEN) {
  52. var message = document.getElementById("txt").value;
  53. websocket.send(message);
  54. } else {
  55. setMessageInnerHTML("WebSocket 未连接...");
  56. }
  57. } else {
  58. setMessageInnerHTML("WebSocket 未创建...");
  59. }
  60. }
  61. // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
  62. window.onbeforeunload = function () {
  63. closeWebSocket();
  64. };
  65. // 关闭WebSocket连接
  66. function closeWebSocket() {
  67. websocket.close();
  68. }
  69. </script>
  70. </html>

服务端 Node

app.js

  1. const ws = require("nodejs-websocket");
  2. const PORT = 3000;
  3. //创建server,每次只要有用户连接,回调执行就会给用户创建一个connect对象
  4. const server = ws.createServer((connect) => {
  5. console.log("用户连接成功");
  6. //用户传来数据,触发text事件
  7. connect.on("text", (data) => {
  8. console.log(`接受到用户的数据:${data}`);
  9. //接受到数据后给用户响应数据
  10. connect.sendText(data);
  11. });
  12. //连接关闭触发close事件
  13. connect.on("close", () => {
  14. console.log("连接断开");
  15. });
  16. //注册error事件,用户端口后就会触发该异常
  17. connect.on("error", () => {
  18. console.log("用户连接异常");
  19. });
  20. });
  21. server.listen(PORT, () => {
  22. console.log("监听3000");
  23. });

2.2、实现简单的聊天功能

通过打开几个窗口,验证不同用户的加入离开
image.png
index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Title</title>
  6. <style>
  7. /*div {
  8. width: 200px;
  9. height: 200px;
  10. border: 1px solid #000;
  11. }*/
  12. </style>
  13. </head>
  14. <body>
  15. <input type="text" placeholder="请输入" />
  16. <button>发送</button>
  17. <!-- 显示内容 -->
  18. <div></div>
  19. <!-- websocket在浏览器的使用 H5提供了websocketAPI-->
  20. <script>
  21. let input = document.querySelector("input");
  22. let button = document.querySelector("button");
  23. let div = document.querySelector("div");
  24. //创建WebSocket('WebSocket服务器地址')
  25. let socket = new WebSocket("ws://localhost:3000/");
  26. //监听WebSocket事件 open和WebSocket服务器连接成功触发
  27. socket.addEventListener("open", () => {
  28. div.innerHTML = "连接成功";
  29. });
  30. //给webSocket发送消息
  31. button.addEventListener("click", () => {
  32. let value = input.value;
  33. socket.send(value);
  34. input.value = "";
  35. });
  36. const TYPE_ENTER = 0;
  37. const TYPE_LEAVE = 1;
  38. const TYPE_MSG = 2;
  39. //接受websocket服务的消息
  40. socket.addEventListener("message", (e) => {
  41. let data = JSON.parse(e.data);
  42. //把消息显示到div,以追加的方式
  43. let dv = document.createElement("div");
  44. //为不同的消息类型添加效果
  45. dv.innerHTML = data.msg + "---" + data.time;
  46. if (data.type === TYPE_ENTER) {
  47. dv.style.color = "green";
  48. } else if (data.type === TYPE_LEAVE) {
  49. dv.style.color = "red";
  50. } else {
  51. dv.style.color = "blue";
  52. }
  53. div.appendChild(dv);
  54. });
  55. //端口服务
  56. socket.addEventListener("close", () => {
  57. console.log("服务断开");
  58. });
  59. </script>
  60. </body>
  61. </html>

app.js

  1. const ws = require('nodejs-websocket');
  2. //进入消息
  3. const TYPE_ENTER = 0;
  4. //离开消息
  5. const TYPE_LEAVE = 1;
  6. //正常消息
  7. const TYPE_MSG = 2;
  8. //记录当前连接的用户数量
  9. let count = 0;
  10. const server = ws.createServer(conn => {
  11. console.log('新连接');
  12. count++;
  13. //给用户一个固定的名字
  14. conn.userName = `用户${count}`;
  15. //告诉所有用户,有人加入聊天室
  16. broadcast({
  17. type: TYPE_ENTER,
  18. msg: `${conn.userName}加入聊天室`,
  19. time: new Date().toLocaleDateString()
  20. });
  21. //接受到客户端的数据触发该事件
  22. conn.on('text', data => {
  23. //接受到某个用户的数据,告诉所有的用户此消息,广播
  24. broadcast({
  25. type: TYPE_MSG,
  26. msg: data,
  27. time: new Date().toLocaleDateString()
  28. });
  29. });
  30. //关闭连接
  31. conn.on('close', () => {
  32. console.log('关闭连接')
  33. count--;
  34. //有人退出也告诉所有的用户
  35. broadcast({
  36. type: TYPE_LEAVE,
  37. msg: `${conn.userName}离开了聊天室`,
  38. time: new Date().toLocaleDateString()
  39. })
  40. });
  41. //发送异常
  42. conn.on('error', () => {
  43. console.log('异常');
  44. });
  45. });
  46. //广播
  47. const broadcast = (msg) => {
  48. //server.connection表示所有的用户
  49. server.connections.forEach(item => {
  50. //遍历出每个用户,挨个发消息
  51. item.send(JSON.stringify(msg));
  52. });
  53. }
  54. server.listen(3000, () => {
  55. console.log('监听3000');
  56. });