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

    [TOC] Disabled options TeX (Based on KaTeX); Emoji; Task lists; HTML tags decode; Flowchart and Sequ ...

  2. Play初识

    2015年11月21日,写下这篇<Play初识> Play是神马呢?不是Google Play,而是一个java的web框架,因为它抛弃了传统的servlet模式的做法,国内网络连接pla ...

  3. JAVA IO中的设计模式

    在java语言 I/O库的设计中,使用了两个结构模式,即装饰模式和适配器模式.       在任何一种计算机语言中,输入/输出都是一个很重要的部分.与一般的计算机语言相比,java将输入/输出的功能和 ...

  4. spring boot --- 初级体验

    Spring boot的介绍我就不多说了,网上可以自己看一下. 它的优点就是:快!适合小白,没有复杂的配置文件. 缺点也很明显:坑有些多, 文档略少,报错有时不知道该如何处理. 下面做个最简单的入门: ...

  5. 升讯威微信营销系统开发实践:(5) Github 源码:微信接口的 .NET 封装。

    微信开发系列教程,将以一个实际的微信平台项目为案例,深入浅出的讲解微信开发.应用各环节的实现方案和技术细节. 本系列教程的最终目标是完成一个功能完善并达到高可用性能指标的微信管理软件,所以除了与微信本 ...

  6. Linux C 程序的开发环境

    1.开发环境的构成 编辑器 vim,vi 编译器 gcc 调试器 gdb 函数库glibc 系统头文件glibc_header 2.gcc编译器 功能强大.性能优越的多平台编译器,gcc可以将c.c+ ...

  7. 【干货】Markdown编辑博文,公式图片轻松搞定

    # Markdown 使用操作手册 作者:白宁超 Blog:伏草唯存 Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」 ...

  8. Longest Palindromic Substring2015年6月20日

    Given a , and there exists one unique longest palindromic substring. 自己的解决方案; public class Solution ...

  9. 【WPF MaterialDesign 示例开源项目】 Work Time Manager

    转岗写了将近一年的 PHP 最近因为 工作太多太杂, 在汇报工作的时候经常会忘记自己做了些什么,本来想只是使用excel来记录,但是发现了excel的很多局限性,光是无法共享就郁闷死了,习惯了下班不带 ...

  10. 第 13 章 可扩展性设计之 MySQL Replication

    前言: MySQL Replication 是 MySQL 非常有特色的一个功能,他能够将一个 MySQL Server 的 Instance 中的数据完整的复制到另外一个 MySQL Server ...