《基于Node.js实现简易聊天室系列之详细设计》

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

(1)前端部分

涉及到的技术:htmlcssbootstrapjqueryjquery UI

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

页面:

 

  1 
2
3
4
5
6 12

13
14
15
16


17

Or

18
19
20
21
22 28

29
30
31
32
33 135
136
137 138 139 140 141
View Code

 

js:

  1 $(function () {
  2   var CookieObj = {}, socket = io(), headInfo = "群聊  (";;
  3   window.onbeforeunload = function (e) {
  4     if (document.cookie) return false;
  5   }
  6   render();
  7   /*
  8   *登录
  9   */
 10   var onLogin = function (e) {
 11     var xhr;
 12     if (!$('#username').val() || !$('#userpassword').val()) return;
 13     xhr = $.ajax({
 14       url: '/login',
 15       type: 'POST',
 16       dataType: 'json',
 17       data: {
 18         name: $('#username').val(),
 19         password: $('#userpassword').val()
 20       }
 21     })
 22       .done(function (data, textStatus, jqXHR) {
 23         if (data.value === 'Y') {
 24           render();
 25         } else {
 26           $('#SignInErr').html(data.msg);
 27         }
 28       })
 29       .fail(function (jqXHR, textStatus, errorThrown) {
 30         $('#SignInErr').html('Error occured! Please try again.');
 31       });
 32   };
 33   /*
 34   *注册
 35   */
 36   var onSignup = function (e) {
 37     var xhr;
 38     if (!$('#upName').val() || !$('#upPassword').val()) return;
 39     xhr = $.ajax({
 40       url: '/signup',
 41       type: 'POST',
 42       dataType: 'json',
 43       data: {
 44         name: $('#upName').val(),
 45         password: $('#upPassword').val()
 46       }
 47     })
 48       .done(function (data, textStatus, jqXHR) {
 49         if (data.value === 'Y') {
 50           $('#SignUpErr').html(data.msg || 'Login now with these credentials.');
 51         } else {
 52           $('#SignUpErr').html(data.msg || 'Invalid username');
 53         }
 54       })
 55       .fail(function (jqXHR, textStatus, errorThrown) {
 56         $('#SignUpErr').html('Error occured! Please try again.');
 57       });
 58   };
 59 
 60   var onMsgSubmit = function () {
 61     var str = $("#msg").val();
 62     var sendMsg = replace_em(str);
 63     if (!sendMsg || sendMsg.length > 1261) {
 64       alert("err:内容为空或者内容长度超出限制!")
 65       $('#msg').val('');
 66       return;
 67     }
 68     var roomOf = $("#headmessages strong").html();
 69     socket.emit('chat message', sendMsg, CookieObj.h_imgPath, roomOf);
 70     $('#msg').val('');
 71     return false;
 72   };
 73 
 74   socket.on('sysJoin', function (msg) {
 75     var joinInfo = "";
 76     joinInfo = '
  • ' + msg + '
  • '; 77 $(joinInfo).appendTo($("#messages")).animate({ "opacity": 0.5 }, 2000, function () { 78 $(this).animate({ "opacity": 1 }, 1500, function () { 79 $(this).animate({ "opacity": 0.3 }, 1000); 80 }); 81 }); 82 scroll(); 83 }); 84 85 socket.on('chat message', function (name, msg, img) { 86 var str = ''; 87 if (name == CookieObj.name) { 88 str = '
  • ' + msg + '

    ' + '
  • '; 89 } else { 90 str = '
  • ' + msg + '

  • '; 91 } 92 $('#messages').append(str); 93 scroll(); 94 }); 95 96 /*房间选择*/ 97 //默认是进广场,从其他房间执行如下函数 98 $("#selectmenu li").eq(0).on("click", function (e) { 99 e.stopPropagation(); 100 $("#selectRoom").hide(); 101 $("#headmessages strong").html("Square"); 102 socket.emit('join', 'Square', $("#gloableName").html()); 103 $("#messages").empty(); 104 }); 105 $("#selectmenu li").eq(1).on("click", function (e) { 106 e.stopPropagation(); 107 $("#selectRoom").show(); 108 }); 109 //选择秦时明月或火影忍者房间 110 $("#selectRoom li").on("click", function () { 111 var roomName = $(this).children("span").html(); 112 var userName = $("#gloableName").html(); 113 $("#headmessages strong").html(roomName); 114 socket.emit('join', roomName, userName); 115 $("#messages").empty(); 116 }); 117 118 119 120 /* 121 *接收所有已注册用户的信息 122 */ 123 socket.on('onlineUser', function (online) { 124 var onlineStr = ''; 125 for (var i = 0; i < online.length; i++) { 126 var item = online[i]; 127 onlineStr += '
  • ' + item.name + '[Online]
  • '; 128 } 129 $("#AllOnline").empty(); 130 $("#oncount").html(online.length); 131 $("#AllOnline").append(onlineStr); 132 }); 133 socket.on('outlineUser', function (outline) { 134 var outlineStr = ''; 135 for (var i = 0; i < outline.length; i++) { 136 var item = outline[i]; 137 outlineStr += '
  • ' + item.name + '[Outline]
  • '; 138 } 139 $("#AllOutline").empty(); 140 $("#AllOutline").append(outlineStr); 141 }); 142 socket.on('allUser', function (doc) { 143 $('#allcount').html(doc.length); 144 }); 145 socket.on('disconnect', function (name, msg) { 146 var leftInfo = ""; 147 leftInfo = '
  • ' + msg + '
  • '; 148 $(leftInfo).appendTo($("#messages")).animate({ "opacity": 0.3 }, 2000, function () { 149 $(this).animate({ "opacity": 1 }, 1500, function () { 150 $(this).animate({ "opacity": 0.3 }, 1000); 151 }); 152 return this; 153 }); 154 scroll(); 155 }); 156 157 /*当前房间人员信息*/ 158 var Lastr, r1, r2, r3; 159 socket.on('SquareRoom', function (roomInfo) { 160 r1 = roomInfo; 161 UpdateRoom(); 162 }); 163 socket.on('QinRoom', function (roomInfo) { 164 r2 = roomInfo; 165 UpdateRoom(); 166 }); 167 socket.on('NarutoRoom', function (roomInfo) { 168 r3 = roomInfo; 169 UpdateRoom(); 170 }); 171 function UpdateRoom() { 172 var $Nowroom = $("#headmessages strong").html(), roomCount, roomStr = ''; 173 switch ($Nowroom) { 174 case "Square": Lastr = r1; break; 175 case "The Legend of Qin": Lastr = r2; break; 176 case "Naruto": Lastr = r3; break; 177 default: Lastr = r1; 178 } 179 roomCount = Lastr.length; 180 for (var i = 0; i < roomCount; i++) { 181 var item = Lastr[i]; 182 roomStr += '
  • ' + item.name + '[Online]
  • '; 183 } 184 $("#roomCount").html(roomCount); 185 $("#Roommembers ul").empty(); 186 $("#Roommembers ul").append(roomStr); 187 } 188 /* 189 *切换/退出账号 190 */ 191 $("#changeUser").on('click', function () { 192 var res = confirm("Are you sure you want to quit and switch to another account??"); 193 if (res) { 194 UL(); 195 } else { 196 $("#control").hide(); 197 $("#setContent").hide(); 198 $("#stateSelect").hide(); 199 } 200 }); 201 $("#layout").on('click', UL); 202 function UL() { 203 if (document.cookie) { 204 $('#loginDiv').addClass('hidden'); 205 $('#main').removeClass('hidden'); 206 var uname = getCookie("userInfo"); 207 CookieObj = JSON.parse(uname.substr(2)); 208 $.ajax({ 209 url: '/layout', 210 type: 'POST', 211 dataType: 'json', 212 data: { 213 name: CookieObj.name 214 } 215 }) 216 .done(function (data, textStatus, jqXHR) { 217 if (data.value === 'Y') { 218 clearCookie(); 219 window.location.reload(); 220 } 221 }); 222 }; 223 } 224 225 $('#signinForm #loginBtn').click(onLogin); 226 $('#signupForm #signupBtn').click(onSignup); 227 $('#chatMsgForm #send').click(onMsgSubmit); 228 229 $("#clear").on("click", function () { 230 $('#messages').empty(); 231 }); 232 233 /* 234 *监听滚动条事件 235 */ 236 $('#messages').get(0).onscroll = function () { 237 $("#messages .Liright").css("margin-right", 1); 238 } 239 240 /* 241 *屏蔽回车键 242 */ 243 $(document).keydown(function (event) { 244 switch (event.keyCode) { 245 case 13: return false; 246 } 247 }); 248 /* 249 *用户信息 250 */ 251 $(".headImg").eq(0).on('click', function (e) { 252 e.stopPropagation(); 253 if ($("#control").get(0).style.display == "none") { 254 $("#control").show(); 255 } else { 256 $("#control").hide(); 257 $("#setContent").hide(); 258 $("#stateSelect").hide(); 259 } 260 }); 261 262 /*更改资料*/ 263 $("#set").on("click", function () { 264 $("#setContent").show(); 265 $("#stateSelect").hide(); 266 }); 267 /*构造头像选择内容*/ 268 var imgStr = ''; 269 for (var i = 1; i <= 18; i++) { 270 imgStr += '
  • '; 271 if (i % 6 == 0) { 272 imgStr += "
    "; 273 } 274 } 275 $("#setThree #imgContent ul").eq(0).append(imgStr); 276 $("#setThree #imgContent li img").on("click", function (e) { 277 e.stopPropagation(); 278 var $index = $(this).attr("data-in"); 279 $("#setThree #imgContent img").removeClass("imgSelected"); 280 $("#setThree #imgContent img").eq(($index - 1)).addClass("imgSelected"); 281 }); 282 /*人物头像模态框*/ 283 $("#setThree").dialog({ 284 autoOpen: false, 285 title: "Changing Avatar", 286 modal: true, 287 width: 578, 288 resizable: false, 289 buttons: { 290 "Ok": function () { 291 var selectedImg = $(".imgSelected").attr("data-in"); 292 // alert(selectedImg); 293 $.ajax({ 294 url: "/updateImg", 295 type: "POST", 296 data: { 297 name: $('#control div span').eq(0).html(), 298 imgIndex: selectedImg 299 } 300 }).done(function (data) { 301 if (data.value === 'Y') { 302 $("#setThree").dialog("close"); 303 $('.headImg').eq(0).attr('src', '/img/' + selectedImg + '.jpg'); 304 $('#setContent').hide(); 305 $('#control').hide(); 306 // alert(data.msg); 307 } 308 }); 309 ; 310 } 311 } 312 }); 313 /*个性签名模态框*/ 314 $("#setTwo").dialog({ 315 autoOpen: false, 316 title: "Personalized signature setting", 317 modal: true, 318 resizable: false, 319 buttons: { 320 "OK": function () { 321 var $newSign = $("#setTwo input[type='text']").eq(0).val(); 322 if ($newSign != '') { 323 $.ajax({ 324 url: '/updateSign', 325 type: 'POST', 326 data: { 327 name: $('#control div span').eq(0).html(), 328 newSign: $newSign 329 } 330 }).done(function (data) { 331 if (data.value === 'Y') { 332 $("#setTwo p").eq(0).html(data.msg); 333 setTimeout(function () { 334 $('#control div em').eq(0).html($newSign); 335 $("#setTwo").dialog('close'); 336 $("#setTwo p").eq(0).html(''); 337 }, 1000); 338 } 339 }); 340 } 341 }, 342 "Cancel": function () { 343 $(this).dialog('close'); 344 } 345 } 346 }); 347 /*密码模态框*/ 348 $("#setOne").dialog({ 349 autoOpen: false, 350 title: "Changeing User password", 351 modal: true, 352 resizable: false, 353 buttons: { 354 "Ok": function () { 355 var $oldpass = $("#setOne #oldpass").val(), $newpass = $("#setOne #newpass").val(); 356 if ($oldpass != '' && $newpass != '') { 357 $.ajax({ 358 url: '/changepass', 359 type: 'POST', 360 data: { 361 name: $('#control div span').eq(0).html(), 362 oldpass: $oldpass, 363 newpass: $newpass 364 } 365 }).done(function (data, textStatus, jqXHR) { 366 if (data.value === 'Y') { 367 $("#setOne p").eq(0).html(data.msg); 368 setTimeout(function () { 369 clearCookie(); 370 $("#setOne p").eq(0).html(''); 371 window.location.reload(); 372 }, 1000); 373 } else if (data.value === 'N') { 374 $("#setOne p").eq(0).html(data.msg); 375 $("#setOne #oldpass").val(''); 376 $("#setOne #newpass").val(''); 377 } 378 }); 379 } 380 }, 381 "Cancel": function () { 382 $(this).dialog('close'); 383 } 384 } 385 }); 386 $("#setContent li").eq(0).click(function (e) { 387 e.stopPropagation(); 388 $("#setOne").dialog("open"); 389 }); 390 $("#setContent li").eq(1).click(function (e) { 391 e.stopPropagation(); 392 $("#setTwo").dialog("open"); 393 }); 394 $("#setContent li").eq(2).click(function (e) { 395 e.stopPropagation(); 396 $("#setThree").dialog("open"); 397 }); 398 399 /* 400 *成员信息面板控制:包括所有成员和具体房间成员的状态 401 */ 402 $("#rightSide #Roommembers ul").hide(); 403 $("#Allmembers div").css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" }); 404 $("#Allmembers div i").addClass("glyphicon glyphicon-triangle-bottom"); 405 $("#Roommembers div i").addClass("glyphicon glyphicon-triangle-right"); 406 $("#rightSide div >div").click(function (e) { 407 var $title = $(this), $anotherTitle = $(this).parent().siblings("div"); 408 if ($title.next('ul').is(":visible")) { 409 $title.siblings('ul').hide(); 410 $title.children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right"); 411 $title.css({ 'backgroundColor': "", "color": "" }); 412 } else { 413 $anotherTitle.children('ul').hide(); 414 $anotherTitle.children('div').css({ 'backgroundColor': "", "color": "" }); 415 $anotherTitle.children("div").children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right"); 416 $title.css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" }); 417 $title.siblings('ul').slideToggle(500).show(); 418 $title.children("i").removeClass("glyphicon glyphicon-triangle-right").addClass("glyphicon glyphicon-triangle-bottom"); 419 } 420 }); 421 422 /*函数集*/ 423 424 /* 425 *保证scroll始终在最底端 426 */ 427 function scroll() { 428 $('#messages,#oldMsg ul').animate({ 429 scrollTop: 999999999 430 }, 0); 431 } 432 433 /* 434 *删除cookie 435 */ 436 function clearCookie() { 437 var keys = document.cookie.match(/[^=;]+(?=\=)/g); 438 if (keys) { 439 var i = keys.length; 440 while (i--) { 441 document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString(); 442 } 443 } 444 } 445 446 /* 447 *获取cookie 448 */ 449 function getCookie(sname) { 450 var aCoookie = document.cookie.split(";"); 451 for (var i = 0; i < aCoookie.length; i++) { 452 var aCrumb = aCoookie[i].split("="); 453 if (sname == aCrumb[0]) 454 return decodeURIComponent(aCrumb[1]); 455 } 456 return null; 457 } 458 459 /* 460 *界面render 461 */ 462 function render() { 463 if (document.cookie) { 464 $('.container').addClass('hidden'); 465 $('#main').removeClass('hidden'); 466 var uname = getCookie("userInfo"); 467 CookieObj = JSON.parse(uname.substr(2)); 468 socket.emit('join', $("#headmessages strong").html(), CookieObj.name); 469 $('.headImg').eq(0).attr('src', CookieObj.h_imgPath); 470 $('#control div span').eq(0).html(CookieObj.name); 471 $('#control div em').eq(0).html(CookieObj.personalizedSign); 472 }; 473 } 474 $('.emotion').qqFace({ 475 id: 'facebox', 476 assign: 'msg', 477 path: 'img/' //表情存放的路径 478 }); 479 function replace_em(str) { 480 str = str.replace(/\); 481 str = str.replace(/\>/g, '>'); 482 str = str.replace(/\n/g, '
    '); 483 str = str.replace(/\[em_([0-9]*)\]/g, ''); 484 return str; 485 } 486 /*查询聊天记录*/ 487 $("#chatRecord").on('click', function () { 488 $.ajax({ 489 url: "/queryChatMsg", 490 type: "POST", 491 data: { 492 roomName: $("#headmessages strong").html() 493 } 494 }).done(function (data) { 495 var Msg = data.msg; 496 var msgStr = '', 497 $name = $('#control div span').eq(0).html(); 498 for (var i = 0; i < Msg.length; i++) { 499 var item = Msg[i]; 500 if (item.name == $name) { 501 msgStr += '
  • ' + item.name + '  ' + item.saytime + '
    ' + item.msg + '
  • '; 502 } else { 503 msgStr += '
  • ' + item.name + '  ' + item.saytime + '
    ' + item.msg + '
  • '; 504 } 505 } 506 $("#oldMsg ul").empty(); 507 $("#oldMsg ul").css({ "background-img": 'url("/img/loading.gif")' }); 508 $("#rightSide").hide(); 509 $("#oldMsg").show(); 510 setTimeout(function () { 511 $("#oldMsg ul").append(msgStr); 512 scroll(); 513 }, 2000); 514 }); 515 }); 516 517 /*关闭历史记录窗口*/ 518 $("#oldMsgHead i").on('click', function () { 519 $("#oldMsg ul").empty(); 520 $("#oldMsg").hide(); 521 $("#rightSide").show(); 522 }); 523 /*清空聊天历史消息*/ 524 $("#clearoldMsg i").on('click', function () { 525 var result = confirm("This action will delete the chat record on the database. Do you want to continue?"); 526 if (result) { 527 $.ajax({ 528 url: '/deleteMsg', 529 type: 'POST', 530 data: { 531 roomName: $("#headmessages strong").html() 532 } 533 }).done(function (data) { 534 if (data.value === 'Y') { 535 alert(data.msg); 536 $("#oldMsg ul").empty(); 537 $("#oldMsg").hide(); 538 $("#rightSide").show(); 539 } 540 }); 541 } 542 }); 543 //多行文本输入框自动聚焦 544 $("#msg").focus(); 545 //获取当前城市以及城市天气 546 function findWeather() { 547 var cityUrl = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js'; 548 $.getScript(cityUrl, function (script, textStatus, jqXHR) { 549 var citytq = remote_ip_info.city;// 获取城市 550 var url = "http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&city=" + citytq + "&day=0&dfc=3"; 551 $.ajax({ 552 url: url, 553 dataType: "script", 554 scriptCharset: "gbk", 555 success: function (data) { 556 var _w = window.SWther.w[citytq][0]; 557 var _f = _w.f1 + "_0.png"; 558 if (new Date().getHours() > 17) { 559 _f = _w.f2 + "_1.png"; 560 } 561 var img = ""; 563 // var tq = citytq + " " + img + " " + _w.s1 + " " + _w.t1 + "℃~" + _w.t2 + "℃ " + _w.d1 + _w.p1 + "级"; 564 var tq = img + _w.s1 + ' ' + citytq + "
     " + _w.t2 + "℃~" + (_w.t1 || 25) + "℃ " + ""; 565 $('#weather').html(tq); 566 } 567 }); 568 }); 569 } 570 571 findWeather(); 572 });
    View Code

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

     《基于Node.js实现简易聊天室系列之详细设计》_第1张图片

    (2)数据库

    涉及到的技术:mongoDB、mongoose 

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

     

     1 var mongoose = require("mongoose");
     2 var msgRecord=new mongoose.Schema({
     3     name:{
     4      type:String,
     5      index:true,
     6     },
     7     roomName:{
     8       type:String
     9     },
    10     msg:{
    11       type:String,
    12     },
    13     saytime:{
    14       type:String,
    15     }     
    16 });
    17 var UserSchema = new mongoose.Schema({
    18   name: {
    19     type: String,
    20     unique: true,
    21     index: true
    22   },
    23   password:{
    24     type: String,
    25     index: true
    26   },
    27   user_id: {
    28     type: mongoose.Schema.Types.ObjectId,
    29     index: true
    30   },
    31   updated: {
    32     type: Date, default: Date.now
    33   },
    34   status: {
    35     type: Boolean,
    36     default: false
    37   },
    38   h_imgPath: {
    39     type: String,
    40     default:"/img/1.jpg"
    41   },
    42   personalizedSign:{
    43     type:String,
    44     default:"Write something will well`"
    45   }
    46 });
    47 
    48 var User = mongoose.model('User', UserSchema);
    49 var Msg=mongoose.model('Msg',msgRecord);
    50 module.exports = {
    51   User:User,
    52   Msg:Msg
    53 };
    View Code

    (3)后台

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

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

    操作socket.io

      1 var users = {};
      2 var QueryUser = require('./mongoDB/models/model').User;
      3 var Msg = require('./mongoDB/models/model').Msg;
      4 //获取实时时间
      5 function gettime() {
      6   var time = new Date();
      7   var timepartone = time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate() + ' ';
      8   var timemid = time.getHours(), s;
      9   if (timemid < 6) {
     10     s = "凌晨 " + timemid;
     11   } else if (timemid < 12) {
     12     s = "上午 " + timemid;
     13   } else if (timemid < 18) {
     14     s = "下午 " + '0' + (timemid - 12);
     15   } else {
     16     s = "晚上 " + (timemid - 12);
     17   }
     18   var timeparttwo = s + ":" + (time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes());
     19   return timepartone + timeparttwo;
     20 }
     21 /*创建三个房间:Square、The Legend of Qin、Naruto*/
     22 var rooms = { 'Square': [], 'The Legend of Qin': [], 'Naruto': [] };
     23 var user = '';
     24 module.exports = function (app, io) {
     25   io.on('connection', function (socket) {
     26     socket.on('join', function (roomName, userName) {
     27       user = userName;
     28       users[socket.id] = userName;
     29       for (var i in rooms) {
     30         if (roomName != i) {
     31           var index = rooms[i].indexOf(user);
     32           if (index !== -1) {
     33             console.log("删除前" + rooms[i]);
     34             rooms[i].splice(index, 1);
     35             io.to(i).emit('sysLeft', user + "退出了房间" + roomName);
     36             socket.leave(i);
     37             console.log(userName + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
     38           }
     39         }
     40       }
     41       var flag = true;
     42       for (var j = 0; j < rooms[roomName].length; j++) {
     43         if (rooms[roomName][j] == user) {
     44           flag = false;
     45         }
     46       }
     47       if (flag) {
     48         rooms[roomName].push(user);
     49         socket.join(roomName);
     50       }
     51       io.sockets.in(roomName).emit('sysJoin', user + '加入了房间' + roomName);
     52       total();
     53       console.log(user + '加入了' + roomName);
     54     });
     55     socket.on('chat message', function (msg, img, roomOf) {
     56       var name = '';
     57       name = users[socket.id];
     58       var newMsg = new Msg({ name: name, msg: msg, saytime: gettime(),roomName:roomOf });
     59       newMsg.save();
     60       if (rooms[roomOf].indexOf(name) === -1) {
     61         return false;
     62       }
     63       console.log(roomOf + ":" + msg);
     64       io.sockets.in(roomOf).emit('chat message', name, msg, img);
     65     });
     66     socket.on('disconnect', function () {
     67       var msg = '', name = '', time = '';
     68       time = gettime();;
     69       name = users[socket.id];
     70       for (var i in rooms) {
     71           var index = rooms[i].indexOf(name);
     72           if (index !== -1) {
     73             console.log("删除前" + rooms[i]);
     74             rooms[i].splice(index, 1);
     75             io.to(i).emit('sysLeft', name + "退出了房间" + i);
     76             socket.leave(i);
     77             console.log(name + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
     78         }
     79       }
     80       msg = name + '离开群聊  ' + time;
     81       io.emit('disconnect', name, msg);
     82       var timeTotal = total();
     83     });
     84     //获取总用户
     85     function total() {
     86       QueryUser.find({}, function (err, doc) {
     87         io.emit('allUser', doc);
     88       });
     89       QueryUser.find({ status: false }, function (err, doc) {
     90         io.emit('outlineUser', doc);
     91       });
     92       QueryUser.find({ status: true }, function (err, doc) {
     93         io.emit('onlineUser', doc);
     94       });
     95       //查询房间里成员的信息
     96       /*三个房间:Square、The Legend of Qin、Naruto*/
     97       var F_RMInfo = [], S_RMInfo = [], T_RMInfo = [];
     98       for (var k = 0; k < rooms["Square"].length; k++) {
     99         QueryUser.findOne({ name: rooms["Square"][k] }, function (err, doc) {
    100           F_RMInfo.push(doc);
    101           io.sockets.in("Square").emit('SquareRoom', F_RMInfo);
    102           console.log(F_RMInfo);
    103         });
    104       }
    105       for (var i = 0; i < rooms["The Legend of Qin"].length; i++) {
    106         QueryUser.findOne({ name: rooms["The Legend of Qin"][i] }, function (err, doc) {
    107           S_RMInfo.push(doc);
    108           io.sockets.in("The Legend of Qin").emit('QinRoom', S_RMInfo);
    109           console.log(S_RMInfo);
    110         });
    111       }
    112       for (var j = 0; j < rooms["Naruto"].length; j++) {
    113         QueryUser.findOne({ name: rooms["Naruto"][j] }, function (err, doc) {
    114           T_RMInfo.push(doc);
    115           io.sockets.in("Naruto").emit('NarutoRoom', T_RMInfo);
    116           console.log(T_RMInfo);
    117         });
    118       }
    119     }
    120   });
    121 
    122 };
    View Code

    服务器js

     1 var express = require('express'),
     2   cookieParser = require('cookie-parser'),
     3   bodyParser = require('body-parser'),
     4   http = require('http'),
     5   path = require('path'),
     6   io = require('socket.io'),
     7   mongoose = require('mongoose'),
     8   app = express(),
     9   db,
    10   userRoutes,
    11   socketIO;
    12 
    13 /* 数据库连接 */
    14 mongoose.connect('mongodb://localhost:27017/chatroom');
    15 db = mongoose.connection;
    16 db.on('error', console.error.bind(console, '数据库连接失败!'));
    17 db.once('open', function callback() {
    18   console.log('数据库连接成功!');
    19 });
    20 
    21 /*Express 配置*/
    22 app.use(cookieParser());
    23 app.use(bodyParser.json()); 
    24 app.use(bodyParser.urlencoded({ extended: true }));
    25 app.use(express.static(path.join(__dirname, 'public')));
    26 
    27 
    28 http=http.createServer(app,function(req,res){
    29   res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
    30 });
    31 io = io(http);
    32 
    33 indexRoutes = require('./routes/index')(app);
    34 userRoutes = require('./routes/users')(app);
    35 
    36 /*绑定io到服务器上*/
    37 socketIO = require('./socketIO')(app, io);
    38 
    39 http.listen(3000, function () {
    40   console.log('listening on *:3000');
    41 });
    View Code

     

    转载于:https://www.cnblogs.com/jiangcheng-langzi/p/7074441.html

    你可能感兴趣的:(《基于Node.js实现简易聊天室系列之详细设计》)