socket.io认证,nodejs实现socket.io demo(server/client)

心酸历史说一下,我是nodejs小白,今天领导让我验证一下socket.io认证是否可以使用header的方式将token传给server端,网上找了很多资料都说不行,但是领导说可以所以我就写了一个例子来实现此功能。其实最终就是socket.io的websocket方式不支持header官网上有写,我所有功能都验证完了才看到。踩了好几天的坑。

demo运行前需要先安装好nodejs的运行环境。直接上代码吧。

server端  index.js

const express = require('express');

const { createServer } = require('node:http');

const { join } = require('node:path');

const { Server } = require('socket.io');

const sqlite3 = require('sqlite3');

const { open } = require('sqlite');

async function main() {

  // open the database file

  const db = await open({

    filename: 'chat.db',

    driver: sqlite3.Database

  });

  // create our 'messages' table (you can ignore the 'client_offset' column for now)

  await db.exec(`

    CREATE TABLE IF NOT EXISTS messages (

        id INTEGER PRIMARY KEY AUTOINCREMENT,

        client_offset TEXT UNIQUE,

        content TEXT

    );

  `);

  const app = express();

  const server = createServer(app);

  // connectionStateRecovery是开启连接状态修复,服务端在发送消息时如果客户端断链了,会把消息临时暂存起来,等待客户端重新连接时发送给他。

  // const io = new Server(server,{

  //     connectionStateRecovery: {} //在服务端挂掉的时候此功能不能生效

  //   });

  const io = new Server(server, {

      cors: {

        allowedHeaders: ["Authorization","abcd"],

        credentials: true

      }

    });

  // 接收请求后返回前端页面 测试demo

  app.get('/', (req, res) => {

    console.log('来请求了 get');

    res.sendFile(join(__dirname, 'index.html'));

  });

  // 连接成功

  io.on('connection', (socket) => {

    console.log('a user connected');

    console.log(socket.handshake.headers);

    // 加入房间

    // socket.join('some room');

    // 向房间内发送消息

    // io.to('some room').emit('hello', 'world');

    // 除房间内其他全部发送

    // io.except('some room').emit('hello', 'world');

    // 离开房间

    //  socket.leave('some room');

    // chatMessage为事件, msg消息内容,callback回调数据,可作为消息接收通知。

    // socket.on('chatMessage', (msg, callback) => {

    //   console.log('chatMessage: ' + msg);

    //   io.emit('chatMessage', msg);

    //   callback({

    //     status: 'ok'

    //   });

    // });


 

    // 连接 处理消息后保存sqlite

    io.on('connection', async (socket) => {

      socket.on('chatMessage', async (msg) => {

        let result;

        try {

          // store the message in the database

          result = await db.run('INSERT INTO messages (content) VALUES (?)', msg);

        } catch (e) {

          // TODO handle the failure

          return;

        }

        // include the offset with the message

        io.emit('chatMessage', msg, result.lastID);

      });

      // 判断客户端重连后查询数据库数据重发

      if (!socket.recovered) {

        // if the connection state recovery was not successful

        try {

          await db.each('SELECT id, content FROM messages WHERE id > ?',

            [socket.handshake.auth.serverOffset || 0],

            (_err, row) => {

              socket.emit('chatMessage', row.content, row.id);

            }

          )

        } catch (e) {

          // something went wrong

        }

      }

    });

    // 断开连接

    socket.on('disconnect', () => {

      console.log('user disconnected');

    });

    // 所有事件全部接收

    // socket.onAny((eventName, ...args) => {

    //   console.log("onAny"); // 'hello'

    //   console.log(eventName); // 'hello'

    //   console.log(args); // [ 1, '2', { 3: '4', 5: ArrayBuffer (1) [ 6 ] } ]

    // });

    // 所有事件全部接收,参数只接收消息的不接收回调的

    socket.onAnyOutgoing((eventName, ...args) => {

      console.log("onAnyOutgoing"); // 'hello'

      console.log(eventName); // 'hello'

      console.log(args); // [ 1, '2', { 3: '4', 5: ArrayBuffer (1) [ 6 ] } ]

    });

  });


 

  server.listen(3000, () => {

    console.log('server running at http://localhost:3000');

  });

}

main();

 客户端代码:  index.html

<!DOCTYPE html>

<html>

  <head>

    <meta name="viewport" content="width=device-width,initial-scale=1.0">

    <title>Socket.IO chat</title>

    <style>

      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }

      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }

      #input:focus { outline: none; }

      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }

      #messages > li { padding: 0.5rem 1rem; }

      #messages > li:nth-child(odd) { background: #efefef; }

    </style>

<script src="/socket.io/socket.io.js"></script>

</head>

<body>

    <ul id="messages"></ul>

    <form id="form" action="">

      <input id="input" autocomplete="off" /><button>Send</button>

      <button id="toggle-btn">Disconnect</button>

    </form>

  </body>

  <script>

    // 消息重发1.设置超时如果服务端没有收到消息,那么10000ms后重发,一共3次。此方法不能保证消息次序。(下面还有一种方法可以实现)

    // const socket = io({

    //   ackTimeout: 10000,

    //   retries: 3

    // });

    // socket.emit('hello', 'world');

    // 使用nginx代理时设置的地址

    // const socket = io("ws://10.2.180.12:6886",{

    //const socket = io("ws://10.2.40.22:3000",{

    const socket = io({

    //transports: ["websocket"],

        auth: {

          serverOffset: 0

        },

        extraHeaders: {

          "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1IjoiNWZkOTU5NjE1NDU2MjQxZDRlZTY3MGJiIiwidyI6IjYxOWIwOGExNTk3OTc1MDYzMGFiMzZhYiIsInIiOiJkIiwiZXhwIjoxNzE0MTIyNDcxfQ.KzJ_JdxHF2j7oFPBEKCWbXYlHdgJLgnq",

          "abcd": "abcdefg"

        }

      });

    const toggleButton = document.getElementById('toggle-btn');

    const form = document.getElementById('form');

    const input = document.getElementById('input');

    const messages = document.getElementById('messages');

    // 监听发送按钮提交表单

    form.addEventListener('submit', (e) => {

      e.preventDefault();

      if (input.value) {

        socket.emit('chatMessage', input.value, (err, response) => {

          if (err) {

            console.log(err);

            console.log(response);

          } else {

            console.log(response.status); // 'ok'

          }

        });

        input.value = '';

      }

    });

    //监听断开连接按钮

    toggleButton.addEventListener('click', (e) => {

      e.preventDefault();

      if (socket.connected) {

        toggleButton.innerText = 'Connect';

        socket.disconnect();

      } else {

        toggleButton.innerText = 'Disconnect';

        socket.connect();

      }

    });

    //chatMessage事件接收

    // socket.on('chatMessage', (msg) => {

    //   const item = document.createElement('li');

    //   item.textContent = msg;

    //   messages.appendChild(item);

    //   window.scrollTo(0, document.body.scrollHeight);

    // });

    // chatMessage事件接收,并添加offset状态

    socket.on('chatMessage', (msg, serverOffset) => {

      const item = document.createElement('li');

      item.textContent = msg;

      messages.appendChild(item);

      window.scrollTo(0, document.body.scrollHeight);

      socket.auth.serverOffset = serverOffset;

    });

    // 消息重发2.发送事件如果服务端没有接收到,那么5000ms后在继续发

    // function emit(socket, event, arg) {

    //   socket.timeout(5000).emit(event, arg, (err) => {

    //     if (err) {

    //       // no ack from the server, let's retry

    //       emit(socket, event, arg);

    //     }

    //   });

    // }

    // emit(socket, 'hello', 'world');

  </script>

</html>

package.json文件

{

  "name": "socket-chat-example",

  "version": "0.0.1",

  "description": "my first socket.io app",

  "type": "commonjs",

  "dependencies": {

    "socket.io": "^4.7.5",

    "sqlite": "^5.1.1",

    "sqlite3": "^5.1.7"

  }

}

运行:node index 即可

浏览器访问:localhost:3000

就会跳转到客户端页面。

此demo代码来自官网Getting started

有问题可以联系我一起探讨,代码我加了很多注释,希望对大家有帮助

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值