一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个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. [UWP]了解模板化控件(9):UI指南

    1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...

  2. Activity设置全屏显示的两种方式及系统自带theme属性解析

    转载说明:原贴地址:http://blog.csdn.net/a_running_wolf/article/details/50480386 设置Activity隐藏标题栏.设置Activity全屏显 ...

  3. Thinkphp与CI的区别

    深入学习一门新技术的最好方法就是看官方文档. ThinkPHP5.0文档: http://www.kancloud.cn/manual/thinkphp5/118003 官方的说辞是: 主要特性 : ...

  4. 博弈论(Game Theory) - 02 - 前传之重复剔除严格劣战略的占优战略均衡

    博弈论(Game Theory) - 02 - 前传之重复剔除严格劣战略的占优战略均衡 开始 "重复剔除劣战略的严格占优战略均衡"(iterated dominance equil ...

  5. 【初识Python】

    一.Python的简介 1.什么是python? Python(发音:[ 'paiθ(ə)n; (US) 'paiθɔn ]),是一种面向对象的解释性的计算机程序设计语言,也是一种功能强大而完善的通用 ...

  6. maven(03)

    修改本地库路径 windows下maven默认路径应该是${user.home}/.m2/repository 修改方法:找到maven安装的根路径,里面有一个conf的文件夹,打开里面有一个sett ...

  7. 我对Stub和Mock的理解

    介绍 使用测试驱动开发大半年了,我还是对Stub和Mock的认识比较模糊,没有进行系统整理. 今天查阅了相关资料,觉得写得很不错,所以我试图在博文中对资料进行整理一下,再加上一些自己的观点. 本文是目 ...

  8. zepto全选按钮之全选会根据按钮是否被全部选中更改状态

    在做手机端二次开发购物车的时候,发现zepto全选,没找到,或者功能不是自己想要的 后来做好,分享给需要的人 //全选或多选处理      var CheckAll = $('#items_check ...

  9. ASP.NET MVC5路由系统机制详细讲解

    请求一个ASP.NET mvc的网站和以前的web form是有区别的,ASP.NET MVC框架内部给我们提供了路由机制,当IIS接受到一个请求时,会先看是否请求了一个静态资源(.html,css, ...

  10. 《高性能javascript》 --- in case of odd number of items(奇怪的条目的数量)

    不知道是做着故意放的还是什么原因.总之运行后就会出现问题(奇怪的条目的数量) function merge(left, right){ var result = []; while (left.len ...