一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个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. DTCMS插件的制作实例电子资源管理(三)前台模板页编写

    总目录 插件目录结构(一) Admin后台页面编写(二) 前台模板页编写(三) URL重写(四) 本实例旨在以一个实际的项目中的例子来介绍如何在dtcms中制作插件,本系列文章非入门教程,部分逻辑实现 ...

  2. PHP获得上(两)周时间

    就不手写了

  3. hdu4614 Vases and Flowers 线段树+二分

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4614 题意: 给你N个花瓶,编号是0  到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花. ...

  4. 第一篇:使用Spark探索经典数据集MovieLens

    前言 MovieLens数据集包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息. 这个数据集经常用来做推荐系统,机器学习算法的测试数据集.尤其在推荐系统领域,很多著名论文都是基于这 ...

  5. struts2.5能不能再恶心点

    Caused by: java.lang.IllegalArgumentException: unknown reserved key '_typeConverter' at ognl.OgnlCon ...

  6. SQLServer 延迟事务持久性

    SQL Server 2014新功能 -- 延迟事务持久性(Delayed Transaction Durability) SQL Server事务提交默认是完全持久性的(Full Durable), ...

  7. 求一个二维整数数组最大子数组之和,时间复杂度为N^2

    本随笔只由于时间原因,我就只写写思想了 二维数组最大子数组之和,可以  引用  一维最大子数组之和 的思想一维最大子数组之和 的思想,在本博客上有,这里就不做多的介绍了 我们有一个最初的二维数组a[n ...

  8. MyBatis 3 User Guide Simplified Chinese.pdf

    MyBatis 3 用户指南 帮助我们把文档做得更好… 如果你发现了本文档的遗漏之处,或者丢失 MyBatis 特性的说明时,那么最好的方法就 是了解一下这个遗漏之处然后把它记录下来. 我们在 wik ...

  9. poi jsp xls

    poi jsp xls <%@ page language="java" pageEncoding="UTF-8"import="java.ut ...

  10. 20个php框架

    对于Web开发者来说,PHP是一款非常强大而又受欢迎的编程语言.世界上很多顶级的网站都是基于PHP开发的.本文我们来回顾一下20个2014年最优秀的PHP框架. 每一个开发者都知道,拥有一个强大的框架 ...