Node.js 切近实战(十一) 之实时通信

今天咱们主要看一下Socket.IO实时通信,先看一下界面。javascript

1java

2node

3mysql

4web

5sql

6数据库

7express

8json

9浏览器

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

.row

 .col-md-9

  .panel.panel-primary

   .panel-heading

    h3.panel-title(style='font-size:13px;') Chat Message

   .panel-body#div_msgbody(style='min-height:590px;max-height:590px;overflow:auto;max-width:750px;')

    #div_msg.panel-content(style='word-wrap:break-word;word-break: break-word;')

   .panel-footer

    #div_footer(style='height:36px;line-height:36px')    

     .row

      .col-md-8(style='color:#3f51b5;font-weight:bold') Chat History:

        input#chat_history

      .col-md-4.right-align-text 

       a#link_clear(href='javascript:void(0)') Clear

  .row

   .col-md-10

    input#txt_msg.form-control(type='text' style='height:40px;resize:none' maxlength=200 placeholder='Input message here.')

   .col-md-2.right-align-text

    button#btn_send.k-button.k-primary(type='button' style='height:40px;width:100%')

     span.glyphicon.glyphicon-send

     span(style='margin-left:5px') Send

 .col-md-3

  .panel.panel-primary

   .panel-heading

    h3.panel-title(style='font-size:13px;') Members

   .panel-body.panel-inner-height(style='overflow:auto;')

    #div_users.panel-content(style='word-break: break-word;')

   .panel-footer

    .left-margin-10 

     span.text-color#total Member Count:0

 

#chat_historyWindow(style='display:none')

  #chat_historyContent.panel-content(style='word-wrap:break-word;word-break: break-word;')

span#notify

 

 

block scripts

 script(type='text/javascript' src='/javascripts/local/other/chat.js')

wKioL1eDsL6xz1MqAABsQWR8_7I057.png 

这就是聊天界面,左边是聊天内容,右边是参加聊天的用户。要实现这个聊天,以前咱们在博客中提到了SingalR,能够用于ASP.NET,WinForm以及WPF。今天咱们要使用Node.js平台上的Socket.IO.js。首先要在项目中引用这个扩展包。

wKiom1eDscyQdWxlAACfiVxC6a8872.png

安装好以后,在Package.json中就会自动加入这个包,管理起来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

"dependencies": {

    "array-splice""^0.1.1",

    "body-parser""~1.8.4",

    "busboy""^0.2.12",

    "cassandra-driver""^3.0.0",

    "cookie-parser""~1.3.3",

    "debug""~2.0.0",

    "express""~4.9.8",

    "express-session""1.12.1",

    "gridfs-stream""^1.1.1",

    "jade""^1.11.0",

    "log4js""^0.6.29",

    "mongoose""~4.2.3",

    "morgan""^1.6.1",

    "request""^2.67.0",

    "serve-favicon""~2.1.3",

    "socket.io""^1.3.7",

    "string.prototype.endswith""^0.2.0",

    "string.prototype.startswith""^0.2.0"

  }

我在这里还使用的是老版本,哈哈,OK,老版本新版本都能用。咱们进入主题,在第一篇环境搭建中,我就说了咱们的启动入口是www文件。

wKioL1eDspryvKkZAAAOS6Gho1U804.png

在www文件中,咱们初始化了SocketIO的一些东西。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

var chatUserCount = 0;

var chatUsers = {};

var server = app.listen(app.get('port'), function() {

    debug('Express server listening on port ' + server.address().port);

});

 

var io = require('socket.io')(server);

 

io.on('connection'function (socket) {

    socket.on('joinchat'function (obj) {

        console.log('a user connected:'+obj.UserName);

        socket.name = obj.UserName;

        if (!chatUsers.hasOwnProperty(obj.UserName)) {

            chatUsers[obj.UserName] = obj;

            chatUserCount++;

        }

 

    io.emit('joinchat', { chatUsers: chatUsers ,chatUserCount: chatUserCount,joinedUser: obj});

    });

     

    socket.on('leftchat'function () {

            console.log('a user left');

            if (chatUsers.hasOwnProperty(socket.name)) {

                var obj = chatUsers[socket.name];

                delete chatUsers[socket.name];

                chatUserCount--;

         

                io.emit('leftchat', { chatUsers: chatUsers, chatUserCount: chatUserCount, leftUser: obj });

            }

        });

 

    socket.on('disconnect'function () {

         if (chatUsers.hasOwnProperty(socket.name)) {

            var obj = chatUsers[socket.name];

            delete chatUsers[socket.name];

            chatUserCount--;

 

            io.emit('leftchat', { chatUsers: chatUsers, chatUserCount: chatUserCount, leftUser: obj });

        }

    });

 

    socket.on('message'function (obj) {

        io.emit('message', obj);

    });

 

    socket.on('error'function(exception) {

    console.log('SOCKET ERROR');

    socket.destroy();

    });

});

在这里当客户端有用户进入聊天时,就会发射joinchat事件,后台就会触发joinchat事件。当客户端和服务端创建链接时,服务端就会发射广播joinchat,全部链接的客户端都会收到这个广播,悄无声息刷新界面。当客户端用户失去链接(关闭浏览器)时,就会自动发射disconnect事件,服务端就会触发disconnect事件,并将结果广播到各个客户端,客户端自动刷新页面。当用户unload该页面时,会触发leftchat。当客户端发信息时,就会触发message事件,将该用户的消息发送到其余人。这个聊天界面的过程就是这样,很简单。

接下来咱们来看一下客户端代码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

var popupNotification = $("#notify").kendoNotification({

    autoHideAfter: 2000,

    height: 60,

    stacking: "down"

}).data("kendoNotification");

 

var socket = io();

var loginUser = sessionStorage.getItem("LoginUser");

if (loginUser == null) {

  window.location.href = "/";

  return;

}

var userObj = eval("(" + loginUser + ")");

     

sessionStorage.removeItem('chatUser');

socket.emit("joinchat", userObj);

socket.on('joinchat'function (data) {

    if (!sessionStorage.getItem('chatUser')) {

        sessionStorage.setItem('chatUser', JSON.stringify({ "user": [] }));

    }

     

    var usersObj = JSON.parse(sessionStorage.getItem('chatUser'));

    if (usersObj.user.indexOf(data.joinedUser.UserID) == -1) {

        usersObj.user.push(data.joinedUser.UserID);

        sessionStorage.setItem('chatUser', JSON.stringify(usersObj));

        setchartdetail(data);

         

        if (data.joinedUser.UserID != userObj.UserID) {

            popupNotification.show('<span style="color:red">' + data.joinedUser.FullName + ' joined in.</span>''info');

        }

    }

});

 

socket.on('leftchat'function (data) {

    var usersObj = JSON.parse(sessionStorage.getItem('chatUser'));

    var index = usersObj.user.indexOf(data.leftUser.UserID);

     

    usersObj.user.splice(index, 1);

    sessionStorage.setItem('chatUser', JSON.stringify(usersObj));

    setchartdetail(data);

    popupNotification.show('<span style="color:red">' + data.leftUser.FullName + ' left.</span>''warning');

});

当用户进入这个页面时,咱们发射joinchat,并将当前登陆用户信息发送到服务端,服务端再将该用户信息以及计算好的用户总数广播到各个客户端。注意这里咱们为了提醒用户,用到了kendoNotification,效果以下,当有人进入或者离开时,会出现popup提示。

wKiom1eDuAvCnttIAABY1yPLRUA192.png

当用户离开时,如上,当用户进入时,以下

wKioL1eDuDbi3mmiAABxPpJdZlM722.png

OK,接下来咱们看一下最主要的部分,聊天。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

$("#btn_send").click(function () {

    sendmsg();

})

 

$("#txt_msg").keydown(function (e) {

    if (e.keyCode == 13) {

        sendmsg();

    }

});

 

function sendmsg() {

    var msg = $.trim($("#txt_msg").val());

    if (msg) {

        var msgObj = { user: userObj, msg: msg };

        socket.emit("message", msgObj);

    }

     

    $("#txt_msg").val("");

}

上面就是点击SEND按钮或者文本框回车发送消息的代码,后台接到message广播给客户端。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

socket.on('message'function (data) {

    var msgObj = data;

     

    var userAvatar = '/images/userlogin.png';

     

    if (msgObj.user.UserName == userObj.UserName) {

        $("#div_msg").append("<div class='row-margin'>" 

        "<img src='" + userAvatar + "' style='height:40px;width:40px;right:-660px;position:relative'/>" 

        "<span style='right:-530px;position:relative'>" + msgObj.user.FullName + "</span>" 

        "<div class='demo clearfix fr'>" 

        "<span class='triangle'></span>" 

        "<div class='article' style='word'>" + msgObj.msg 

        "</div></div></div>");

         

        db.transaction(function (tx) {

            tx.executeSql('INSERT INTO ChatRecords(userId,sendUserId,fullname,content) VALUES("' + userObj.UserID + '","' + msgObj.user.UserID + '","' + msgObj.user.FullName + '","' + msgObj.msg + '")');

        });

    }

    else {

        $("#div_msg").append("<div class='row-margin'>" 

        "<img src='" + userAvatar + "' style='height:40px;width:40px;position:relative'/>" 

        "<span style='left:10px;position:relative'>" + msgObj.user.FullName + "</span>" 

        "<div class='demo clearfix'>" 

        "<span class='triangle'></span>" 

        "<div class='article'>" + msgObj.msg 

        "</div></div></div>");

         

        db.transaction(function (tx) {

            tx.executeSql('INSERT INTO ChatRecords(userId,sendUserId,fullname,content) VALUES("' + userObj.UserID + '","' + msgObj.user.UserID + '","' + msgObj.user.FullName + '","' + msgObj.msg + '")');

        });

    }

     

    var objDiv = document.getElementById("div_msgbody");

    objDiv.scrollTop = objDiv.scrollHeight;

});

其实这里不过是一个拼message的过程,若是发送者是本人,则消息靠右显示,不然靠左显示。看看James和lilei的聊天。

wKioL1eDvFLChW-nAAEI2Pjo2wY690.png

wKioL1eDvISxYZWoAAEF5YOxGPs308.png

看到了吧,聊天聊的很Happy。OK,上面你们是否是看到了一段相似sql的代码,不错,就是将聊天信息存到本地WebSQL sqlite数据库

1

2

3

db.transaction(function (tx) {

            tx.executeSql('INSERT INTO ChatRecords(userId,sendUserId,fullname,content) VALUES("' + userObj.UserID + '","' + msgObj.user.UserID + '","' + msgObj.user.FullName + '","' + msgObj.msg + '")');

        });

在使用以前咱们须要首先链接数据库建立表。

1

2

3

4

var db = openDatabase('ChatHistory''2.0''chat records', 10 * 1024 * 1024);

db.transaction(function (tx) {

    tx.executeSql('CREATE TABLE IF NOT EXISTS ChatRecords (id INTEGER PRIMARY KEY AUTOINCREMENT,userId TEXT NOT NULL DEFAULT "",sendUserId TEXT NOT NULL DEFAULT "",fullname TEXT NOT NULL DEFAULT "",content TEXT NOT NULL DEFAULT "",indate DATETIME default CURRENT_TIMESTAMP)');

});

确实和sqlSever的语法有点像,咱们看一下存储到本地webSQL的聊天记录,google Chrome,按F12

wKiom1eDvhOAWuv8AAFYusu4uNo881.png

看到了吧,聊天记录已经被存储下来,因为我是一台机器,一个浏览器开两个tab页,因此这里的聊天记录就是两份,一个是发送人的,一个是接收人的。你们注意这里还有张表,sqlite_sequence,咱们的主键id定义为自增列,因此这张表存储的是咱们的自增列(id)的最大值。

wKioL1eDvwDz8K2YAABqyhD4c2k819.png

最大是54,和咱们表ChatRecords中的最大值相等。

 

结束语

 

免费学习更多精品课程,登陆乐搏学院官网http://h.learnbo.cn/

或关注咱们的官方微博微信,还有更多惊喜哦~

 

本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1825565

相关文章
相关标签/搜索