node.js - 一次与两个用户随机聊天(Socket.io)

标签 node.js sockets random socket.io chat

我刚刚开始学习NodeJS和Socket.io。到目前为止,我已经从socket.io官方网站获得了这个演示代码:

http://socket.io/demos/chat/

我能够获取所连接的每个用户(套接字)的唯一客户端ID,但我仍在设法弄清楚,如何使我的代码在某人运行该应用程序时一次只能与1个随机用户连接。我只想像Omegle(http://www.omegle.com/)进行随机聊天。

只有两个用户应该随机连接并彼此聊天,直到他们重新运行该应用程序为止;如果他们回来了,他们应该与处于在线队列中的其他人建立连接。

要具有类似的行为,我需要做些什么更改?

更新

添加了客户端站点代码 main.js

$(function() {
  var FADE_TIME = 150; // ms
  var TYPING_TIMER_LENGTH = 400; // ms
  var COLORS = [
    '#e21400', '#91580f', '#f8a700', '#f78b00',
    '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
    '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
  ];

  // Initialize variables
  var $window = $(window);
  var $usernameInput = $('.usernameInput'); // Input for username
  var $messages = $('.messages'); // Messages area
  var $inputMessage = $('.inputMessage'); // 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;
  var $currentInput = $usernameInput.focus();


  //Own Global

  var room = '';

  var socket = io();

  function addParticipantsMessage (data) {
    var message = '';
    if (data.numUsers === 1) {
     // message += "there's 1 participant";
     // Status Message
        message += "Waiting to connect with someone";

    } else {
     // message += "there are " + data.numUsers + " participants";
     //Status message update
      message = "You are connected to a stranger! Say Hey!";
    }
    log(message);
  }

  // Sets the client's username
  function 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);

      // Own
      socket.emit('login', {'username' : 'Faizan'});

    }
  }

最佳答案

尽管由于过于笼统,我将结束这个问题,但是由于过去几年我在websockets上做了大量的工作(尽管在socketio和nodejs上做的工作不多),我还是有义务向您提供一些见解。我想一些简单的指南和相关链接可以为您提供帮助。所以首先

相关介绍的种类

您应该已经知道 Socket.io WebSocket 实现。
WebSockets(WS)允许服务器在需要时随时发送数据,只要连接仍处于打开状态即可,这与旧方法相反:客户端一直在查询,询问服务器上是否有更新。

您可以想象一个聚会结束时一个女人和一个男人:“谢谢您,今晚,我很乐意有时再重复一遍。您能给我您的电话吗?” -老人问。 “恩,你知道吗,最好把你的给我,我保证我会调用你的!”
如果女孩要给他她的电话号码,他每天会打几次电话问她是否会去某个地方(她会回答“否”)。相反,她只有在她想离开并且他会离开的情况下才给他打电话。当然可以。
我有些不高兴,但你知道了。女人是服务器,男人是客户。

重要的是要了解

(绝对基础,您应该知道这个=>)
当客户端连接到您的服务器时,应该为他提供一个html页面和一些javascript,以建立与您的WS服务器的连接。在您发布的代码中, Express 用作http服务器。检查this example以查看应如何为用户提供html&js。

您还将在大多数这些教程中注意到namespacesrooms。这些用于将用户划分为子类别。一台服务器可以包含多个 namespace (默认情况下只有一个),每个 namespace 可以包含多个房间。您可能不需要打扰 namespace ,一个名称就足够满足您的情况了。但是,您将需要了解rooms(稍后会对此进行更多介绍)。

接下来的事情,取自您的代码

io.on('connection', function (socket) {

重要的是要知道,这里的套接字基本上表示一个连接的客户端(在一个 namespace 中,但可能在多个房间中)。您可以用它来做各种各样的事情,最著名的是:
  • 在上面安装事件处理程序(这就是调用socket.on(event, handler(data))
  • 时要做的事情
  • 使用socket.emit(event, data)向其发送事件
  • 使用socket.broadcast.emit(event, data)向所有用户发送广播事件
  • 分别使用socket.join(room)socket.leave(room)将其添加到房间中/从中删除。
  • 普通变量一起使用-将其存储在所需的位置,然后再使用

  • 您是否在代码中看到 numUsers 的定义?这是一个与所有客户端共享的全局变量,因为nodejs是单线程的。在示例中,它是在事件处理程序之一中递增的。认为我们可以使用类似的东西吗?

    我们可以定义全局变量,例如队列。或Q,如果您想要的话。关键是,它可以是用于存储套接字的数组,或者是存储当前不在聊天室中的客户端的数组。

    在本节的最后,我想指出另一件事。io.on('connection', handler);定义 io 对象(WS服务器)上发生的“连接”事件的事件处理程序。每当客户端与您的WS服务器建立连接(在您的情况下,是通过客户端浏览器内部运行的javascript)触发的。该方法的参数是套接字,在此方法中,您应该为每个客户端添加事件监听器(您已经在代码中进行了此操作,特别是处理事件“新消息”,“添加用户”,“键入”,“停止键入”和“断开连接”)。

    您需要什么事件

    这实际上取决于您希望应用程序的复杂程度。我认为,最低要求是(请注意,您可以更改事件名称,但“断开连接”应保持为“断开连接”):

    事件名称->给定的数据

    服务器端处理的事件
  • 登录->用户名(应如何称呼用户),如果要启用注册
  • ,可以输入密码
  • 消息->文本(正在发送的消息的内容)
  • 离开房间->房间名称
  • 断开

  • 在客户端处理事件
  • 连接
  • 聊天开始->名称(第二个客户的名字),房间(以便我们离开)
  • 聊天结束->如果您想一次只允许一次聊天,则不需要数据。如果有多个聊天,您还应该包括哪个聊天已关闭
  • 断开

  • 开始之前的最后一个注意事项

    这只是一个粗略的草图。一路走来有许多不同的十字路口,走哪条路主要取决于您对应用程序的想法。如果要同时打开多个聊天,则需要进行一些修改。如果您想让两个以上的人连接到同一聊天室,也是如此。在这里,我将向大家介绍最简单的情况,一次聊天,无需注册。从您的帖子来看,可能是您想要的。可能是错误的。

    工作流程

    用户在其网络浏览器中打开您的页面。您为他们提供html和javascript。 javascript将开始与您的websocket服务器的新连接。另外,此时应定义所需事件的处理程序。
    建立连接后,将发生以下情况:
  • 在服务器端
  • io.on('connection', handler)将被解雇。仅安装适用于新套接字的处理程序,此时不执行任何其他操作。
  • 客户端上的
  • socket.on('connect', handler)将被解雇。此时,客户端应将用户名存储在某处。如果没有,那就没问题。连接将在相当长的时间内保持事件状态。连接后,您可以随时调用socket.emit('login', {'username':name)(在下面的示例中,我设置了connected变量,该变量默认为false,但在建立连接后将立即设置为true。)

    从客户端发送登录事件后,服务器将其注册并保存到某处。可能性无穷无尽,在这种情况下,我将创建将socket.id映射到用户名的全局字典。之后,用户套接字应与另一个套接字配对或添加到队列中。
    因此,如果队列为空,只需将套接字附加到全局变量(它不必是数组,因为我们会将第一个可用的套接字配对在一起,但是您可能想要实现一些用户的历史记录,因此他们不会重新与同一个人建立联系)。如果队列不为空,我们从Q中拉出一个 socket ,并将其添加到同一房间。房间名称可以是随机的,也可以是您想要的任何名称,我将使用(socket1.id +'#'+ socket2.id(如果您希望在一次聊天中拥有更多用户,则必须进行更改)。

    将他们都添加完后,您需要通知他们聊天已开始,并向他们发送其他对等方的名字。您将发出事件“聊天开始”。

    客户将 catch 事件并打开新窗口。此后,每当用户键入内容并将其发送时,客户端就会发出带有有效负载{'message':user_inserted_text}的事件'message'。服务器将在.on('message'处理程序中捕获它,并将它broadcast捕获到房间中。笔记:

    Broadcasting means sending a message to everyone else except for the socket that starts it.



    注意:我现在对socketio代码真的很困惑。看看at this并告诉我,如果socket.rooms是an arrayan object(socket.rooms[room] = room; ??为什么?)

    为了避免处理这种不简单的代码,让我们创建另一个全局对象rooms,它将为我们存储房间名称。我们将在那里映射socket.id-> roomName。

    因此,当出现消息时,我们可以通过调用rooms[socket.id]来获得房间的名称。然后我们广播这样的消息:
    socket.broadcast.to(room).emit('message', data);
    

    数据是我们从发送方收到的数据,因此对象为{'text':'some nice message'}。然后,您的对等方将收到(您不会)并显示它(发送时应显示它)。

    因此,这样的聊天持续了一段时间,然后其中一个用户决定他要离开/与其他人聊天。他们将关闭窗口,客户将发出事件“离开房间”。服务器将捕获它,并将其发送给其对等方已断开连接的另一方。如果客户端断开连接,也会发生同样的情况。关闭所有内容后,将两个用户都添加到队列中(如果另一个与服务器断开连接,则仅添加一个)。在我的代码中,我不会确保它们不会再次配对。那是为了让OP进行编码(不难)。

    因此,如果您读了这么多,应该得到一些实际的代码。尽管我说的是实际的,但实际上尚未经过测试。但您知道,它应该像这样工作。

    一些代码

    客户端
    var connected = false;
    var username = 'Faizan';
    var room = '';
    var socket = io('http://localhost');
    socket.on('connect', function (data) { // we are connected, should send our name
        connected = true;
        if (username) socket.emit('login', {'username' : username});
    });
    socket.on('chat start', function(data) {
        room = data.room;
        show_chat_window(data.name); // some method which will show chat window
    });
    socket.on('chat end', function(data) {
        hide_chat_window(); // this will close chat window and alert user that the peer ended chat
        socket.leave(room); // it's possible to leave from both server and client, hoever it is better to be done by the client in this case
        room = '';
    });
    socket.on('disconnect', function(data) { // handle server/connection falling
        console.log('Connection fell or your browser is closing.');
    });
    var send_message = function(text) { // method, which you will call when user hits enter in input field
        if (connected) socket.emit('message', {'text': text});
    };
    var leave_chat = function() { // call this when user want to end current chat
        if (connected) {
            socket.emit('leave room');
            socket.leave(room);
            room = '';
        }
    };
    

    服务器端

    不包括初始要求和html/js服务。仅包括全局定义和主要io处理程序。
    var queue = [];    // list of sockets waiting for peers
    var rooms = {};    // map socket.id => room
    var names = {};    // map socket.id => name
    var allUsers = {}; // map socket.id => socket
    
    var findPeerForLoneSocket = function(socket) {
        // this is place for possibly some extensive logic
        // which can involve preventing two people pairing multiple times
        if (queue) {
            // somebody is in queue, pair them!
            var peer = queue.pop();
            var room = socket.id + '#' + peer.id;
            // join them both
            peer.join(room);
            socket.join(room);
            // register rooms to their names
            rooms[peer.id] = room;
            rooms[socket.id] = room;
            // exchange names between the two of them and start the chat
            peer.emit('chat start', {'name': names[socket.id], 'room':room});
            socket.emit('chat start', {'name': names[peer.id], 'room':room});
        } else {
            // queue is empty, add our lone socket
            queue.push(socket);
        }
    }
    
    io.on('connection', function (socket) {
        console.log('User '+socket.id + ' connected');
        socket.on('login', function (data) {
            names[socket.id] = data.username;
            allUsers[socket.id] = socket;
            // now check if sb is in queue
            findPeerForLoneSocket(socket);
        });
        socket.on('message', function (data) {
            var room = rooms[socket.id];
            socket.broadcast.to(room).emit('message', data);
        });
        socket.on('leave room', function () {
            var room = rooms[socket.id];
            socket.broadcast.to(room).emit('chat end');
            var peerID = room.split('#');
            peerID = peerID[0] === socket.id ? peerID[1] : peerID[0];
            // add both current and peer to the queue
            findPeerForLoneSocket(allUsers[peerID]);
            findPeerForLoneSocket(socket);
        });
        socket.on('disconnect', function () {
            var room = rooms[socket.id];
            socket.broadcast.to(room).emit('chat end');
            var peerID = room.split('#');
            peerID = peerID[0] === socket.id ? peerID[1] : peerID[0];
            // current socket left, add the other one to the queue
            findPeerForLoneSocket(allUsers[peerID]);
        });
    });
    

    P.S.

    上面的代码最后有点困惑。可以做得更好,我鼓励您做得更好。掌握了这些 Material 后,请逐步进行操作并尝试理解。我想我评论最多,即使不是全部。祝你好运。

    Tl;博士

    我什至都不惊讶。在这里,阅读comic

    关于node.js - 一次与两个用户随机聊天(Socket.io),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35385609/

    相关文章:

    node.js - 如何在 Electron + Node -ffi-napi中获得正确的 native 内存地址

    javascript - 返回数组内对象的值

    javascript - Firebase 不部署。错误的自签名证书

    java - 在后台接收消息

    c++ - 在库中使用 srand() 的问题

    javascript - 如何在托管 Web 服务器上实际部署 Node.js(或 django)项目?

    macos - Mac更改IP总长度字段

    java - 是否可以在全双工 TCP 通信中使用 Netty?

    c++ - 创建随机迭代器(排列)

    c - 开头的随机数组大小?