一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个Demo系列的文章层次不是很清楚,索性这一章将所有的过程(前后端以及数据库)做一个介绍,下一章写完总结就OK了吧。

(1)前端部分

涉及到的技术:html、css、bootstrap、jquery、jquery UI

登录/注册界面使用的是bootstrap响应式布局,即支持不同尺寸的客户端,以此提高用户的体验。在这之前我以为聊天室比较适合做成SPA(单页应用),想采取backbone,但是结合毕设的主题是基于Node.js,如果采用backbone,路由功能就有两种选择,backbone和Node.js都有着丰富的路由API,由于之前没有用Node.js做过相关项目,所以就放弃了backbone。Demo通过改变css中display的属性来控制div的显示与隐藏。

页面:

 <div>
<div class="container">
<div class="row">
<div class="col-sm-5 col-md-5">
<div id="loginBox">
<form id="signinForm" class="form-signin" role="form" onsubmit="return false;">
<h2 class="form-signin-heading">Sign in</h2>
<input id="username" type="text" class="form-control" placeholder="Username" required="" autofocus="">
<input id="userpassword" type="password" class="form-control" placeholder="Password" required="">
<button id="loginBtn" class="btn btn-lg btn-primary btn-block">Sign in</button>
</form>
<p id="SignInErr"></p>
</div>
</div>
<div class="col-sm-2 col-md-2">
<div class="text-center"><br><br>
<h1>Or</h1>
</div>
</div>
<div class="col-sm-5 col-md-5">
<div id="signupBox">
<form id="signupForm" class="form-signin" role="form" onsubmit="return false;">
<h2 class="form-signin-heading">Sign up</h2>
<input id="upName" type="text" maxlength="5" class="form-control" placeholder="Username" required="" />
<input id="upPassword" type="password" maxlength="6" class="form-control" placeholder="Password" required="" />
<button id="signupBtn" class="btn btn-lg btn-primary btn-block">Sign up</button>
</form>
<p id="SignUpErr"></p>
</div>
</div>
</div>
</div>
<div id="main" class="hidden">
<div id="sideBar">
<div id="userInfo">
<img class="headImg" />
<span id="weather"></span>
</div>
<hr style="margin:0;">
<div id="control">
<div>
<span id="gloableName"></span>
<br>
<em></em>
</div>
<hr>
<ul>
<li id="set"><i class="glyphicon glyphicon-cog"></i>&nbsp;&nbsp;Setting</li>
<li id="changeUser"><i class="glyphicon glyphicon-transfer"></i>&nbsp;&nbsp;Switch</li>
<li id="layout"><i class="glyphicon glyphicon-off"></i>&nbsp;&nbsp;Layout</li>
</ul>
</div>
<ul id="setContent" style="display: none">
<li><i class="glyphicon glyphicon-eye-close"></i><em>&nbsp;&nbsp;Update Password</em></li>
<li><i class="glyphicon glyphicon-tags"></i><em>&nbsp;&nbsp;Personal Sign</em></li>
<li><i class="glyphicon glyphicon-user"></i><em>&nbsp;&nbsp;Head Portrait </em></li>
</ul>
<div id="setOne" style="display:none;">
<input type="password" placeholder="Old Password" maxlength="6" id="oldpass" />
<input type="password" placeholder="New Password" maxlength="6" id="newpass" />
<p></p>
</div>
<div id="setTwo" style="display: none;">
<input type="text" placeholder="write something will well" maxlength="16" />
<p></p>
</div>
<div id="setThree" style="display:none;">
<p>*Double click the picture to select</p>
<div id="imgContent">
<ul>
</ul>
</div>
</div>
<div id="chatChange">
<ul id="selectmenu">
<li>Square</li>
<li>Choose Room</li>
<ul id="selectRoom" style="display: none;">
<li><img src="/img/firsthead.jpg" alt="" /><span>The Legend of Qin</span></li>
<li><img src="/img/secondhead.jpg" alt=""><span>Naruto</span></li>
</ul>
</ul> <div>
</div>
</div>
</div>
<div id="chatBox">
<div id="headmessages"><strong>Square</strong></div>
<div id="content">
<ul id="messages"></ul>
</div>
<div id="chatbottom">
<div>
<span class="emotion" title="插入表情"><i class="glyphicon glyphicon-picture"></i></span>
<span id="clear" title="清空聊天窗口"><i class="glyphicon glyphicon-refresh"></i></span>
<span id="chatRecord" title="聊天历史消息"><i class="glyphicon glyphicon-time"></i></span>
</div>
<form id="chatMsgForm" onsubmit="return false;">
<textarea id="msg" rows="5" cols="35" maxlength="161" placeholder="Enter the content here, you can enter 161 characters at most ~">
</textarea>
<button id="send" class="btn btn-default"><i class="glyphicon glyphicon-send"></i></button>
</form>
</div>
</div>
<div id="model">
</div>
<div id="rightSide">
<span id="membersTitle">Members Information</span>
<div id="Allmembers">
<div>
<i></i>
<span>All Members</span>
<span id="oncount"></span>/<span id="allcount"></span>
</div>
<ul id="AllOnline"></ul>
<ul id="AllOutline"></ul>
</div>
<div id="Roommembers">
<div>
<i></i>
<span>Room Members</span>
<span id="roomCount"></span>
</div>
<ul>
</ul>
</div>
</div>
<div id="oldMsg" style="display: none;">
<span id="oldMsgHead" title="关闭历史消息窗口"><i class="glyphicon glyphicon-arrow-left"></i>&nbsp;&nbsp;MsgHistory</span>
<ul></ul>
<span id="clearoldMsg"><i title="清空聊天历史消息" class="glyphicon glyphicon-trash"></i></span>
</div>
</div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="/js/jquery.js"></script>
<script src="/js/jquery-ui.min.js"></script>
<script src="/js/app.js"></script>
<script src="/js/jquery.qqFace.js"></script>

js:

 $(function () {
var CookieObj = {}, socket = io(), headInfo = "群聊 (";;
window.onbeforeunload = function (e) {
if (document.cookie) return false;
}
render();
/*
*登录
*/
var onLogin = function (e) {
var xhr;
if (!$('#username').val() || !$('#userpassword').val()) return;
xhr = $.ajax({
url: '/login',
type: 'POST',
dataType: 'json',
data: {
name: $('#username').val(),
password: $('#userpassword').val()
}
})
.done(function (data, textStatus, jqXHR) {
if (data.value === 'Y') {
render();
} else {
$('#SignInErr').html(data.msg);
}
})
.fail(function (jqXHR, textStatus, errorThrown) {
$('#SignInErr').html('Error occured! Please try again.');
});
};
/*
*注册
*/
var onSignup = function (e) {
var xhr;
if (!$('#upName').val() || !$('#upPassword').val()) return;
xhr = $.ajax({
url: '/signup',
type: 'POST',
dataType: 'json',
data: {
name: $('#upName').val(),
password: $('#upPassword').val()
}
})
.done(function (data, textStatus, jqXHR) {
if (data.value === 'Y') {
$('#SignUpErr').html(data.msg || 'Login now with these credentials.');
} else {
$('#SignUpErr').html(data.msg || 'Invalid username');
}
})
.fail(function (jqXHR, textStatus, errorThrown) {
$('#SignUpErr').html('Error occured! Please try again.');
});
}; var onMsgSubmit = function () {
var str = $("#msg").val();
var sendMsg = replace_em(str);
if (!sendMsg || sendMsg.length > 1261) {
alert("err:内容为空或者内容长度超出限制!")
$('#msg').val('');
return;
}
var roomOf = $("#headmessages strong").html();
socket.emit('chat message', sendMsg, CookieObj.h_imgPath, roomOf);
$('#msg').val('');
return false;
}; socket.on('sysJoin', function (msg) {
var joinInfo = "";
joinInfo = '<li class="markInfo">' + msg + '</li>';
$(joinInfo).appendTo($("#messages")).animate({ "opacity": 0.5 }, 2000, function () {
$(this).animate({ "opacity": 1 }, 1500, function () {
$(this).animate({ "opacity": 0.3 }, 1000);
});
});
scroll();
}); socket.on('chat message', function (name, msg, img) {
var str = '';
if (name == CookieObj.name) {
str = '<li class="Liright"><p>' + msg + '</p><img class="msgImg" src="' + CookieObj.h_imgPath + '"/>' + '</li>';
} else {
str = '<li class="Lileft"><img class="msgImg" src="' + img + '"/><p>' + msg + '</p></li>';
}
$('#messages').append(str);
scroll();
}); /*房间选择*/
//默认是进广场,从其他房间执行如下函数
$("#selectmenu li").eq(0).on("click", function (e) {
e.stopPropagation();
$("#selectRoom").hide();
$("#headmessages strong").html("Square");
socket.emit('join', 'Square', $("#gloableName").html());
$("#messages").empty();
});
$("#selectmenu li").eq(1).on("click", function (e) {
e.stopPropagation();
$("#selectRoom").show();
});
//选择秦时明月或火影忍者房间
$("#selectRoom li").on("click", function () {
var roomName = $(this).children("span").html();
var userName = $("#gloableName").html();
$("#headmessages strong").html(roomName);
socket.emit('join', roomName, userName);
$("#messages").empty();
}); /*
*接收所有已注册用户的信息
*/
socket.on('onlineUser', function (online) {
var onlineStr = '';
for (var i = 0; i < online.length; i++) {
var item = online[i];
onlineStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Online]</em></li>';
}
$("#AllOnline").empty();
$("#oncount").html(online.length);
$("#AllOnline").append(onlineStr);
});
socket.on('outlineUser', function (outline) {
var outlineStr = '';
for (var i = 0; i < outline.length; i++) {
var item = outline[i];
outlineStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Outline]</em></li>';
}
$("#AllOutline").empty();
$("#AllOutline").append(outlineStr);
});
socket.on('allUser', function (doc) {
$('#allcount').html(doc.length);
});
socket.on('disconnect', function (name, msg) {
var leftInfo = "";
leftInfo = '<li class="markInfo leave">' + msg + '</li>';
$(leftInfo).appendTo($("#messages")).animate({ "opacity": 0.3 }, 2000, function () {
$(this).animate({ "opacity": 1 }, 1500, function () {
$(this).animate({ "opacity": 0.3 }, 1000);
});
return this;
});
scroll();
}); /*当前房间人员信息*/
var Lastr, r1, r2, r3;
socket.on('SquareRoom', function (roomInfo) {
r1 = roomInfo;
UpdateRoom();
});
socket.on('QinRoom', function (roomInfo) {
r2 = roomInfo;
UpdateRoom();
});
socket.on('NarutoRoom', function (roomInfo) {
r3 = roomInfo;
UpdateRoom();
});
function UpdateRoom() {
var $Nowroom = $("#headmessages strong").html(), roomCount, roomStr = '';
switch ($Nowroom) {
case "Square": Lastr = r1; break;
case "The Legend of Qin": Lastr = r2; break;
case "Naruto": Lastr = r3; break;
default: Lastr = r1;
}
roomCount = Lastr.length;
for (var i = 0; i < roomCount; i++) {
var item = Lastr[i];
roomStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Online]</em></li>';
}
$("#roomCount").html(roomCount);
$("#Roommembers ul").empty();
$("#Roommembers ul").append(roomStr);
}
/*
*切换/退出账号
*/
$("#changeUser").on('click', function () {
var res = confirm("Are you sure you want to quit and switch to another account??");
if (res) {
UL();
} else {
$("#control").hide();
$("#setContent").hide();
$("#stateSelect").hide();
}
});
$("#layout").on('click', UL);
function UL() {
if (document.cookie) {
$('#loginDiv').addClass('hidden');
$('#main').removeClass('hidden');
var uname = getCookie("userInfo");
CookieObj = JSON.parse(uname.substr(2));
$.ajax({
url: '/layout',
type: 'POST',
dataType: 'json',
data: {
name: CookieObj.name
}
})
.done(function (data, textStatus, jqXHR) {
if (data.value === 'Y') {
clearCookie();
window.location.reload();
}
});
};
} $('#signinForm #loginBtn').click(onLogin);
$('#signupForm #signupBtn').click(onSignup);
$('#chatMsgForm #send').click(onMsgSubmit); $("#clear").on("click", function () {
$('#messages').empty();
}); /*
*监听滚动条事件
*/
$('#messages').get(0).onscroll = function () {
$("#messages .Liright").css("margin-right", 1);
} /*
*屏蔽回车键
*/
$(document).keydown(function (event) {
switch (event.keyCode) {
case 13: return false;
}
});
/*
*用户信息
*/
$(".headImg").eq(0).on('click', function (e) {
e.stopPropagation();
if ($("#control").get(0).style.display == "none") {
$("#control").show();
} else {
$("#control").hide();
$("#setContent").hide();
$("#stateSelect").hide();
}
}); /*更改资料*/
$("#set").on("click", function () {
$("#setContent").show();
$("#stateSelect").hide();
});
/*构造头像选择内容*/
var imgStr = '';
for (var i = 1; i <= 18; i++) {
imgStr += '<li><img data-in="' + i + '" src="./img/' + i + '.jpg"/></li>';
if (i % 6 == 0) {
imgStr += "<br/>";
}
}
$("#setThree #imgContent ul").eq(0).append(imgStr);
$("#setThree #imgContent li img").on("click", function (e) {
e.stopPropagation();
var $index = $(this).attr("data-in");
$("#setThree #imgContent img").removeClass("imgSelected");
$("#setThree #imgContent img").eq(($index - 1)).addClass("imgSelected");
});
/*人物头像模态框*/
$("#setThree").dialog({
autoOpen: false,
title: "Changing Avatar",
modal: true,
width: 578,
resizable: false,
buttons: {
"Ok": function () {
var selectedImg = $(".imgSelected").attr("data-in");
// alert(selectedImg);
$.ajax({
url: "/updateImg",
type: "POST",
data: {
name: $('#control div span').eq(0).html(),
imgIndex: selectedImg
}
}).done(function (data) {
if (data.value === 'Y') {
$("#setThree").dialog("close");
$('.headImg').eq(0).attr('src', '/img/' + selectedImg + '.jpg');
$('#setContent').hide();
$('#control').hide();
// alert(data.msg);
}
});
;
}
}
});
/*个性签名模态框*/
$("#setTwo").dialog({
autoOpen: false,
title: "Personalized signature setting",
modal: true,
resizable: false,
buttons: {
"OK": function () {
var $newSign = $("#setTwo input[type='text']").eq(0).val();
if ($newSign != '') {
$.ajax({
url: '/updateSign',
type: 'POST',
data: {
name: $('#control div span').eq(0).html(),
newSign: $newSign
}
}).done(function (data) {
if (data.value === 'Y') {
$("#setTwo p").eq(0).html(data.msg);
setTimeout(function () {
$('#control div em').eq(0).html($newSign);
$("#setTwo").dialog('close');
$("#setTwo p").eq(0).html('');
}, 1000);
}
});
}
},
"Cancel": function () {
$(this).dialog('close');
}
}
});
/*密码模态框*/
$("#setOne").dialog({
autoOpen: false,
title: "Changeing User password",
modal: true,
resizable: false,
buttons: {
"Ok": function () {
var $oldpass = $("#setOne #oldpass").val(), $newpass = $("#setOne #newpass").val();
if ($oldpass != '' && $newpass != '') {
$.ajax({
url: '/changepass',
type: 'POST',
data: {
name: $('#control div span').eq(0).html(),
oldpass: $oldpass,
newpass: $newpass
}
}).done(function (data, textStatus, jqXHR) {
if (data.value === 'Y') {
$("#setOne p").eq(0).html(data.msg);
setTimeout(function () {
clearCookie();
$("#setOne p").eq(0).html('');
window.location.reload();
}, 1000);
} else if (data.value === 'N') {
$("#setOne p").eq(0).html(data.msg);
$("#setOne #oldpass").val('');
$("#setOne #newpass").val('');
}
});
}
},
"Cancel": function () {
$(this).dialog('close');
}
}
});
$("#setContent li").eq(0).click(function (e) {
e.stopPropagation();
$("#setOne").dialog("open");
});
$("#setContent li").eq(1).click(function (e) {
e.stopPropagation();
$("#setTwo").dialog("open");
});
$("#setContent li").eq(2).click(function (e) {
e.stopPropagation();
$("#setThree").dialog("open");
}); /*
*成员信息面板控制:包括所有成员和具体房间成员的状态
*/
$("#rightSide #Roommembers ul").hide();
$("#Allmembers div").css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" });
$("#Allmembers div i").addClass("glyphicon glyphicon-triangle-bottom");
$("#Roommembers div i").addClass("glyphicon glyphicon-triangle-right");
$("#rightSide div >div").click(function (e) {
var $title = $(this), $anotherTitle = $(this).parent().siblings("div");
if ($title.next('ul').is(":visible")) {
$title.siblings('ul').hide();
$title.children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right");
$title.css({ 'backgroundColor': "", "color": "" });
} else {
$anotherTitle.children('ul').hide();
$anotherTitle.children('div').css({ 'backgroundColor': "", "color": "" });
$anotherTitle.children("div").children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right");
$title.css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" });
$title.siblings('ul').slideToggle(500).show();
$title.children("i").removeClass("glyphicon glyphicon-triangle-right").addClass("glyphicon glyphicon-triangle-bottom");
}
}); /*函数集*/ /*
*保证scroll始终在最底端
*/
function scroll() {
$('#messages,#oldMsg ul').animate({
scrollTop: 999999999
}, 0);
} /*
*删除cookie
*/
function clearCookie() {
var keys = document.cookie.match(/[^=;]+(?=\=)/g);
if (keys) {
var i = keys.length;
while (i--) {
document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();
}
}
} /*
*获取cookie
*/
function getCookie(sname) {
var aCoookie = document.cookie.split(";");
for (var i = 0; i < aCoookie.length; i++) {
var aCrumb = aCoookie[i].split("=");
if (sname == aCrumb[0])
return decodeURIComponent(aCrumb[1]);
}
return null;
} /*
*界面render
*/
function render() {
if (document.cookie) {
$('.container').addClass('hidden');
$('#main').removeClass('hidden');
var uname = getCookie("userInfo");
CookieObj = JSON.parse(uname.substr(2));
socket.emit('join', $("#headmessages strong").html(), CookieObj.name);
$('.headImg').eq(0).attr('src', CookieObj.h_imgPath);
$('#control div span').eq(0).html(CookieObj.name);
$('#control div em').eq(0).html(CookieObj.personalizedSign);
};
}
$('.emotion').qqFace({
id: 'facebox',
assign: 'msg',
path: 'img/' //表情存放的路径
});
function replace_em(str) {
str = str.replace(/\</g, '&lt;');
str = str.replace(/\>/g, '&gt;');
str = str.replace(/\n/g, '<br/>');
str = str.replace(/\[em_([0-9]*)\]/g, '<img src="img/$1.gif" border="0" />');
return str;
}
/*查询聊天记录*/
$("#chatRecord").on('click', function () {
$.ajax({
url: "/queryChatMsg",
type: "POST",
data: {
roomName: $("#headmessages strong").html()
}
}).done(function (data) {
var Msg = data.msg;
var msgStr = '',
$name = $('#control div span').eq(0).html();
for (var i = 0; i < Msg.length; i++) {
var item = Msg[i];
if (item.name == $name) {
msgStr += '<li><span class="blue">' + item.name + '</span>&nbsp;&nbsp;<em class="blue">' + item.saytime + '</em><br/><span>' + item.msg + '</span></li>';
} else {
msgStr += '<li><span class="green">' + item.name + '</span>&nbsp;&nbsp;<em class="green">' + item.saytime + '</em><br/><span>' + item.msg + '</span></li>';
}
}
$("#oldMsg ul").empty();
$("#oldMsg ul").css({ "background-img": 'url("/img/loading.gif")' });
$("#rightSide").hide();
$("#oldMsg").show();
setTimeout(function () {
$("#oldMsg ul").append(msgStr);
scroll();
}, 2000);
});
}); /*关闭历史记录窗口*/
$("#oldMsgHead i").on('click', function () {
$("#oldMsg ul").empty();
$("#oldMsg").hide();
$("#rightSide").show();
});
/*清空聊天历史消息*/
$("#clearoldMsg i").on('click', function () {
var result = confirm("This action will delete the chat record on the database. Do you want to continue?");
if (result) {
$.ajax({
url: '/deleteMsg',
type: 'POST',
data: {
roomName: $("#headmessages strong").html()
}
}).done(function (data) {
if (data.value === 'Y') {
alert(data.msg);
$("#oldMsg ul").empty();
$("#oldMsg").hide();
$("#rightSide").show();
}
});
}
});
//多行文本输入框自动聚焦
$("#msg").focus();
//获取当前城市以及城市天气
function findWeather() {
var cityUrl = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js';
$.getScript(cityUrl, function (script, textStatus, jqXHR) {
var citytq = remote_ip_info.city;// 获取城市
var url = "http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&city=" + citytq + "&day=0&dfc=3";
$.ajax({
url: url,
dataType: "script",
scriptCharset: "gbk",
success: function (data) {
var _w = window.SWther.w[citytq][0];
var _f = _w.f1 + "_0.png";
if (new Date().getHours() > 17) {
_f = _w.f2 + "_1.png";
}
var img = "<img width='25px' height='25px' src='http://i2.sinaimg.cn/dy/main/weather/weatherplugin/wthIco/20_20/" + _f
+ "' />";
// var tq = citytq + " " + img + " " + _w.s1 + " " + _w.t1 + "℃~" + _w.t2 + "℃ " + _w.d1 + _w.p1 + "级";
var tq = img + _w.s1 + ' ' + citytq + "<br/><span>&nbsp" + _w.t2 + "℃~" + (_w.t1 || 25) + "℃ " + "</span>";
$('#weather').html(tq);
}
});
});
} findWeather();
});

主界面如下图所示即聊天分为三个模块(左中右)即:左为功能模块,用户可以进行房间的选择,以及点击自己的图像修改个人资料等操作;中为聊天模块,显示当前房间聊天内容以及聊天信息输入框;右为信息展示模块,默认显示所有在线用户信息以及当前房间在线成员信息,用户可以切换查看当前房间历史聊天记录。

(2)数据库

涉及到的技术:mongoDB、mongoose

由于javascript是一门弱类型语言,所以操作数据库没有java、php等语言方便。但是我们可以通过mongoose建立模型model映射到数据库中去,将对数据库的操作转换到操作model中去。

 var mongoose = require("mongoose");
var msgRecord=new mongoose.Schema({
name:{
type:String,
index:true,
},
roomName:{
type:String
},
msg:{
type:String,
},
saytime:{
type:String,
}
});
var UserSchema = new mongoose.Schema({
name: {
type: String,
unique: true,
index: true
},
password:{
type: String,
index: true
},
user_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
updated: {
type: Date, default: Date.now
},
status: {
type: Boolean,
default: false
},
h_imgPath: {
type: String,
default:"/img/1.jpg"
},
personalizedSign:{
type:String,
default:"Write something will well`"
}
}); var User = mongoose.model('User', UserSchema);
var Msg=mongoose.model('Msg',msgRecord);
module.exports = {
User:User,
Msg:Msg
};

(3)后台

涉及到的技术:Node.js,socket.io,Express

后台作为前端和数据库的桥梁,接收前端传过来的参数,去请求服务器,响应不同的服务请求。同时,通过socket.io进行实时通信,实时通信的前提是在客户端也要引入相关的js文件,通过on()和emit()方法、自定义事件达到目的

操作socket.io

 var users = {};
var QueryUser = require('./mongoDB/models/model').User;
var Msg = require('./mongoDB/models/model').Msg;
//获取实时时间
function gettime() {
var time = new Date();
var timepartone = time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate() + ' ';
var timemid = time.getHours(), s;
if (timemid < 6) {
s = "凌晨 " + timemid;
} else if (timemid < 12) {
s = "上午 " + timemid;
} else if (timemid < 18) {
s = "下午 " + '0' + (timemid - 12);
} else {
s = "晚上 " + (timemid - 12);
}
var timeparttwo = s + ":" + (time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes());
return timepartone + timeparttwo;
}
/*创建三个房间:Square、The Legend of Qin、Naruto*/
var rooms = { 'Square': [], 'The Legend of Qin': [], 'Naruto': [] };
var user = '';
module.exports = function (app, io) {
io.on('connection', function (socket) {
socket.on('join', function (roomName, userName) {
user = userName;
users[socket.id] = userName;
for (var i in rooms) {
if (roomName != i) {
var index = rooms[i].indexOf(user);
if (index !== -1) {
console.log("删除前" + rooms[i]);
rooms[i].splice(index, 1);
io.to(i).emit('sysLeft', user + "退出了房间" + roomName);
socket.leave(i);
console.log(userName + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
}
}
}
var flag = true;
for (var j = 0; j < rooms[roomName].length; j++) {
if (rooms[roomName][j] == user) {
flag = false;
}
}
if (flag) {
rooms[roomName].push(user);
socket.join(roomName);
}
io.sockets.in(roomName).emit('sysJoin', user + '加入了房间' + roomName);
total();
console.log(user + '加入了' + roomName);
});
socket.on('chat message', function (msg, img, roomOf) {
var name = '';
name = users[socket.id];
var newMsg = new Msg({ name: name, msg: msg, saytime: gettime(),roomName:roomOf });
newMsg.save();
if (rooms[roomOf].indexOf(name) === -1) {
return false;
}
console.log(roomOf + ":" + msg);
io.sockets.in(roomOf).emit('chat message', name, msg, img);
});
socket.on('disconnect', function () {
var msg = '', name = '', time = '';
time = gettime();;
name = users[socket.id];
for (var i in rooms) {
var index = rooms[i].indexOf(name);
if (index !== -1) {
console.log("删除前" + rooms[i]);
rooms[i].splice(index, 1);
io.to(i).emit('sysLeft', name + "退出了房间" + i);
socket.leave(i);
console.log(name + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
}
}
msg = name + '离开群聊 ' + time;
io.emit('disconnect', name, msg);
var timeTotal = total();
});
//获取总用户
function total() {
QueryUser.find({}, function (err, doc) {
io.emit('allUser', doc);
});
QueryUser.find({ status: false }, function (err, doc) {
io.emit('outlineUser', doc);
});
QueryUser.find({ status: true }, function (err, doc) {
io.emit('onlineUser', doc);
});
//查询房间里成员的信息
/*三个房间:Square、The Legend of Qin、Naruto*/
var F_RMInfo = [], S_RMInfo = [], T_RMInfo = [];
for (var k = 0; k < rooms["Square"].length; k++) {
QueryUser.findOne({ name: rooms["Square"][k] }, function (err, doc) {
F_RMInfo.push(doc);
io.sockets.in("Square").emit('SquareRoom', F_RMInfo);
console.log(F_RMInfo);
});
}
for (var i = 0; i < rooms["The Legend of Qin"].length; i++) {
QueryUser.findOne({ name: rooms["The Legend of Qin"][i] }, function (err, doc) {
S_RMInfo.push(doc);
io.sockets.in("The Legend of Qin").emit('QinRoom', S_RMInfo);
console.log(S_RMInfo);
});
}
for (var j = 0; j < rooms["Naruto"].length; j++) {
QueryUser.findOne({ name: rooms["Naruto"][j] }, function (err, doc) {
T_RMInfo.push(doc);
io.sockets.in("Naruto").emit('NarutoRoom', T_RMInfo);
console.log(T_RMInfo);
});
}
}
}); };

服务器js

 var express = require('express'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
http = require('http'),
path = require('path'),
io = require('socket.io'),
mongoose = require('mongoose'),
app = express(),
db,
userRoutes,
socketIO; /* 数据库连接 */
mongoose.connect('mongodb://localhost:27017/chatroom');
db = mongoose.connection;
db.on('error', console.error.bind(console, '数据库连接失败!'));
db.once('open', function callback() {
console.log('数据库连接成功!');
}); /*Express 配置*/
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public'))); http=http.createServer(app,function(req,res){
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
});
io = io(http); indexRoutes = require('./routes/index')(app);
userRoutes = require('./routes/users')(app); /*绑定io到服务器上*/
socketIO = require('./socketIO')(app, io); http.listen(3000, function () {
console.log('listening on *:3000');
});

《基于Node.js实现简易聊天室系列之详细设计》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. C#基础知识-基本的流程控制语句(三)

    所谓的流程控制就是在程序运行中控制程序的走向,可以通过各种的条件判断执行代码的顺序,有if... if...else.. else...if |switch case...|while... Do.. ...

  2. 关于vector push_back()与其他方式读取数据的效率对比

    引言: 在读取大量数据(数组)时,使用vector会尽量保证不会炸空间(MLE),但是相比于scanf的读取方式会慢上不少.但到底效率相差有多大,我们将通过对比测试得到结果. 测试数据:利用srand ...

  3. ZooKeeper实践:(1)配置管理

    一. 前言     配置是每个程序不可或缺的一部分,配置有多重方式:xml.ini.property.database等等,从最初的单机环境到现在的分布式环境. 1. 以文件的格式存储配置,修改任何都 ...

  4. .Net程序员学用Oracle系列(27):PLSQL 之游标、异常和事务

    1.游标 1.1.游标属性 1.2.隐式游标 1.3.游标处理及案例 2.异常 2.1.异常类别 2.2.异常函数 2.3.异常处理及案例 3.事务 3.1.开始事务.结束事务 3.2.自治事务 3. ...

  5. Linux parent process and child process when 'sudo'

    如果在一般用户下如user,执行sudo命令,会产生两个进程. ps -ef | grep Container root 4305 643 0 16:37 pts/39 00:00:00 sudo . ...

  6. OpenStack(企业私有云)万里长征第二步——使用Fuel部署

    一.前言 最近一直在使用DevStack来安装OpenStack,注意一直二字,部署了一遍又一遍,操作系统怕是安装了不下上百次,有时是为了验证新的方案,有时是安装出错,还有时是运行过程中出错.总之是碰 ...

  7. 搭建本地 Registry - 每天5分钟玩转 Docker 容器技术(20)

    Docker Hub 虽然非常方便,但还是有些限制,比如: 需要 internet 连接,而且下载和上传速度慢. 上传到 Docker Hub 的镜像任何人都能够访问,虽然可以用私有 reposito ...

  8. C语言错题小本子

    int a; ; a = ! x< //a的值是多少 我的答案:0, 正确答案:1 错误原因:没有熟练掌握运算符的优先级 // 找出下面无效的C语言变量名 A. _a B. main C. pr ...

  9. 【 js 基础 】【 源码学习 】源码设计 (持续更新)

    学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...

  10. CSS小技巧-为内盒子添加margin-top时,会带着父盒子一起下来,如何解决?

    1.为父盒子添加一个padding 2.为父盒子添加一个border 3.为父盒子添加一个overflow:hidden