2019独角兽企业重金招聘Python工程师标准>>>

今天我们主要看一下Socket.IO实时通讯,先看一下界面。

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

.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')

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

安装好之后,在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文件。

在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提示。

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

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的聊天。

看到了吧,聊天聊的很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

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

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

结束语

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

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

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

转载于:https://my.oschina.net/learnbo/blog/776482

Node.js 切近实战(十一) 之实时通讯相关推荐

  1. Node.js 切近实战(七) 之Excel在线(文件文件组)

    2019独角兽企业重金招聘Python工程师标准>>> 今天我们来看一下Excel在线部分的文件和文件组.首先我们来看一下页面,调一下胃口.俗话说无图无真相,先看图. 没错,还是Te ...

  2. Node.js 切近实战(八) 之Excel在线(文件权限)

    2019独角兽企业重金招聘Python工程师标准>>> 最近美国又他妈的皮痒了,在南海找事,还说什么中国必须接受南海仲裁结果,我去你大爷的,你以为你是谁啊.说实话只要我们要决一死战的 ...

  3. Node.js 切近实战(四) 之图书管理系统(图书查询)

    2019独角兽企业重金招聘Python工程师标准>>> 最近又当上了Master,负责带项目,有时候,遇到的问题我很郁闷.比如一个Story,需求中说的是将单个修改改为批量修改,举个 ...

  4. 使用Node.js+Socket.IO搭建WebSocket实时应用

    Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新.它有着广泛的应用场景,比如在线聊天室.在线客服系统.评论系统.WebIM等. 作 ...

  5. Koa与Node.js开发实战(1)——Koa安装搭建(视频演示)

    2019独角兽企业重金招聘Python工程师标准>>> 学习架构: 由于Koa2已经支持ES6及更高版本,包括支持async方法,所以请读者保证Node.js版本在7.6.0以上.如 ...

  6. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 --持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  7. 腾讯高级工程师带你完整体验Node.js开发实战

    前几天,跟我一朋友聊天,他现在是阿里的架构师,说:「他们根本不知道,现在的电商大促有多么依赖 Node.js.」 说真的,我倒并不意外.作为一个定位明确的高性能 Web 服务器,Node.js 目前非 ...

  8. Vue.js+Node.js开发实战:从入门到项目上线

    <Vue.js+Node.js开发实战:从入门到项目上线>以JavaScript语言为基础,以一个完整的网站开发过程为主线,介绍了一整套面向Web项目的开发技术,如使用Node.js搭建服 ...

  9. 《Node.js开发实战》代码下载、简介与前言

    请下载代码评估:https://pan.baidu.com/s/1qYC3cVa   (密码: bba3). 内容简介 本书以实战开发为原则,以Node.js原生知识和框架实战为主线,详细介绍Node ...

最新文章

  1. spring定时器(@Scheduled)
  2. 山西农信社计算机知识,山西人事考试网 山西农信社考试计算机知识高频考点(二)...
  3. Feedback about (Blockchain OR ML) AND (logistics)
  4. PCB设计检查表( 布局后检查一次 ; 布线完再检查一次 )
  5. 我的程序员偶像在哪里?
  6. 手工打造目标PE的步骤
  7. phpstudy运行时出现没有安装VC库
  8. 【To Debug】牛客网--华为机试在线训练3:明明的随机数
  9. java 工具类库 Apache Commons
  10. Jtable 表格按多列排序(支持中文汉字排序)
  11. java Math.random()
  12. 五种百度云盘下载速度慢解决方法
  13. 【FXCG】如何成功启动SWOT分析法
  14. 视频基础 以及 MP4 容器解封装
  15. 爬虫-3-requests和代理
  16. 红米手机4X获得Root权限的流程
  17. 罗格斯的计算机科学博士奖学金,罗格斯大学cs怎么样
  18. GIS空间分析 栅格数据分析2 成本距离分析
  19. 图像处理---HSV变换
  20. 联想0xc000007b蓝屏怎么修复

热门文章

  1. 专家称中国***袭美是炒作
  2. subst命令镜像虚拟磁盘指南(原创)
  3. Android应用中,去掉Activity标题栏以及状态栏
  4. Redis 单例、主从模式、sentinel 以及集群的配置方式及优缺点对比(转)
  5. php用get方式传json数据 变成null了
  6. linux设置永久别名
  7. USACO Section1.5 Superprime Rib 解题报告
  8. POJ 1654 Area 凸包面积
  9. 预编译指令与相关宏小结
  10. Android 拍照、从相册选择图片