【原创】node+express+socket搭建一个实时推送应用

技术背景

Web领域的实时推送技术,也被称作Realtime技术。这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。

应用场景:

  • 监控系统:后台硬件热插拔、LED、温度、电压发生变化
  • 即时通信系统:其它用户登录、发送信息
  • 即时报价系统:后台数据库内容发生变化

技术实现方案:ajax long polling(ajax长轮询),comet(http长连接)、socket

这里有篇文章介绍了这几种技术,可以看一下。

http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

http://jingyan.baidu.com/article/08b6a591e07ecc14a80922f1.html

websocket简介

HTTP是一种基于消息(message)的请求(request )/应答(response)协议。当我们在网页中点击一条链接(或者提交一个表单)的时候,浏览器给服务器发一个request message,然后服务器算啊算,答复一条response message。主动发起TCP连接的是client,接受TCP连接的是server。HTTP消息只有两种:request和response。client只能发送request message,server只能发送response message。一问一答,因此按HTTP协议本身的设计,服务器不能主动的把消息推给客户端。

因此,如果让服务器端也可以主动发送信息到客户端,就可以很大程度改进这些不足。WebSocket就是一个实现这种双向通信的新协议。

WebSocket是基于HTTP的功能追加协议

WebSocket最初由html5提出,但现在已经发展为一个独立的协议标准。WebSocket可以分为协议( Protocol )和 API 两部分,分别由 IETF 和W3C制定了标准。

先来看看WebSocket协议的建立过程。

为了实现WebSocket通信,首先需要客户端发起一次普通HTTP请求(也就是说,WebSocket的建立是依赖HTTP的)。请求报文可能像这样:

GET ws://websocket.example.com/ HTTP/1.1
Host: websocket.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.com
Sec-WebSocket-Key:pAloKxsGSHtpIHrJdWLvzQ==
Sec-WebSocket-Version:13

其中HTTP头部字段 Upgrade: websocket 和 Connection: Upgrade 很重要,告诉服务器通信协议将发生改变,转为WebSocket协议。支持WebSocket的服务器端在确认以上请求后,应返回状态码为 101 Switching Protocols 的响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: nRu4KAPUPjjWYrnzxDVeqOxCvlM=

其中字段 Sec-WebSocket-Accept 是由服务器对前面客户端发送的 Sec-WebSocket-Key 进行确认和加密后的结果,相当于一次验证,以帮助客户端确信对方是真实可用的WebSocket服务器。

验证通过后,这个握手响应就确立了WebSocket连接,此后,服务器端就可以主动发信息给客户端了。此时的状态比较像服务器端和客户端接通了电话,无论是谁有什么信息想告诉对方,开口就好了。

一旦建立了WebSocket连接,此后的通信就不再使用HTTP了,改为使用WebSocket独立的数据帧

整个过程像这样:

开始码砖

1.建立项目文件,安装node 和express框架

socket.io http://socket.io/docs/

服务器端安装socket.io

$ npm install socket.io
客户端下载socket.io.js

client是客户端文件 server是服务器端文件。

2.写界面

我的界面是这样的

html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>聊天室</title>
    <link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
    <div class="main">
        <div class="main-top">
            socket.io demo
        </div>
        <div class="main-body">
            <section class="chatRoomInfo">
                <div class="info">当前共有<span class="chatNum">0</span>人在线。在线列表:&nbsp;<span class="chatList"></span></div>
            </section>
            <!--<section class="chatRoomTip">
                <div>子木加入到聊天室</div>
            </section>
            <section class="user clearfix">
                <span>子木</span>
                <div>
                    测试测试测试测试测试测试测试测试测试试测试测试测试测试测试测试测试测试测试测试测试
                </div>
            </section>
            <section class="server clearfix">
                <span>子木</span>
                <div>
                    测试测试测试
                </div>
            </section>-->
        </div>
        <div class="main-footer clearfix">
            <div class="input">
                <input type="text" name="msg" id="msg" value="" />
            </div>
            <button type="button" class="send">发送</button>
        </div>
    </div>
    <script src="js/jquery-2.1.0.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/socket.io.js" type="text/javascript" charset="utf-8"></script>
    <script>
       //do something</script>
</body>
</html>

css

* {
    margin: 0;
    padding: 0;
}

.clearfix {
    zoom: 1;
}

.clearfix:after {
    clear: both;
    content: ‘.‘;
    display: block;
    width: 0;
    height: 0;
    visibility: hidden;
}

.main {
    width: 100%;
    height: 100%;
    font-size: 14px;
}

.main-top {
    height: 30px;
    background-color: #3d3d3d;
    text-indent: 15px;
    color: #ffffff;
    font-size: 16px;
    line-height: 30px;
}

.main-body {
    background-color: #efeff4;
    position: absolute;
    top: 30px;
    bottom: 50px;
    width: 100%;
    overflow-y: scroll;
    scrollbar-3dlight-color: ;
}

.chatRoomInfo {
    padding: 10px;
    font-size: 12px;
    color: #666;
}

.chatRoomTip {
    text-align: center;
    padding: 10px;
    font-size: 12px;
    color: #444;
}

.user {
    width: 100%;
    min-height: 38px;
    min-width: 36px;
    margin-bottom: 15px;
}

.user span {
    float: right;
}

.user div {
    float: right;
    min-height: 38px;
    min-width: 38px;
    max-width: 70%;
    line-height: 38px;
    padding: 0 15px;
    color: #FFFFFF;
    margin-right: 10px;
    word-break: break-all;
    background-color: #007aff;
    position: relative;
    border-radius: 5px;
}

.user div:after {
    content: "";
    position: absolute;
    right: -5px;
    top: 4px;
    width: 0;
    height: 0;
    border-top: solid transparent;
    border-left: 7px solid #007aff;
    border-bottom: 4px solid transparent;
}

.server {
    width: 100%;
    min-height: 38px;
    min-width: 36px;
    margin-bottom: 15px;
}

.server span {
    float: left;
}

.server div {
    float: left;
    min-height: 38px;
    min-width: 38px;
    max-width: 70%;
    line-height: 38px;
    padding: 0 15px;
    color: #FFFFFF;
    margin-left: 10px;
    word-break: break-all;
    background-color: #007aff;
    position: relative;
    border-radius: 5px;
}

.server div:after {
    content: "";
    position: absolute;
    left: -5px;
    top: 4px;
    width: 0;
    height: 0;
    border-top: solid transparent;
    border-right: 7px solid #007aff;
    border-bottom: 4px solid transparent;
}
.main-footer{
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 50px;
}
.input{
    float: left;
    width: 80%;
    height: 40px;
    margin-top: 5px;
    margin-left: 1%;
    margin-right: 1%;
    border: 1px solid #666666;
}
.input input{
    width: 100%;
    height: 40px;
    outline: none;
    border: none;
    font-size: 14px;
    color: #333;
}
.send{
    float: left;
    width: 16%;
    height: 40px;
    margin-top: 5px;
    margin-left: 1%;
    border: none;
    background-color: #e8e8e8;
    color: #007aff;
    outline: none;
}

现在开始写逻辑

客户端代码实现

     /*按钮点击效果*/
        $(‘.send‘).mousedown(function(){
            $(this).css({‘background‘:"#007aff",‘color‘:"#ffffff"});
        })
        $(‘.send‘).mouseup(function(){
            $(this).css({‘background‘:"#e8e8e8",‘color‘:"#ffffff"});
        })
        /*socket*/
        window.onload=function () {
              var username=prompt(‘请输入您的姓名‘);
              if (!username){
                  alert(‘姓名必填‘);
                  history.go(0);
              }
//          username="子木";
            userId=genUid();
            var userInfo={
                ‘userid‘:userId,
                ‘username‘:username
            };
            //连接socket后端服务器
            var socket=io.connect("ws://127.0.0.1:4000");
            //通知用户有用户登录
            socket.emit(‘login‘,userInfo);
            //监听新用户登录
            socket.on(‘login‘,function (o) {
                updateMsg(o, ‘login‘);
               });
            //监听用户退出
            socket.on(‘logout‘,function (o) {
                updateMsg(o, ‘logout‘);
            });
            //发送消息
            socket.on(‘message‘,function (obj) {
                if(obj.userid==userId) {
                    var MsgHtml=‘<section class="user clearfix">‘
                                        +‘<span>‘+obj.username+‘</span>‘
                                        +‘<div>‘+obj.content+‘</div>‘
                                     +‘</section>‘;
                }else{
                    var MsgHtml=‘<section class="server clearfix">‘
                                         +‘<span>‘+obj.username+‘</span>‘
                                         +‘<div>‘+obj.content+‘</div>‘
                                      +‘</section>‘;
                }
                $(‘.main-body‘).append(MsgHtml);
                $(‘.main-body‘).scrollTop(99999);
            })
            $(‘.send‘).click(function () {
                var content=$(‘input[name="msg"]‘).val();
                if (content){
                    var obj={
                        ‘userid‘:userId,
                        ‘username‘:username,
                        ‘content‘:content
                    }
                    socket.emit(‘message‘,obj);
                    $(‘input[name="msg"]‘).val("");
                }
            })

        }

        /*用户id生成*/
        function  genUid() {
            return new Date().getTime()+""+Math.floor(Math.random()*899+100);
        }
        function logout(){
            socket.disconnect();
            location.reload();
        }
        /*监听函数*/
        function updateMsg(o,action) {
            //当前在线列表
            var onlineUser=o.onlineUser;
            //当前在线数
            var onlineCount=o.onlineCount;
            //新加用户
            var user=o.user;
            //更新在线人数
            var userList=‘‘;
            var separator = ‘‘;
            for(key in onlineUser){
                userList+=separator+onlineUser[key];
                separator = ‘、‘;
            }
            //跟新房间信息
            $(‘.chatNum‘).text(onlineCount);
            $(‘.chatList‘).text(userList);
            //系统消息
            if(action==‘login‘){
                var sysHtml=‘<section class="chatRoomTip"><div>‘+user.username+‘进入聊天室</div></section>‘;
            }
            if(action=="logout"){
                var sysHtml=‘<section class="chatRoomTip"><div>‘+user.username+‘退出聊天室</div></section>‘;
            }
            $(".main-body").append(sysHtml);
            $(‘.main-body‘).scrollTop(99999);
        }

服务器代码实现 app.js

var app = require(‘express‘)();
var http=require(‘http‘).Server(app);
var io=require(‘socket.io‘)(http);

app.get(‘/socket/client/index.html‘,function (req,res) {
    res.send(‘<h1>welcome</h1>‘);
})
//在线用户
var onlineUser={};
var onlineCount=0;

io.on(‘connection‘,function (socket) {
    console.log(‘新用户登录‘);

    //监听新用户加入
    socket.on(‘login‘,function (obj) {
        socket.name=obj.userid;
        //检查用户在线列表
        if(!onlineUser.hasOwnProperty(obj.userid)){
            onlineUser[obj.userid]=obj.username;
            //在线人数+1
            onlineCount++;
        }
        //广播消息
        io.emit(‘login‘,{onlineUser:onlineUser,onlineCount:onlineCount,user:obj});
        console.log(obj.username+"加入了聊天室");
    })

    //监听用户退出
    socket.on(‘disconnect‘,function () {
        //将退出用户在在线列表删除
        if(onlineUser.hasOwnProperty(socket.name)){
            //退出用户信息
            var obj={userid:socket.name, username:onlineUser[socket.name]};
            //删除
            delete onlineUser[socket.name];
            //在线人数-1
            onlineCount--;
            //广播消息
            io.emit(‘logout‘,{onlineUser:onlineUser,onlineCount:onlineCount,user:obj});
            console.log(obj.username+"退出了聊天室");
        }
    })

    //监听用户发布聊天内容
    socket.on(‘message‘, function(obj){
        //向所有客户端广播发布的消息
        io.emit(‘message‘, obj);
        console.log(obj.username+‘说:‘+obj.content);
    });
})
http.listen(4000, function(){
    console.log(‘listening on *:4000‘);
});

代码全部贴上来

源码地址:https://github.com/zimuqi/socketChat

下载后安装好socket.io  express后进入到server 目录下 直接node app.js。然后打开项目主页就可以看到效了。可以多打开几个窗口互动一下。

有兴趣的可以再去研究一下WebIM系统,实现类似微信,qq的功能,客户端可以看到好友在线状态,在线列表,添加好友,删除好友,新建群组等,消息的发送除了支持基本的文字外,还能支持表情、图片和文件。

时间: 2024-11-05 14:53:34

【原创】node+express+socket搭建一个实时推送应用的相关文章

Spring MVC 实现web Socket向前端实时推送数据

最近项目中用到了webSocket服务,由后台实时向所有的前端推送消息,前端暂时是不可以发消息给后端的,数据的来源是由具体的设备数据收集器收集起来,然后通过socket推送给后端,后端收到数据后,再将这些数据推送给前端. 听起来业务逻辑有点复杂.其实单独的实现socket或websocket都比较简单,但是二者之间的数据传输问题,困扰了我很久.也想过用redis做一个消息队列,将socket接收到的数据处理后丢进去,然后再用websocket从redis里取出数据,再推送给前端. 但是.问题来了

node+express+ejs搭建一个简单的&quot;页面&quot;

1.建立工程文件夹my_ejs. 2.首先利用npm install express和npm install ejs 下载这两个家伙.至于要不要设置成全局的,看习惯,我习惯性的下载到本项目中的文件夹中my_ejs. 然后建立相应的文件: index.js: form.ejs: index.ejs app.js: 开始运行app.js node app.js,然后再浏览器端访问:localhost:1337 单击发表文章: 点击发表,跳转到首页. 好了到此为止,一个简易的"网站"算是出来

node+express+jade搭建一个简单的&quot;网站&quot;

1.建立工程文件夹:my_jade 2.下载express和jade包到本地.我个人不喜欢下载成全局的,我喜欢下到工程文件夹中去. 3.建立相关的文件夹和文件. index.js: style.css gys.jade代码: index.jade: layout.jade app.js 运行app.js:node app.js; 在浏览器中预览: 在看一个不使用页面布局的例子: 修改index.js 修改app.js 运行app.js在浏览器中预览: 单击提交 会触发ajax. 过几天补上.睡觉

关于 百度-站长工具 sitemap 实时推送 那点事

相信 站长们对 百度 站长工具 一定不陌生吧!网站地图(sitemap) 文件,站长们也一点都很 熟悉!网站生成 网站地图文件,这样有助于百度蜘蛛在收录时,找到自己喜欢的格式,更好的收录你的网站内容!但这是被动的,只有百度定时来访问!内容收录不是很及时. 但最近 发现百度 站长工具 的 "数据提交"-->"Sitemap" 里面多了一个"实时推送" 功能! 实时推送功能:就是在你发布新内容的时候,通过自己的程序 向百度的 数据推送接口 提交

node+express+socket.io+mysql=通讯服务器搭建(一)

首发github/blog 欢迎大家评论给星 安装 首先假定你已经安装了 Node.js,接下来为你的应用创建一个目录,然后安装express-generator应用骨架 $ mkdir node-demo $ npm install express-generator -g //mac需要加sudo express -h $ express -h 用法: express [options] [dir] 选项: -h, --help 输出使用信息 -V, --version 输出版本号 -e,

用node.js(socket.io)实现数据实时推送

在做商品拍卖的时候,要求在商品的拍卖页面需要实时的更新当前商品的最高价格.实现的方式有很多,比如: 1.setInterval每隔n秒去异步拉取数据(缺点:更新不够实时) 2. AJAX轮询方式方式推送数据(缺点:服务端需要在死循环中反复查询数据库) 3.websocket推送数据(缺点:仅支持html5标准的浏览器) socket.io的简要介绍 所有客户端都通过socket.io挂在nodejs服务器上(注意: 只是挂着,不需要任何循环,因为它是事件驱动的):需要推送消息了,服务器就与nod

使用Nodejs实现实时推送MySQL数据库最新信息到客户端

下面我们要做的就是把MySQL这边一张表数据的更新实时的推送到客户端,比如MySQL这边表的数据abc变成123了,那使用程序就会把最新的123推送到每一个连接到服务器的客户端.如果服务器的连接的客户端为0,也就是这时候没有客户端连接,那程序也不会执行推送信息的代码以免产生不必要的资源消耗,当有客户端连上的时候又开始推送.demo的代码大家可以到下面的Download按钮去下载. 要运行首先我们要安装nodejs要用到的mysql模块: $ npm install mysql 更多关于mysql

基于HTTP协议之WEB消息实时推送技术原理及实现

很早就想写一些关于网页消息实时推送技术方面的文章,但是由于最近实在忙,没有时间去写文章.本文主要讲解基于 HTTP1.1 协议的 WEB 推送的技术原理及实现.本人曾经在工作的时候也有做过一些用到网页消息实时推送的项目,但是当时实现的都不是很完美,甚至有时候是通过 Ajax 轮训的方式实现.在网上也找过不少的资料,真正说到点子上的几乎没有,很多文章大都是长篇大论,说了一些新有名字,什么“HTTP 长连接”,“实时推送”,“Comet 长连接推送技术”等.但真正提到如何实现实时推送的文章倒是没有看

nodejs+socketio+redis实现前端消息实时推送

nodejs+socketio+redis实现前端消息实时推送 1. 后端部分 发送redis消息 可以参考此篇实现(直接使用Jedis即可) http://www.cnblogs.com/binyue/p/4763352.html 2.后端部分: 接收redis消息 var redis; if(process.argv.length <= 2){ redis = require('redis').createClient(); }else{ redis = require('redis').c