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

相關文章
相關標籤/搜索