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



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

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

我能够获得连接的每个用户(套接字)的唯一客户端ID,我仍在努力弄清楚,当有人运行应用程序时,我如何使我的代码一次只连接一个随机用户。我只想像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'});
}
}

虽然我会结束这个问题,因为它太模糊了,但我觉得有必要给你一些见解,因为我在过去几年里对websocket做了太多的工作(尽管对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,如果你愿意的话。重点是,它可以是一个用于存储套接字的数组,或者更确切地说,是当前不在聊天室中的客户端。

在本节的最后,我想指出另一件显而易见的事情。io.on('connection', handler);io对象(WS服务器)上发生的"connection"事件定义了事件处理程序。每当客户端连接到WS服务器时(在您的情况下,通过客户端浏览器内运行的javascript),都会触发此操作。该方法的参数是socket,您应该在该方法中为每个客户端添加事件侦听器(您在代码中已经这样做了,特别是处理事件"new message"、"add user"、"typeing"、"stop typeing"one_answers"disconnect")。

你需要什么活动

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

事件名称->给定的数据

在服务器端处理的事件

  • login->用户名(应该如何调用用户),如果您想启用注册,可能是密码
  • 消息->text(发送消息的内容)
  • 离开房间->房间名称
  • 断开

客户端处理的事件

  • 连接
  • 聊天开始->name(第二个客户的名字),房间(这样我们就可以离开了)
  • 聊天结束->如果您想同时只允许一次聊天,则不需要任何数据。如果是多个聊天,您还应该包括关闭的聊天
  • 断开

开始前的最后一个音符

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

工作流

用户在其web浏览器中打开您的页面。你为他们提供html和javascript。javascript将启动与您的websocket服务器的新连接。此外,此时应该定义所需事件的处理程序。当建立连接时,将发生以下情况:

  1. 在服务器端

io.on('connection', handler)将被激发。此时将只安装新套接字的适当处理程序,而不执行任何其他操作。

  1. 客户端

socket.on('connect', handler)将被激发。此时,客户端应该将用户名存储在某个位置。如果没有,没问题。该连接将保持相当长的一段时间。连接后,您可以随时调用socket.emit('login', {'username':name)(在下面的示例中,我设置了变量connected,默认为false,但一旦建立连接,就会设置为true。)

从客户端发送登录事件后,服务器会注册并将其保存在某个位置。可能性是无限的,在这种情况下,我将创建一个全局字典,将socket.id映射到username。之后,用户套接字应该和另一个套接字配对,或者添加到队列中。因此,如果队列为空,只需将套接字附加到全局变量(它不一定是数组,因为我们将把第一个可用的套接字配对在一起,但您可能希望实现一些用户历史记录,这样他们就不会再次连接到同一个人)。如果队列不是空的,我们从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);

其中数据是我们从发送者那里收到的,因此object{‘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]);
});
});

第页。S

上面的代码最后变得有点乱。它可以做得更好,我鼓励你做得比我做得更好。手头有这些材料,一步一步地仔细阅读,试着理解。我想我评论最多,如果不是全部的话。祝你好运。

Tl;dr

我甚至一点也不惊讶。在这里,阅读漫画

最新更新