本文介绍了一次与两个用户随机聊天(Socket.io)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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

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

更新

添加了客户端站点代码 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服务器.查看此示例,以了解如何为用户提供html& js.

您还会注意到命名空间和.这些用于将用户划分为子类别.一台服务器可以包含多个名称空间(默认情况下只有一个),每个名称空间可以包含多个房间.您可能不需要打扰名称空间,一个名称就足够满足您的情况了.但是,您将需要了解房间(稍后会详细介绍).

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

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

重要的是要知道,这里的 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 ,在该方法中,您应该为每个客户端添加事件侦听器(您已经在代码中进行了此操作,尤其是处理事件新消息",添加用户",键入",停止键入"和断开连接").

您需要什么事件

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

事件名称->给出的数据

在服务器端处理的事件

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

在客户端处理的事件

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

开始之前的最后一个提示

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

工作流程

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

  1. 在服务器端

io.on('connection', handler)将被解雇.将仅安装适用于新套接字的处理程序,此时不执行任何其他操作.

  1. 在客户端上

socket.on('connect', handler)将被解雇.此时,客户端应将用户名存储在某处.如果没有,那就没问题.连接将在相当长的时间内保持活动状态.连接后,您可以随时调用socket.emit('login', {'username':name)(在下面的示例中,我设置了connected变量,该变量默认为false,但在建立连接后立即设置为true.)

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

将他们都添加完后,您需要通知他们他们的聊天已经开始,并向他们发送对方的名字.您将发出事件聊天开始".

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

注意:我现在对socketio代码真的很困惑.在此查看"并告诉我,如果socket.rooms是数组对象(socket.rooms[room] = room; ??为什么?)

为避免处理此非直截了当的代码,让我们创建另一个全局对象rooms,该对象将为我们存储房间名称.我们将在那里映射socket.id-> roomName.

因此,当出现消息时,我们可以通过调用rooms[socket.id]来获取房间的名称.然后我们像这样广播消息:

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

其中 data 是我们从发送方收到的,因此对象{'text':'some nice message'}.然后您的同伴将收到(不会)并显示它(发送时应显示它).

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

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

某些代码

客户端

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.

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

Tl; dr

我什至不感到惊讶.在这里,阅读漫画

I just started learning NodeJS and Socket.io. Until now I have this demo code, from official socket.io site:

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

I am able to get the unique client's ID of each user (socket) which connects, I am still trying to figure out, How can I make my code to only connect with 1 random user at a time when somebody runs the application. I just want to make random chat like Omegle (http://www.omegle.com/).

Only two users should randomly connect and chat with each other till they re-run the app, if they come back they should get connected with someone else who is in the online queue.

What changes do I need to do to have a similar behaviour?

Update

Added Client site code, 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'});

    }
  }
解决方案

Although I would close this question because it's too vague, I feel obliged to give you some insight since I worked way too much with websockets in the last years (although not that much with socketio & nodejs). I suppose some simple guide and relevant links could help you. So first,

Kind of relevant intro

You should already know that Socket.io is a WebSocket implementation.WebSockets (WS) allow server to send data whenever it wants, as long as the connection is still open, as opposed to old way: client querying all the time asking, if there is an update on the server.

You can imagine a woman and a man at the end of a party: "Thanks for tonight, I'd love to repeat it sometimes soon. Would you give me your number?" - asks the old man. "Ughhh, you know what, better give me yours, I promise I will call you!"If the girl were to give him her number, he'd call a few times a day asking if she'd go somewhere (and she'd reply no). The other way around, she would call him only if she wanted to go and he would go. Of course he would.I got a bit carried away, but you get the picture. The woman is a server, the guy is a client.

What is important to understand

(Absolute basic, you should know this =>)When client connect to your server, (s)he should be served a html page and some javascript, which establishes connection to your WS server. In the code you've posted, Express is used as http server. Check this example to see how you should give user html&js.

You'll also notice namespaces and rooms in most of these tutorials. These are used for separating users into subcategories. One server may contain multiple namespaces (by default only one) and each namespace may contain multiple rooms. You probably won't need to bother with namespaces, one is just enough for your case. You will, however, need to understand rooms (more on that later).

Next thing, taken from your code

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

It's important to know, that socket here basically represent one connected client (in one namespace, but possibly in multiple rooms). You can do all sort of stuff with it, most notably:

  • install event handlers on it (that's what you do when you call socket.on(event, handler(data))
  • send events to it with socket.emit(event, data)
  • send broadcast event to all users with socket.broadcast.emit(event, data)
  • add/remove it to/from room with socket.join(room), socket.leave(room) respectively.
  • work with it as with an ordinary variable - store it wherever you want and then reuse it

Do you see the definition of numUsers in your code? That's a global variable which is shared with all clients, since nodejs is single-threaded. In the example it is being incremented inside one of the event handlers. Think we could use something like that? YES.

We can define global variable, queue for example. Or Q if you want. Point is, it can be an array used to store sockets, or rather clients, which are not currently in chat room.

At the end of this section I'd like to point out another obvious thing.io.on('connection', handler); defines an event handler for 'connection' event happening on the io object (WS server). This is triggered each time client makes connection to your WS server (in your case, through javascript ran inside client's browser). Argument to the method is socket and it is this method where you should add event listeners for each client (that you already do in the code, particularly handling events 'new message', 'add user', 'typing', 'stop typing' and 'disconnect').

What events shall you need

That really depends on how complex you want your app to be. In my opinion, the bare minimum would be (note that you can change the event names, but 'disconnect' should stay 'disconnect'):

event name -> data given

Events handled on server side

  • login -> username (how the user should be called), possibly password if you want to enable registration
  • message -> text (content of the message being sent)
  • leave room -> room name
  • disconnect

Event handled on client side

  • connect
  • chat start -> name (second client's name), room (so we can leave it)
  • chat end -> no data required if you want to allow only one chat at the same time. In case of multiple chats you should also include which chat got closed
  • disconnect

Last note before we get started

This is only a rough sketch. There are multiple different crossroads along the way and which path you take mostly depends on your idea of the app. If you want to have multiple chats opened at the same time, you'll need to do some modifications. The same goes if you want to have more than two people connected to the same chat. Here I'll describe the simplest case possible, one chat, to people, no registration. Possibly what you want, judging from your post. Could be wrong.

Workflow

User opens your page in their web browser. You serve them html and javascript. The javascript will start new connection to your websocket server. Also, handlers for desired events should be defined at this point.When the connection is established, this will be happening:

  1. ON SERVER SIDE

io.on('connection', handler) will be fired. Only appropriate handlers for new socket will be installed, not doing anything else at this point.

  1. ON CLIENT SIDE

socket.on('connect', handler) will be fired. Client should at that point have username stored somewhere. If not, no problem. The connection will be alive for quite some time. You can just call socket.emit('login', {'username':name) any time you wish after you are connected (in the example below I set up variable connected, which defaults to false but will be set to true as soon as connection is established.)

After you send login event from client, server registers it and saves it somewhere. Possibilities are endless, in this case I'll create global dictionary which maps socket.id to username. After that, user socket should be either paired with another one or added to queue.So, if the queue is empty, simply append socket to global variable (it doesn't have to be an array, since we will pair the first available sockets together, however you may want to implement some history of users so they won't get connected to the same person again). If the queue is not empty, we pull one socket out of the Q and add them to the same room. Room name can be random or whatever you want, I'll use (socket1.id+'#'+socket2.id (if you wanted to have more users in one chat, this would have to be changed).

After you add them both, you'll need to notify them that their chat has started and send them the other peer's name. You will emit event 'chat start'.

Clients will catch the event and open new window. After that, whenever user types something and sends it, client emits event 'message' with payload {'message': user_inserted_text}. Server will capture it in the .on('message' handler and broadcast it to the room. Note:

Note: I am really confused about socketio code right now. Look at this and tell me, if socket.rooms is an array or an object (socket.rooms[room] = room; ?? why?)

To avoid dealing with this not-straightforward code, lets create another global object, rooms, which will store the room names for us. We will map socket.id -> roomName there.

So when message comes, we can get name of the room by calling rooms[socket.id]. Then we broadcast the message like this:

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

Where data is what we received from the sender, therefore object {'text': 'some nice message'}. Your peer will then receive it (you won't) and display it (you should display it when you are sending it).

So the chat continues like this for a while, then one of the users decides (s)he wants to leave / chat with somebody else. They will close window and client will emit event 'leave room'. Server will capture it and send to the other party that her/his peer has disconnected. The same should happen if the client disconnects. After everything is closed, add both users to queue (or only one, if the other has disconnected from the server). In my code I will not make sure they won't get paired again. That is for the OP to code (can't be hard).

So, if you read this far, you deserve some actual code. Although I say actual, it's actually untested. But you know, it should work like this.

Some code

Client side

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 = '';
    }
};

Server side

Not including initial requires and html/js serving., only global definitions and main io handler.

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.

The code above got a bit messy in the end. It can be done better and I encourage you to do better job than I did. Having this material at hand, go through it step by step and try to understand. I think I commented most, if not all of it. Good luck.

Tl;dr

I am not even surprised. Here, read a comic

这篇关于一次与两个用户随机聊天(Socket.io)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-18 08:16
查看更多