一次与2个用户随机聊天(Socket.io)

时间:2016-02-13 21:37:02

标签: node.js sockets random socket.io chat

我刚开始学习NodeJS和Socket.io ...到目前为止,我有这个演示代码,来自官方socket.io网站:

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

我能够获得连接的每个用户(套接字)的唯一客户端ID,我仍然试图弄清楚,如何使我的代码只能连接1个随机用户有人运行应用程序。我只是想像聊天http://www.omegle.com/

一样随机聊天

只有2个用户应该随机连接并相互聊天,直到他们重新运行应用程序,如果他们回来,他们应该与在线队列中的其他人联系。

我需要做些什么改变才能有类似的行为?

到目前为止,这是我的代码:

专家的代码示例或指导对我这样的菜鸟非常有帮助。

更新:添加了客户端网站代码 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'});

    }
  }

提前致谢

1 个答案:

答案 0 :(得分:31)

虽然我会因为它太模糊而关闭这个问题,但我觉得有必要给你一些见解,因为我在过去几年中使用websockets进行了太多的工作(尽管socketio& nodejs没那么多)。我想一些简单的指南和相关链接可以帮助你。首先,

相关介绍的种类

您应该已经知道 Socket.io WebSocket 实现。 WebSockets(WS)允许服务器随时发送数据,只要连接仍然是打开的,而不是旧的方式:客户端查询一直询问,如果服务器上有更新。

你可以想象一个女人和一个男人在聚会结束时:" 感谢今晚,我很快就会重复它。你能告诉我你的号码吗?" - 老人问道。 " 呃,你知道吗,最好给我你的,我保证我会打电话给你!" 如果这个女孩要给他一个号码,他每天都会打几次电话询问她是否会去某个地方(而且她没有回复)。反过来说,只有当她想去,他才会打电话给他。他当然会。 我有点沮丧,但你得到了照片。女人是服务员,男人是客户。

理解什么是重要的

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

在大多数教程中,您还会注意到namespacesrooms。这些用于将用户分成子类别。一个服务器可能包含多个命名空间(默认情况下只有一个),每个命名空间可能包含多个空间。你可能不需要打扰命名空间,一个就足够了你的情况。但是,您需要了解rooms(稍后会详细介绍)。

接下来,取自你的代码

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

重要的是要知道,套接字在这里基本上代表一个连接的客户端(在一个命名空间中,但可能在多个房间中)。你可以用它做各种各样的事情,最值得注意的是:

  • 在其上安装事件处理程序(这是您拨打socket.on(event, handler(data))
  • 时所执行的操作)
  • 使用socket.emit(event, data)
  • 向其发送活动
  • 使用socket.broadcast.emit(event, data)
  • 向所有用户发送广播事件
  • 分别使用socket.join(room)socket.leave(room)添加/删除会议室
  • 普通变量一起使用 - 将其存储在任何地方,然后重复使用

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

我们可以定义全局变量,例如队列。或Q如果你想。重点是,它可以是用于存储 socket 的数组,或者更确切地说是客户端,它们当前不在聊天室中。

在本节结尾处,我想指出另一个显而易见的事情。 io.on('connection', handler);为'连接'定义事件处理程序。事件发生在 io 对象(WS服务器)上。每次客户端连接到您的WS服务器时都会触发此操作(在您的情况下,通过在客户端浏览器中运行的javascript)。该方法的参数是 socket ,这个方法是你应该为每个客户端添加事件监听器(你已经在代码中做了,特别是处理事件'新消息',& #39;添加用户','输入','停止输入'以及'断开')。

您需要什么样的活动

这实际上取决于您希望应用程序的复杂程度。在我看来,最低限度是(请注意,您可以更改活动名称,但是'断开'应该保持'断开连接'):

活动名称 - >给出的数据

在服务器端处理的事件

  • 登录 - >用户名(如何调用用户),如果要启用注册,则可能是密码
  • 消息 - >文本(正在发送的消息的内容)
  • 离开房间 - >房间名称
  • 断开

在客户端处理的事件

  • 连接
  • 聊天开始 - >姓名(第二个客户的名字),房间(所以我们可以留下)
  • 聊天结束 - >如果您只想同时允许一个聊天,则无需数据。如果有多个聊天,您还应该包括哪个聊天已关闭
  • 断开

我们开始之前的最后一点

这只是一个粗略的草图。沿途有多个不同的十字路口,您走的路径主要取决于您对应用的想法。如果您想同时打开多个聊天记录,则需要进行一些修改。如果您想让两个以上的人连接到同一个聊天,也是如此。在这里,我将描述最简单的情况,一个聊天,一个人,没有注册。从你的帖子来看,可能你想要什么。可能是错的。

工作流

用户在其网络浏览器中打开您的页面。你为他们提供HTML和JavaScript。 javascript将启动与websocket服务器的新连接。此外,此时应定义所需事件的处理程序。 建立连接后,就会发生这种情况:

  1. ON SERVER SIDE
  2. io.on('connection', handler)将被解雇。只安装适用于新套接字的处理程序,此时不做任何其他操作。

    1. ON CLIENT SIDE
    2. socket.on('connect', handler)将被解雇。客户端应该在某处存储用户名。如果没有,没问题。连接将存在很长一段时间。您可以在连接后随时调用socket.emit('login', {'username':name)(在下面的示例中,我设置变量已连接,默认为false,但一旦建立连接就会设置为true。)

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

      在您同时添加它们之后,您需要通知他们他们的聊天已经开始并向他们发送其他同行的名称。您将发出事件'聊天开始'。

      客户端将捕获事件并打开新窗口。之后,每当用户输入内容并发送内容时,客户端都会发出事件消息'有效负载{' message':user_inserted_text}。服务器将在.on('message'处理程序中捕获它,并将broadcast捕获到房间。注意:

        

      广播意味着向除了启动它的套接字以外的所有人发送消息。

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

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

      因此,当消息传来时,我们可以通过致电rooms[socket.id]来获取房间的名称。然后我们广播这样的信息:

      socket.broadcast.to(room).emit('message', data);
      

      数据是我们从发件人处收到的内容,因此对象{' text':'一些不错的消息'}。然后你的同伴会收到它(你赢了)并显示它(你应该在发送时显示它)。

      所以聊天会持续一段时间,然后其中一个用户决定他要离开/与其他人聊天。他们将关闭窗口,客户将发出事件'离开房间'。服务器将捕获它并将其/他的同伴断开连接发送给另一方。如果客户端断开连接,也会发生同样的情况。关闭所有内容后,将两个用户添加到队列(如果另一个已与服务器断开连接,则只添加一个)。在我的代码中,我不会确保他们不会再次配对。这是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。

      上面的代码到最后有点乱。它可以做得更好,我鼓励你做得比我做得更好。掌握了这些材料,逐步完成并尝试理解。我想我评论最多,如果不是全部的话。祝你好运。

      铊组成; dr

      我甚至都不感到惊讶。在这里,阅读comic