美文网首页
socket.io简单使用

socket.io简单使用

作者: 绝尘kinoko | 来源:发表于2022-06-24 15:38 被阅读0次

    websocket都知道,是用于CS全双工通信的。socket.io是在此基础上进行了封装,提供了namespace、Manager等概念。
    demo采用服务端渲染,所以还是用静态资源引入。服务端也是用commonJs,不太明白为什么官网都是用ES6模式引入的例子。在API用法时,会说一下差别。文档全英文,可能有理解不到位的地方,望指正。
    版本:4.4.0

    基本使用

    1. 建立连接
    // server
    var app = require('../app');
    var http = require('http');
    
    var server = http.createServer(app);
    let io = require('socket.io')(server);
    
    // client
    // html
    // script(src='/javascripts/socket.io.js')
    // js
    let socket = io('');
    

    服务端可以用npm下载,客户端就从GitHub上下了配套的client静态js,跟socket.io-client不太一样,且官网也没有静态js的文档。
    首先引入一下namespace和Manager,借用官网的图看下关系。


    server
    client

    还有个room,关系上算是socket 1 --- * room。另外manager和socket 1-n的关系没太搞懂,client看起来像单例。
    按个人理解,用聊天软件举例,namespace相当于账号,room相当于群组,manager就是个人。
    namespace和manager是对应的

    // server
    io = io.of('/namespace1');
    // client 静态js是没有Manager外部接口的
    let socket = io('/namespace1');
    
    2. 通信

    总体来说还是eventEmitter那一套,不过底层原理应该不一样。
    client.emit -- server.on
    server.emit -- client.on
    emit(eventName, ...params)
    on(eventName, callback)
    事件名可自定义,也有一些内置事件

    1. server/namespace
    • connection 连接后即触发
    • socket disconnect 断开后触发,socket是connection callback回调的参数
    1. client socket
    • connect 连接后触发,相当于server connection,类似登录步骤
    • disconnect 断开后触发
    • connect_error 连接失败,一般是重连尝试失败
    1. manager
      client socket.io即可拿到当前manager
    • reconnect_attempt 服务器断开时尝试请求,轮询
    • reconnect_error 轮询失败时
    • reconnect 服务器重新连接

    demo

    因为公司不能连外网,简单记录下;client底子是官网直接搬的

    // server
    let idNameMap = Object.create(null);
    let numUsers = 0;
    
    io = io.of('/namespace1');
    io.on('connection', (client) => {
        numUsers++;
        client.emit('login', { numUsers });
    
        client.on('add user', (name) => {
            idNameMap[client.id] = name;
            client.broadcast.emit('user joined', { username: name, numUsers });
        });
        client.on('new message', (data) => {
            client.broadcast.emit('new message', data);
        });
        client.on('typing', () => {
            client.broadcast.emit('typing', {
                username: idNameMap[client.id]
            });
        });
        client.on('stop typing', () => {
            client.broadcast.emit('stop typing', {
                username: idNameMap[client.id]
            });
        });
        client.on('disconnect', () => {
            numUsers--;
            console.log(idNameMap, client.id, numUsers);
            client.broadcast.emit('user left', {
                username: idNameMap[client.id],
                numUsers
            });
            delete idNameMap[client.id];
        });
        client.on('request_reconnect', () => {
            client.emit('reconnect');
        });
    });
    // client
    $(() => {
        const FADE_TIME = 150; // ms
        const TYPING_TIMER_LENGTH = 400; // ms
        const COLORS = ['#e21400', '#91580f', '#f8a700', '#f78b00', '#58dc00', '#287b00', '#a8f07a', '#4ae8c4', '#3b88eb', '#3824aa', '#a700ff', '#d300e7'];
    
        var $window = $(window);
        var $usernameInput = $('.user-name'); // Input for username
        var $messages = $('.messages'); // Messages area
        var $inputMessage = $('.input-message'); // Input message input box
    
        var $loginPage = $('.login.page'); // The login page
        var $chatPage = $('.chat.page'); // The chatroom page
    
        // Prompt for setting a username
        var username;
        var connected = false;
        var typing = false;
        var lastTypingTime;
        let loginFlag = false;
        var $currentInput = $usernameInput.focus();
        var timer = null;
    
        // let socket = io();
        let socket = io('/namespace1');
    
        const addParticipantsMessage = (data) => {
            var message = '';
            if (data.numUsers === 1) {
                message += '现在只有你一个(`・ω・´)';
            } else {
                message += '现在有 ' + data.numUsers + ' 只小伙伴啦( ̄▽ ̄)/';
            }
            log(message);
        };
    
        // Sets the client's username
        const setUsername = () => {
            username = cleanInput($usernameInput.val().trim());
    
            // If the username is valid
            if (username) {
                $loginPage.fadeOut();
                $chatPage.show();
                $loginPage.off('click');
                $currentInput = $inputMessage.focus();
    
                // Tell the server your username
                socket.emit('add user', username);
            }
        };
    
        // Sends a chat message
        const sendMessage = () => {
            var message = $inputMessage.val().trim();
            // Prevent markup from being injected into the message
            message = cleanInput(message);
            // if there is a non-empty message and a socket connection
            if (message && connected) {
                $inputMessage.val('');
                let data = {
                    username,
                    message
                };
                addChatMessage(data);
                // tell server to execute 'new message' and send along one parameter
                socket.emit('new message', data);
            }
        };
    
        // Log a message
        const log = (message, options) => {
            var $el = $('<li>').addClass('log').text(message);
            addMessageElement($el, options);
        };
    
        // Adds the visual chat message to the message list
        const addChatMessage = (data, options) => {
            // Don't fade the message in if there is an 'X was typing'
            var $typingMessages = getTypingMessages(data);
            options = options || {};
            if ($typingMessages.length !== 0) {
                options.fade = false;
                $typingMessages.remove();
            }
    
            var $usernameDiv = $('<span class="username"/>').text(data.username).css('color', getUsernameColor(data.username));
            var $messageBodyDiv = $('<span class="messageBody">').text(data.message);
    
            var typingClass = data.typing ? 'typing' : '';
            var $messageDiv = $('<li class="message"/>').data('username', data.username).addClass(typingClass).append($usernameDiv, $messageBodyDiv);
    
            addMessageElement($messageDiv, options);
        };
    
        // Adds the visual chat typing message
        const addChatTyping = (data) => {
            data.typing = true;
            data.message = '正在编辑...';
            addChatMessage(data);
        };
    
        // Removes the visual chat typing message
        const removeChatTyping = (data) => {
            getTypingMessages(data).fadeOut(function () {
                $(this).remove();
            });
        };
    
        // Adds a message element to the messages and scrolls to the bottom
        // el - The element to add as a message
        // options.fade - If the element should fade-in (default = true)
        // options.prepend - If the element should prepend
        //   all other messages (default = false)
        const addMessageElement = (el, options) => {
            var $el = $(el);
    
            // Setup default options
            if (!options) {
                options = {};
            }
            if (typeof options.fade === 'undefined') {
                options.fade = true;
            }
            if (typeof options.prepend === 'undefined') {
                options.prepend = false;
            }
    
            // Apply options
            if (options.fade) {
                $el.hide().fadeIn(FADE_TIME);
            }
            if (options.prepend) {
                $messages.prepend($el);
            } else {
                $messages.append($el);
            }
            $messages[0].scrollTop = $messages[0].scrollHeight;
        };
    
        // Prevents input from having injected markup
        const cleanInput = (input) => {
            return $('<div/>').text(input).html();
        };
    
        // Updates the typing event
        const updateTyping = () => {
            if (connected) {
                if (!typing) {
                    typing = true;
                    socket.emit('typing');
                }
                lastTypingTime = new Date().getTime();
    
                setTimeout(() => {
                    var typingTimer = new Date().getTime();
                    var timeDiff = typingTimer - lastTypingTime;
                    if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
                        socket.emit('stop typing');
                        typing = false;
                    }
                }, TYPING_TIMER_LENGTH);
            }
        };
    
        // Gets the 'X is typing' messages of a user
        const getTypingMessages = (data) => {
            return $('.typing.message').filter(function (i) {
                return $(this).data('username') === data.username;
            });
        };
    
        // Gets the color of a username through our hash function
        const getUsernameColor = (username) => {
            // Compute hash code
            var hash = 7;
            for (var i = 0; i < username.length; i++) {
                hash = username.charCodeAt(i) + (hash << 5) - hash;
            }
            // Calculate color
            var index = Math.abs(hash % COLORS.length);
            return COLORS[index];
        };
    
        // Keyboard events
    
        $window.keydown((event) => {
            // Auto-focus the current input when a key is typed
            if (!(event.ctrlKey || event.metaKey || event.altKey)) {
                $currentInput.focus();
            }
            // When the client hits ENTER on their keyboard
            if (event.which === 13) {
                if (username) {
                    sendMessage();
                    socket.emit('stop typing');
                    typing = false;
                } else {
                    setUsername();
                }
            }
        });
    
        $inputMessage.on('input', () => {
            updateTyping();
        });
    
        // Click events
    
        // Focus input when clicking anywhere on login page
        $loginPage.click(() => {
            $currentInput.focus();
        });
    
        // Focus input when clicking on the message input's border
        $inputMessage.click(() => {
            $inputMessage.focus();
        });
    
        // Socket events
    
        // Whenever the server emits 'login', log the login message
        socket.on('login', (data) => {
            connected = true;
            // Display the welcome message
            if (!loginFlag) {
                loginFlag = true;
                var message = '欢迎来到技术交流群(ಥ_ಥ) ';
                log(message, {
                    prepend: true
                });
                addParticipantsMessage(data);
            }
        });
    
        // Whenever the server emits 'new message', update the chat body
        socket.on('new message', (data) => {
            addChatMessage(data);
        });
    
        // Whenever the server emits 'user joined', log it in the chat body
        socket.on('user joined', (data) => {
            log(data.username + ' 进来了(*^▽^*)');
            addParticipantsMessage(data);
        });
    
        // Whenever the server emits 'user left', log it in the chat body
        socket.on('user left', (data) => {
            log(data.username + ' 离开了!!!∑(゚Д゚ノ)ノ');
            addParticipantsMessage(data);
            removeChatTyping(data);
        });
    
        // Whenever the server emits 'typing', show the typing message
        socket.on('typing', (data) => {
            addChatTyping(data);
        });
    
        // Whenever the server emits 'stop typing', kill the typing message
        socket.on('stop typing', (data) => {
            removeChatTyping(data);
        });
    
        socket.on('connect', () => {
            console.log('connect..');
        });
    
        socket.on('disconnect', () => {
            log('服务器已断开...');
        });
    
        socket.io.on('reconnect', () => {
            console.log('...xxx');
            log('服务器已重连...');
        });
    
        socket.io.on('reconnect_attempt', () => {
            // 发起轮询时
            console.log('reconnect_attempt');
        });
    
        socket.io.on('reconnect_error', () => {
            // 发起轮询失败时
            console.log('reconnect_error');
        });
    
        socket.io.on('reconnect_failed', () => {
            console.log('reconnect_failed');
        });
    
        socket.on('connect_error', () => {
            log('服务器重连失败...');
        });
    
        // todo refresh
    });
    
    

    问题

    • 服务器热更新会重置全局变量,所以有undefined离开的情况,或者重连应该强制退出?

    相关文章

      网友评论

          本文标题:socket.io简单使用

          本文链接:https://www.haomeiwen.com/subject/heymvrtx.html