1、前端界面代码

  前端不是重点,够用就行,下面是前端界面,具体代码可到github下载。

2、服务器端搭建

  本服务器需要提供两个功能:http服务和websocket服务,由于node的事件驱动机制,可将两种服务搭建在同一个端口下。

  1、包描述文件:package.json,这里用到了两个依赖项,mime:确定静态文件mime类型,socket.io:搭建websocket服务,然后使用npm install  安装依赖

{
"name": "chat_room",
"version": "1.0.0",
"description": "this is a room where you can chat with your friends",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "sfs",
"license": "ISC",
"dependencies": {
"socket.io":"2.0.3",
"mime":"1.3.6"
}
}

  2、http服务器

  http服务主要是给web浏览器提供静态文件,既浏览器发来一个请求,服务器返回一个响应。

 const
http=require('http'),
fs=require('fs'),
path=require('path'),
mime=require('mime'),
chatServer=require('./lib/chat_server'); var cache={};//缓存静态文件内容
//发送错误响应
function send404(response){
response.writeHead(404,{'Content-Type':'text/plain'});
response.write('Error 4.4:文件未找到。');
response.end();
}
//发送文件内容
function sendFile(response,filePath,fileContents){
response.writeHead(
200,
{"content-Type":mime.lookup(path.basename(filePath))}
);
response.end(fileContents);
}
//查找文件
function serveStatic(response,cache,absPath){
if(cache[absPath]){
sendFile(response,absPath,cache[absPath]);
}else{
fs.exists(absPath,function(exists){
if(exists){
fs.readFile(absPath,function(err,data){
if(err){
send404(response);
}else{
cache[absPath]=data;
sendFile(response,absPath,data);
}
});
}else{
send404(response);
}
});
}
} //入口
var server=http.createServer(function(request,response){
var filePath=false;
console.log(`new request for ${request.url}`);
if(request.url==='/'){
filePath='public/index.html';
}else{
filePath='public'+request.url;
} var absPath='./'+filePath;
serveStatic(response,cache,absPath);
});
server.listen(3000,function(){
console.log("the server is listening on prot 3000.");
});
chatServer.listen(server); //websocket服务也绑定到该端口上

  3、socket服务

  socket.io提供了开箱既用的虚拟通道,所以不需要任务手动转发消息到已连接的的用户,可以使用 socket.broadcast.to(room).emit('message','hello'); room为某个聊天室id

 const
socketio=require('socket.io'); var io,
guestNumber=1, //用户编号
nickNames={}, //socket id对应的nickname
namesUsed={}, //所有已使用的nickname
allRooms={}, //聊天室--人数
currentRoom={}; //sockid--聊天室 module.exports.listen=function(server){
io=socketio.listen(server);
io.serveClient('log level',1);
io.sockets.on('connection',function(socket){
guestNumber=assignGuestName(socket,guestNumber,nickNames);
joinRoom(socket,'Lobby');
handleMessageBroadcasting(socket,nickNames);
handleNameChangeAttempts(socket,nickNames,namesUsed);
handleRoomJoining(socket);
socket.on('rooms',function(){
socket.emit('rooms',JSON.stringify(allRooms));
});
handleClientDisconnection(socket,nickNames,namesUsed);
});
};
//新socket连入,自动分配一个昵称
function assignGuestName(socket,guesetNumber,nickNames){
var name='Guest'+guestNumber;
nickNames[socket.id]=name;
socket.emit('nameResult',{
success:true,
name:name
});
namesUsed[name]=1;
return guestNumber+1;
}
//加入某个聊天室
function joinRoom(socket,room){
socket.join(room);
var num=allRooms[room];
if(num===undefined){
allRooms[room]=1;
}else{
allRooms[room]=num+1;
}
currentRoom[socket.id]=room;
socket.emit('joinResult',{room:room});
socket.broadcast.to(room).emit('message',{
text:nickNames[socket.id]+' has join '+room+'.'
}); var usersinRoom=io.sockets.adapter.rooms[room];
if(usersinRoom.length>1){
var usersInRoomSummary='Users currently in '+room+' : ';
for(var index in usersinRoom.sockets){
if(index!=socket.id){
usersInRoomSummary+=nickNames[index]+',';
}
}
socket.emit('message',{text:usersInRoomSummary});
}
}
//修改昵称
function handleNameChangeAttempts(socket,nickNames,namesUsed){
socket.on('nameAttempt',function(name){
if(name.indexOf('Guest')==0){
socket.emit('nameResult',{
success:false,
message:'Names cannot begin with "Guest".'
});
}else{
if(namesUsed[name]==undefined){
var previousName=nickNames[socket.id];
delete namesUsed[previousName];
namesUsed[name]=1;
nickNames[socket.id]=name;
socket.emit('nameResult',{
success:true,
name:name
});
socket.broadcast.to(currentRoom[socket.id]).emit('message',{
text:previousName+' is now known as '+name+'.'
});
}else{
socket.emit('nameResult',{
success:false,
message:'That name is already in use.'
});
}
}
});
}
//将某个用户的消息广播到同聊天室下的其他用户
function handleMessageBroadcasting(socket){
socket.on('message',function(message){
console.log('message:---'+JSON.stringify(message));
socket.broadcast.to(message.room).emit('message',{
text:nickNames[socket.id]+ ': '+message.text
});
});
}
//加入/创建某个聊天室
function handleRoomJoining(socket){
socket.on('join',function(room){
var temp=currentRoom[socket.id];
delete currentRoom[socket.id];
socket.leave(temp);
var num=--allRooms[temp];
if(num==0)
delete allRooms[temp];
joinRoom(socket,room.newRoom);
});
}
//socket断线处理
function handleClientDisconnection(socket){
socket.on('disconnect',function(){
console.log("xxxx disconnect");
allRooms[currentRoom[socket.id]]--;
delete namesUsed[nickNames[socket.id]];
delete nickNames[socket.id];
delete currentRoom[socket.id];
})
}

3、客户端实现socket.io

  1、chat.js处理发送消息,变更房间,聊天命令。

 var Chat=function(socket){
this.socket=socket;//绑定socket
}
//发送消息
Chat.prototype.sendMessage=function(room,text){
var message={
room:room,
text:text
};
this.socket.emit('message',message);
};
//变更房间
Chat.prototype.changeRoom=function(room){
this.socket.emit('join',{
newRoom:room
});
};
//处理聊天命令
Chat.prototype.processCommand=function(command){
var words=command.split(' ');
var command=words[0].substring(1,words[0].length).toLowerCase();
var message=false; switch(command){
case 'join':
words.shift();
var room=words.join(' ');
this.changeRoom(room);
break;
case 'nick':
words.shift();
var name=words.join(' ');
this.socket.emit('nameAttempt',name);
break;
default:
message='Unrecognized command.';
break;
}
return message;
};

  2、chat_ui.js 处理用户输入,根据输入调用chat.js的不同方法发送消息给服务器

 function divEscapedContentElement(message){
return $('<div></div>').text(message);
}
function divSystemContentElement(message){
return $('<div></div>').html('<i>'+message+'</i>');
}
function processUserInput(chatApp,socket){
var message=$('#send-message').val();
var systemMessage;
if(message.charAt(0)=='/'){
systemMessage=chatApp.processCommand(message);
if(systemMessage){
$('#messages').append(divSystemContentElement(systemMessage));
}
}else{
chatApp.sendMessage($('#room').text(),message);
$('#messages').append(divSystemContentElement(message));
$('#messages').scrollTop($('#messages').prop('scrollHeight'));
}
$('#send-message').val('');
}

  3、init.js客户端程序初始化   创建一个websocket连接,绑定事件。

 if(window.WebSocket){
console.log('This browser supports WebSocket');
}else{
console.log('This browser does not supports WebSocket');
}
var socket=io.connect();
$(document).ready(function(){
var chatApp=new Chat(socket);
socket.on('nameResult',function(result){
var message;
if(result.success){
message='You are known as '+result.name+'.';
}else{
message=result.message;
}
console.log("nameResult:---"+message);
$('#messages').append(divSystemContentElement(message));
$('#nickName').text(result.name);
}); socket.on('joinResult',function(result){
console.log('joinResult:---'+result);
$('#room').text(result.room);
$('#messages').append(divSystemContentElement('Room changed.'));
}); socket.on('message',function(message){
console.log('message:---'+message);
var newElement=$('<div></div>').text(message.text);
$('#messages').append(newElement);
$('#messages').scrollTop($('#messages').prop('scrollHeight'));
}); socket.on('rooms',function(rooms){
console.log('rooms:---'+rooms);
rooms=JSON.parse(rooms);
$('#room-list').empty();
for(var room in rooms){
$('#room-list').append(divEscapedContentElement(room+':'+rooms[room]));
}
$('#room-list div').click(function(){
chatApp.processCommand('/join '+$(this).text().split(':')[0]);
$('#send-message').focus();
});
}); setInterval(function(){
socket.emit('rooms');
},1000); $('#send-message').focus();
$('#send-button').click(function(){
processUserInput(chatApp,socket);
});
});

完整代码,可到https://github.com/FleyX/ChatRoom 下载。

  

nodejs构建多房间简易聊天室的更多相关文章

  1. 基于Node.js + WebSocket 的简易聊天室

    代码地址如下:http://www.demodashi.com/demo/13282.html Node.js聊天室运行说明 Node.js的本质就是运行在服务端的JavaScript.Node.js ...

  2. node+websocket创建简易聊天室

    关于websocket的介绍太多,在这就不一一介绍了,本文主要实现通过websocket创建一个简易聊天室,就是90年代那种聊天室 服务端 1.安装ws模块,uuid模块,ws是websocket模块 ...

  3. Laravel + Swoole 打造IM简易聊天室

    最近在学习Swoole,利用Swoole扩展让PHP生动了不少,本篇就来Swoole开发一款简易的IM聊天室 应用场景:实现简单的即时消息聊天室. (一)扩展安装 pecl install swool ...

  4. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  5. 使用nodejs+express+socketio+mysql搭建聊天室

    使用nodejs+express+socketio+mysql搭建聊天室 nodejs相关的资料已经很多了,我也是学习中吧,于是把socket的教程看了下,学着做了个聊天室,然后加入简单的操作mysq ...

  6. node.js+websocket实现简易聊天室

    (文章是从我的个人主页上粘贴过来的,大家也可以访问我的主页 www.iwangzheng.com) websocket提供了一种全双工客户端服务器的异步通信方法,这种通信方法使用ws或者wss协议,可 ...

  7. php_D3_“简易聊天室 ”实现的关键技术 详解

                      PHP+MySQL实现Internet上一个简易聊天室的关键技术  系统目标: 聊天室使用数据库汇集每个人的发言,并可将数据库内的发言信息显示在页面,让每个用户都可 ...

  8. Python开发【笔记】:aiohttp搭建简易聊天室

    简易聊天室: 1.入口main.py import logging import jinja2 import aiohttp_jinja2 from aiohttp import web from a ...

  9. 学习JavaSE TCP/IP协议与搭建简易聊天室

    一.TCP/IP协议 1.TCP/IP协议包括TCP.IP和UDP等 2.域名通过dns服务器转换为IP地址 3.局域网可以通过IP或者主机地址寻找到相应的主机 4.TCP是可靠的连接,效率低,且连接 ...

随机推荐

  1. 【算法系列学习】状压dp [kuangbin带你飞]专题十二 基础DP1 D - Doing Homework

    https://vjudge.net/contest/68966#problem/D http://blog.csdn.net/u010489389/article/details/19218795 ...

  2. 一条sql语句引发mysql不停创建临时表的问题解决..coping to tmp table on desk

    (不懂临时表的先看 MySQL临时表 ) 首先,临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间.因此在不同的连接中可以创建同名的临时表,并且操作属于本连接的临时表.     ...

  3. 在Mvc中进行异步请求是出现(没有为该对象定义无参数的构造函数)

    解决办法就是给相应的类添加无参数的构造函数:

  4. 关于MAC设置免费的动态壁纸

    首先大部分的动态壁纸都是收费的或者是已经固定的,其实这一款也是固定的 但是这个固定的是可以进行修改的 第一先在App Store下载 LiveDesktop Pro  这一款是免费的 然后下载后进行打 ...

  5. Day4 函数、列表生成式、生成器、迭代器

    温故而知新: 1. 集合 主要作用: 去重 关系测试, 交集\差集\并集\反向(对称)差集 2. 元组 只读列表,只有count, index 2 个方法 作用:如果一些数据不想被人修改, 可以存成元 ...

  6. 倒计时(距离活动结束还有X天X小时X分X秒)

    一个简单的倒计时,可以设定结束时间,然后自动计算出距离活动结束还有X天X小时X分X秒. 废话不多说,上代码,挺简单的,代码里有注释: // 活动倒计时 var time_end = new Date( ...

  7. 关于JS面向对象中原型和原型链以及他们之间的关系及this的详解

    一:原型和原型对象: 1.函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址. 2.函数的原型对象__proto__:所有对象都有__ ...

  8. java jdk中安装证书的步骤

    需要注意的是:导入证书时,请确认导入的JDK为当前程序运行所用的JDK,且路径是jdk目录下的jre目录路径,非与jdk同级的jre目录 首先你可以把需要导入的证书放在keytool的同级目录下,然后 ...

  9. 如何在Unity中分别实现Flat Shading(平面着色)、Gouraud Shading(高洛德着色)、Phong Shading(冯氏着色)

    写在前面: 先说一下为什么决定写这篇文章,我也是这两年开始学习3D物体的光照还有着色方式的,对这个特别感兴趣,在Wiki还有NVIDIA官网看了相关资料后,基本掌握了渲染物体时的渲染管道(The re ...

  10. ASP.Net WebAPI与Ajax进行跨域数据交互时Cookies数据的传递

    前言 最近公司项目进行架构调整,由原来的三层架构改进升级到微服务架构(准确的说是服务化,还没完全做到微的程度,颗粒度没那么细),遵循RESTFull规范,使前后端完全分离,实现大前端思想.由于是初次尝 ...