即时通讯_即时通讯源码 - CSDN
即时通讯 订阅
即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷;服务提供商也提供了越来越丰富的通讯服务功能。 不容置疑,Internet已经成为真正的信息高速公路。从实际工程应用角度出发,以计算机网络原理为指导,结合当前网络中的一些常用技术,编程实现基于C/S架构的网络聊天工具是切实可行的。目前,中国市场上的企业级即时通信工具主要包括:信鸽、视高科技的视高可视协同办公平台、263EM、群英CC2010、通软联合的GoCom、腾讯公司的RTX、IBM的Lotus Sametime、点击科技的GKE、中国互联网办公室的imo、中国移动的企业飞信、华夏易联的e-Link、擎旗的UcStar等。相对于个人即时通信工具而言,企业级即时通信工具更加强调安全性、实用性、稳定性和扩展性。 [1]  实时通信(Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流。 展开全文
即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷;服务提供商也提供了越来越丰富的通讯服务功能。 不容置疑,Internet已经成为真正的信息高速公路。从实际工程应用角度出发,以计算机网络原理为指导,结合当前网络中的一些常用技术,编程实现基于C/S架构的网络聊天工具是切实可行的。目前,中国市场上的企业级即时通信工具主要包括:信鸽、视高科技的视高可视协同办公平台、263EM、群英CC2010、通软联合的GoCom、腾讯公司的RTX、IBM的Lotus Sametime、点击科技的GKE、中国互联网办公室的imo、中国移动的企业飞信、华夏易联的e-Link、擎旗的UcStar等。相对于个人即时通信工具而言,企业级即时通信工具更加强调安全性、实用性、稳定性和扩展性。 [1]  实时通信(Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流。
信息
中文名
即时通讯
译    名
Instant Messenger
简    称
IM
又    名
实时通讯
分    类
应用软件
即时通讯简介
IM最早的创始人是三个以色列青年,是他们在1996年开发出来的,取名叫ICQ。1998年当ICQ注册用户数达到1200万时,被AOL看中,以2.87亿美元的天价买走。2008年CQ有1亿多用户,主要市场在美洲和欧洲,已成为世界上最大的即时通信系统。即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流。即时通讯按使用用途分为企业即时通讯和网站即时通讯,根据装载的对象又可分为手机即时通讯和PC即时通讯,手机即时通讯代表是短信,网站、视频即时通讯。
收起全文
  • 原生Android开发实战教程,讲授如何实现在App中发送文字、语音、图片、VoIP、自定义(如红包)等消息,以及如何添加/删除好友、设置/修改头像、创建群组、讨论组、聊天室等基础IM功能。
  • Android IM 即时通讯实战课程 基于Bmob IM + 云数据库 + User System 实现 即时通讯App
  • IM即时通讯的技术123

    2020-07-30 23:30:19
    即时通讯IM完整源码,项目包含所有源码,有数据库,im使用说明,技术文档,即时通讯系统ppt文件,还有数据库设计文档
  • 首先讲讲IM(即时通讯)技术可以用来做什么: 聊天:qq、微信 直播:斗鱼直播、抖音 实时位置共享、游戏多人互动等等 可以说几乎所有高实时性的应用场景都需要用到IM技术。 本篇将带大家从零开始...

    前言

    首先讲讲IM(即时通讯)技术可以用来做什么:

    • 聊天:qq、微信

    • 直播:斗鱼直播、抖音

    • 实时位置共享、游戏多人互动等等

    可以说几乎所有高实时性的应用场景都需要用到IM技术。

    本篇将带大家从零开始搭建一个轻量级的IM服务端,麻雀虽小,五脏俱全,我们搭建的IM服务端实现以下功能:

    • 一对一的文本消息、文件消息通信

    • 每个消息有“已发送”/“已送达”/“已读”回执

    • 存储离线消息

    • 支持用户登录,好友关系等基本功能。

    • 能够方便地水平扩展

    通过这个项目能学到什么?

    这个项目涵盖了很多后端必备知识:

    • rpc通信

    • 数据库

    • 缓存

    • 消息队列

    • 分布式、高并发的架构设计

    • docker部署

    消息通信

    文本消息

    我们先从最简单的特性开始实现:一个普通消息的发送

    消息格式如下:

    message ChatMsg{
        id = 1;
        //消息id
        fromId = Alice
        //发送者userId
        destId = Bob
        //接收者userId
        msgBody = hello
        //消息体
    }
    

     

    从零开始开发IM(即时通讯)服务端(一)附源码

    如上图,我们现在有两个用户:Alice和Bob连接到了服务器,当Alice发送消息message(hello)给Bob,服务端接收到消息,根据消息的destId进行转发,转发给Bob。

    发送回执

    那我们要怎么来实现回执的发送呢?

    我们定义一种回执数据格式ACK,MsgType有三种,分别是sent(已发送),delivered(已送达), read(已读):

    message AckMsg {
        id;
        //消息id
        fromId;
        //发送者id
        destId;
        //接收者id
        msgType;
        //消息类型
        ackMsgId;
        //确认的消息id
    }
    
    enum MsgType {
        DELIVERED;
        READ;
    }
    

    当服务端接受到Alice发来的消息时:

    1.向Alice发送一个sent(hello)表示消息已经被发送到服务器。

    message AckMsg {
        id = 2;
        fromId = Alice;
        destId = Bob;
        msgType = SENT;
        ackMsgId = 1;
    }
    

     

    从零开始开发IM(即时通讯)服务端(一)附源码

    2.服务器把hello转发给Bob后,立刻向Alice发送delivered(hello)表示消息已经发送给Bob。

    message AckMsg {
        id = 3;
        fromId = Bob;
        destId = Alice;
        msgType = DELIVERED;
        ackMsgId = 1;
    }
    

     

    从零开始开发IM(即时通讯)服务端(一)附源码

    3.Bob阅读消息后,客户端向服务器发送read(hello)表示消息已读

    message AckMsg {
        id = 4;
        fromId = Bob;
        destId = Alice;
        msgType = READ;
        ackMsgId = 1;
    }
    

    这个消息会像一个普通聊天消息一样被服务器处理,最终发送给Alice。

     

    从零开始开发IM(即时通讯)服务端(一)附源码

    在服务器这里不区分ChatMsg和AckMsg,处理过程都是一样的:解析消息的destId并进行转发。

    水平扩展

    当用户量越来越大,必然需要增加服务器的数量,用户的连接被分散在不同的机器上。此时,就需要存储用户连接在哪台机器上。

    我们引入一个新的模块来管理用户的连接信息。

    管理用户状态

    从零开始开发IM(即时通讯)服务端(一)附源码

     

    模块叫做user status,共有三个接口:

    public interface UserStatusService {
    
        /**
         * 用户上线,存储userId与机器id的关系
         *
         * @param userId
         * @param connectorId
         * @return 如果当前用户在线,则返回他连接的机器id,否则返回null
         */
        String online(String userId, String connectorId);
    
        /**
         * 用户下线
         *
         * @param userId
         */
        void offline(String userId);
    
        /**
         * 通过用户id查找他当前连接的机器id
         *
         * @param userId
         * @return
         */
        String getConnectorId(String userId);
    }
    

    这样我们就能够对用户连接状态进行管理了,具体的实现应考虑服务的用户量、期望性能等进行实现。

    此处我们使用redis来实现,将userId和connectorId的关系以key-value的形式存储。

    消息转发

    除此之外,还需要一个模块在不同的机器上转发消息,如下结构:

    从零开始开发IM(即时通讯)服务端(一)附源码

    此时我们的服务被拆分成了connector和transfer两个模块,connector模块用于维持用户的长链接,而transfer的作用是将消息在多个connector之间转发。

    现在Alice和Bob连接到了两台connector上,那么消息要如何传递呢?

    1.Alice上线,连接到机器[1]上时

    • 将Alice和它的连接存入内存中。

    • 调用user status的online方法记录Alice上线。

    2.Alice发送了一条消息给Bob

    • 机器[1]收到消息后,解析destId,在内存中查找是否有Bob。

    • 如果没有,代表Bob未连接到这台机器,则转发给transfer。

    3.transfer调用user status的getConnectorId(Bob)方法找到Bob所连接的connector,返回机器[2],则转发给机器[2]。

    流程图:

    从零开始开发IM(即时通讯)服务端(一)附源码

    总结:

    • 引入user status模块管理用户连接,transfer模块在不同的机器之间转发,使服务可以水平扩展。

    • 为了满足实时转发,transfer需要和每台connector机器都保持长链接。

    离线消息

    如果用户当前不在线,就必须把消息持久化下来,等待用户下次上线再推送,这里使用mysql存储离线消息。

    为了方便地水平扩展,我们使用消息队列进行解耦。

    • transfer接收到消息后如果发现用户不在线,就发送给消息队列入库。

    • 用户登录时,服务器从库里拉取离线消息进行推送。

    用户登录、好友关系

    用户的注册登录、账户管理、好友关系链等功能更适合使用http协议,因此我们将这个模块做成一个restful服务,对外暴露http接口供客户端调用。

    至此服务端的基本架构就完成了:

    从零开始开发IM(即时通讯)服务端(一)附源码

    总结

    以上就是这篇博客的所有内容,本篇帮大家构建了IM服务端的架构,但还有很多细节需要我们去思考,例如:

    • 如何保证消息的顺序和唯一

    • 多个设备在线如何保证消息一致性

    • 如何处理消息发送失败

    • 消息的安全性

    • 如果要存储聊天记录要怎么做

    • 数据库分表分库

    • 服务高可用
      ……

    更多细节实现就留到下一篇啦~

    IM1.0.0版本已上线,github链接:

    https://github.com/yuanrw/IM

    1. SpringBoot内容聚合

    2. 面试题内容聚合

    3. 设计模式内容聚合

    4. Mybatis内容聚合

    5. 多线程内容聚合

    最后,推荐一个专注于Java学习的公众号,Java知音。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式及面试资料、开源项目,助力开发者成长!

    Javaç¥é³å®æ¹å¬ä¼å·

    展开全文
  • IM即时通讯哇呼--解析

    2020-07-01 14:33:40
    哇呼“Chat”是一款包含android客户端/ios客户端/pc客户端/WEB客户端的即时通讯系统。是由闪电云自主研发,服务器端源码直接部署在客户主机。非任何第三方IM通讯平台! 为各行业门户网站和企事业单位提供“一站式”...

    哇呼“Chat”是一款包含android客户端/ios客户端/pc客户端/WEB客户端的即时通讯系统。是由闪电云自主研发,服务器端源码直接部署在客户主机。非任何第三方IM通讯平台!

    为各行业门户网站和企事业单位提供“一站式”定制解决方案,打造一个稳定,安全,高效,可扩展的即时通信系统。

    哇呼IM的优点在于私有通信协议彻底防监听、防泄密。军事级的保密通话,彻底杜绝后门、漏洞等安全隐患。拥有独立服务器、独立数据库、独立账号体系、所有数据均由您自己掌控,部署完全属于自己的IM系统。
    哇呼采用RSA+AES的双重加密方式,端对端通讯方式将文字、语音、图片发送给自己好友,不再被第三方存储和监控,消息经私有隧道传输,在分享信息的同时充分保护您的隐私。

    提供服务端快速集成方案

    1.IM消息服务接口
    提供IM消息服务接口,使您的app快速集成即时通讯功能
    2.IM消息服务类库
    提供IM消息服务类库,在项目开发中直接集成,实现个性化的聊天功能
    3.集成开发
    提供业务代码,我们将业务代码与即时通讯进行整合
    产品亮点:
    阅后即焚
    通讯加密
    私有部署
    语音视频聊天
    红包转账
    朋友圈
    单聊/群聊

    展开全文
  • 即时通讯-im

    2017-11-02 21:12:56
    No.1_即时通讯 作用:即时通信(instant message,IM)是指能够即时发送和接收互联网消息等的业务。1998年即时通信的功能日益丰富,逐渐集成了电子邮件、博客、音乐、电视、游戏和搜索等多种功能。 即时通信...
    No.1_即时通讯

    作用:即时通信(instant message,IM)是指能够即时发送和接收互联网消息等的业务。1998年即时通信的功能日益丰富,逐渐集成了电子邮件博客音乐、电视、游戏和搜索等多种功能。
    即时通信已经发展成集交流、资讯、娱乐、搜索、电子商务、办公协作和企业客户服务等为一体的综合化信息平台。微软腾讯AOLYahoo等重要即时通信提供商都提供通过手机接入互联网即时通信的业务,用户可以通过手机与其他已经安装了相应客户端软件的手机或电脑收发消息。
    ------来自百度百科
    融云的网址:http://www.rongcloud.cn/
    主流的四种IM(IM:instant messaging,即时消息)协议

    简答题:
    简述什么是IM?
    答:即时通信为IM的简写是指能够即使发送和接收互联网的业务,生活中见到的APP为QQ,微信等,所用的第三方容云,环信等,在本月中我们同第三方容云的集成文档学习

    简述使用第三方SDK实现即时通讯的优缺点
    答:优点是清楚的划分了私人空间与工作,让工作交流专业化,多元化的沟通方式不单一,让团队沟通更有目的和效率,添加聚合服务,形成统一的消息中心,与企业管理融合,高效办公,企业即时通讯软件能够保障企业沟通的信息安全

    简述如何获取容云的appkey和secret
    答:首先登陆融云的官网,去注册用户,进入后台之后点击创建应用,就会显示AppKey

    获取 Token
    Token 称为用户令牌,App Key 是您的 App 的唯一标识,Token 则是您 App 上的每一个用户的身份授权象征。您可以通过提交 userId 等信息来获得一个该用户对应的 Token,并使用这个 Token 作为该用户的唯一身份凭证与其他用户进行通信
    Token 的主要作用是身份授权和安全,因此不能通过客户端直接访问融云服务器获取 Token,您必须通过 Server API 从融云服务器 获取 Token 返回给您的 App,并在之后连接时使用。详细描述请参考 Server 开发指南中的用户服务和获取 Token 方法小节。
    我们将Token存入Sp中,Token是加密的二进制所组成的




    展开全文
  • 即时通讯简介

    2018-04-25 10:21:26
    前言有关IM(InstantMessaging)聊天应用(如:微信,QQ)、消息推送技术(如:现今移动端APP标配的消息推送模块)等即时通讯应用场景下,大多数都是桌面应用程序或者native应用较为流行,而网上关于原生IM(相关文章...

    前言

    有关IM(InstantMessaging)聊天应用(如:微信,QQ)、消息推送技术(如:现今移动端APP标配的消息推送模块)等即时通讯应用场景下,大多数都是桌面应用程序或者native应用较为流行,而网上关于原生IM(相关文章请参见:《IM架构篇》、《IM综合资料》、《IM/推送的通信格式、协议篇》、《IM心跳保活篇》、《IM安全篇》、《实时音视频开发》)、消息推送应用(参见:《推送技术好文》)的通信原理介绍也较多,此处不再赘述。


    而web端的IM应用,由于浏览器的兼容性以及其固有的“客户端请求服务器处理并响应”的通信模型,造成了要在浏览器中实现一个兼容性较好的IM应用,其通信过程必然是诸多技术的组合,本文的目的就是要详细探讨这些技术并分析其原理和过程。

    学习交流

    - 更多即时通讯技术资料:http://www.52im.net/forum.php?mod=collection&op=all

    Web端即时通讯技术盘点请参见:

    Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

    关于Ajax短轮询:
    找这方面的资料没什么意义,除非忽悠客户,否则请考虑其它3种方案即可。

    有关Comet技术的详细介绍请参见:
    Comet技术详解:基于HTTP长连接的Web端实时通信技术
    WEB端即时通讯:HTTP长连接、长轮询(long polling)详解
    WEB端即时通讯:不用WebSocket也一样能搞定消息的即时性
    开源Comet服务器iComet:支持百万并发的Web端即时通讯方案

    有关WebSocket的详细介绍请参见:
    WebSocket详解(一):初步认识WebSocket技术
    WebSocket详解(二):技术原理、代码演示和应用案例
    WebSocket详解(三):深入WebSocket通信协议细节
    Socket.IO介绍:支持WebSocket、用于WEB端的即时通讯的框架
    socket.io和websocket 之间是什么关系?有什么区别?

    有关SSE的详细介绍文章请参见:
    SSE技术详解:一种全新的HTML5服务器推送事件技术

    更多WEB端即时通讯文章请见:
    http://www.52im.net/forum.php?mod=collection&action=view&ctid=15

    一、传统Web的通信原理

    浏览器本身作为一个瘦客户端,不具备直接通过系统调用来达到和处于异地的另外一个客户端浏览器通信的功能。这和我们桌面应用的工作方式是不同的,通常桌面应用通过socket可以和远程主机上另外一端的一个进程建立TCP连接,从而达到全双工的即时通信。
    浏览器从诞生开始一直走的是客户端请求服务器,服务器返回结果的模式,即使发展至今仍然没有任何改变。所以可以肯定的是,要想实现两个客户端的通信,必然要通过服务器进行信息的转发。例如A要和B通信,则应该是A先把信息发送给IM应用服务器,服务器根据A信息中携带的接收者将它再转发给B,同样B到A也是这种模式,如下所示:

    新手入门贴:史上最全Web端即时通讯技术原理详解_1.png 

    二、传统通信方式实现IM应用需要解决的问题

    我们认识到基于web实现IM软件依然要走浏览器请求服务器的模式,这这种方式下,针对IM软件的开发需要解决如下三个问题:

    • 双全工通信:
      即达到浏览器拉取(pull)服务器数据,服务器推送(push)数据到浏览器;
    • 低延迟:
      即浏览器A发送给B的信息经过服务器要快速转发给B,同理B的信息也要快速交给A,实际上就是要求任何浏览器能够快速请求服务器的数据,服务器能够快速推送数据到浏览器;
    • 支持跨域:
      通常客户端浏览器和服务器都是处于网络的不同位置,浏览器本身不允许通过脚本直接访问不同域名下的服务器,即使IP地址相同域名不同也不行,域名相同端口不同也不行,这方面主要是为了安全考虑。


    即时通讯网注:关于浏览器跨域访问导致的安全问题,有一个被称为CSRF网络攻击方式,请看下面的摘录:

    CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

    你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

    CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI……而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。


    基于以上分析,下面针对这三个问题给出解决方案。

    三、全双工低延迟的解决办法

    解决方案3.1:客户端浏览器轮询服务器(polling)

    这是最简单的一种解决方案,其原理是在客户端通过Ajax的方式的方式每隔一小段时间就发送一个请求到服务器,服务器返回最新数据,然后客户端根据获得的数据来更新界面,这样就间接实现了即时通信。优点是简单,缺点是对服务器压力较大,浪费带宽流量(通常情况下数据都是没有发生改变的)。

    客户端代码如下:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    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
    function createXHR(){
            if(typeof XMLHttpRequest !='undefined'){
                return new XMLHttpRequest();
            }else if(typeof ActiveXObject !='undefined' ){
                if(typeof arguments.callee.activeXString!="string"){
                var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0",
                        "MSXML2.XMLHttp"],
                        i,len;
                for(i=0,len=versions.length;i<len;i++){
                    try{
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString=versions[i];
                        break;
                    }catch(ex) {
     
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
           }else{
                throw new Error("no xhr object available");
            }
        }
        function polling(url,method,data){
           method=method ||'get';
           data=data || null;
           var xhr=createXHR();
            xhr.onreadystatechange=function(){
                if(xhr.readyState==4){
                    if(xhr.status>=200&&xhr.status<300||xhr.status==304){
                        console.log(xhr.responseText);
                    }else{
                        console.log("fail");
                    }
                }
            };
            xhr.open(method,url,true);
            xhr.send(data);
        }
        setInterval(function(){
            polling('http://localhost:8088/time','get');
        },2000);



    创建一个XHR对象,每2秒就请求服务器一次获取服务器时间并打印出来。

    服务端代码(Node.js):




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    var http=require('http');
    var fs = require("fs");
    var server=http.createServer(function(req,res){
    if(req.url=='/time'){
        //res.writeHead(200, {'Content-Type': 'text/plain','Access-Control-Allow-Origin':'http://localhost'});
        res.end(new Date().toLocaleString());
    };
    if(req.url=='/'){
        fs.readFile("./pollingClient.html","binary",function(err, file) {
            if (!err) {
                res.writeHead(200, {'Content-Type':'text/html'});
                res.write(file,"binary");
                res.end();
            }
    });
    }
    }).listen(8088,'localhost');
    server.on('connection',function(socket){
        console.log("客户端连接已经建立");
    });
    server.on('close',function(){
        console.log('服务器被关闭');
    });




    结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_2.png 

    解决方案3.2:长轮询(long-polling)

    在上面的轮询解决方案中,由于每次都要发送一个请求,服务端不管数据是否发生变化都发送数据,请求完成后连接关闭。这中间经过的很多通信是不必要的,于是又出现了长轮询(long-polling)方式。这种方式是客户端发送一个请求到服务器,服务器查看客户端请求的数据是否发生了变化(是否有最新数据),如果发生变化则立即响应返回,否则保持这个连接并定期检查最新数据,直到发生了数据更新或连接超时。同时客户端连接一旦断开,则再次发出请求,这样在相同时间内大大减少了客户端请求服务器的次数。代码如下。(详细技术文章请参见《WEB端即时通讯:HTTP长连接、长轮询(long polling)详解》)

    客户端:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    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
    function createXHR(){
            if(typeof XMLHttpRequest !='undefined'){
                return new XMLHttpRequest();
            }else if(typeof ActiveXObject !='undefined' ){
                if(typeof arguments.callee.activeXString!="string"){
                    var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0",
                                "MSXML2.XMLHttp"],
                            i,len;
                    for(i=0,len=versions.length;i<len;i++){
                        try{
                            new ActiveXObject(versions[i]);
                            arguments.callee.activeXString=versions[i];
                            break;
                        }catch(ex) {
     
                        }
                    }
                }
                return new ActiveXObject(arguments.callee.activeXString);
            }else{
                throw new Error("no xhr object available");
            }
        }
        function longPolling(url,method,data){
            method=method ||'get';
            data=data || null;
            var xhr=createXHR();
            xhr.onreadystatechange=function(){
                if(xhr.readyState==4){
                    if(xhr.status>=200&&xhr.status<300||xhr.status==304){
                        console.log(xhr.responseText);
                    }else{
                        console.log("fail");
                    }
                    longPolling(url,method,data);
                }
            };
            xhr.open(method,url,true);
            xhr.send(data);
        }
        longPolling('http://localhost:8088/time','get');



    在XHR对象的readySate为4的时候,表示服务器已经返回数据,本次连接已断开,再次请求服务器建立连接。

    服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    var http=require('http');
    var fs = require("fs");
    var server=http.createServer(function(req,res){
        if(req.url=='/time'){
            setInterval(function(){
                sendData(res);
            },20000);
        };
        if(req.url=='/'){
            fs.readFile("./lpc.html","binary",function(err, file) {
                if (!err) {
                    res.writeHead(200, {'Content-Type':'text/html'});
                    res.write(file,"binary");
                    res.end();
                }
            });
        }
    }).listen(8088,'localhost');
    //用随机数模拟数据是否变化
    function sendData(res){
        var randomNum=Math.floor(10*Math.random());
        console.log(randomNum);
        if(randomNum>=0&&randomNum<=5){
            res.end(new Date().toLocaleString());
        }
    }



    在服务端通过生成一个在1到9之间的随机数来模拟判断数据是否发生了变化,当随机数在0到5之间表示数据发生了变化,直接返回,否则保持连接,每隔2秒再检测。

    结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_3.png 
    可以看到返回的时间是没有规律的,并且单位时间内返回的响应数相比polling方式较少。

    解决方案3.3:基于http-stream通信

    上面的long-polling技术为了保持客户端与服务端的长连接采取的是服务端阻塞(保持响应不返回),客户端轮询的方式,在Comet技术中(详细技术文章请参见《Comet技术详解:基于HTTP长连接的Web端实时通信技术》),还存在一种基于http-stream流的通信方式。其原理是让客户端在一次请求中保持和服务端连接不断开,然后服务端源源不断传送数据给客户端,就好比数据流一样,并不是一次性将数据全部发给客户端。它与polling方式的区别在于整个通信过程客户端只发送一次请求,然后服务端保持与客户端的长连接,并利用这个连接在回送数据给客户端。

    这种方案有分为几种不同的数据流传输方式。

    3.3.1 基于XHR对象的streaming方式

    这种方式的思想是构造一个XHR对象,通过监听它的onreadystatechange事件,当它的readyState为3的时候,获取它的responseText然后进行处理,readyState为3表示数据传送中,整个通信过程还没有结束,所以它还在不断获取服务端发送过来的数据,直到readyState为4的时候才表示数据发送完毕,一次通信过程结束。在这个过程中,服务端传给客户端的数据是分多次以stream的形式发送给客户端,客户端也是通过stream形式来获取的,所以称作http-streaming数据流方式,代码如下。

    客户端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function createStreamClient(url,progress,done){
            //received为接收到数据的计数器
            var xhr=new XMLHttpRequest(),received=0;
            xhr.open("get",url,true);
            xhr.onreadystatechange=function(){
                var result;
                if(xhr.readyState==3){
                    //console.log(xhr.responseText);
                    result=xhr.responseText.substring(received);
                    received+=result.length;
                    progress(result);
                }else if(xhr.readyState==4){
                    done(xhr.responseText);
                }
            };
            xhr.send(null);
            return xhr;
        }
        var client=createStreamClient("http://localhost:8088/stream",function(data){
            console.log("Received:"+data);
        },function(data){
            console.log("Done,the last data is:"+data);
        })



    这里由于客户端收到的数据是分段发过来的,所以最好定义一个游标received,来获取最新数据而舍弃之前已经接收到的数据,通过这个游标每次将接收到的最新数据打印出来,并且在通信结束后打印出整个responseText。

    服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    var http=require('http');
    var fs = require("fs");
    var count=0;
    var server=http.createServer(function(req,res){
        if(req.url=='/stream'){
            res.setHeader('content-type','multipart/octet-stream');
            var timer=setInterval(function(){
                sendRandomData(timer,res);
            },2000);
     
        };
        if(req.url=='/'){
            fs.readFile("./xhr-stream.html","binary",function(err, file) {
                if (!err) {
                    res.writeHead(200, {'Content-Type':'text/html'});
                    res.write(file,"binary");
                    res.end();
                }
            });
        }
    }).listen(8088,'localhost');
    function sendRandomData(timer,res){
        var randomNum=Math.floor(10000*Math.random());
        console.log(randomNum);
        if(count++==10){
            clearInterval(timer);
            res.end(randomNum.toString());
        }
            res.write(randomNum.toString());
    }



    服务端通过计数器count将数据分十次发送,每次生成一个小于10000的随机数发送给客户端让它进行处理。

    结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_4.png 
    可以看到每次传过来的数据流都进行了处理,同时打印出了整个最终接收到的完整数据。这种方式间接实现了客户端请求,服务端及时推送数据给客户端。

    3.3.2 基于iframe的数据流

    由于低版本的IE不允许在XHR的readyState为3的时候获取其responseText属性,为了达到在IE上使用这个技术,又出现了基于iframe的数据流通信方式。具体来讲,就是在浏览器中动态载入一个iframe,让它的src属性指向请求的服务器的URL,实际上就是向服务器发送了一个http请求,然后在浏览器端创建一个处理数据的函数,在服务端通过iframe与浏览器的长连接定时输出数据给客户端,但是这个返回的数据并不是一般的数据,而是一个类似于<script type=\”text/javascript\”>parent.process(‘”+randomNum.toString()+”’)</script>脚本执行的方式,浏览器接收到这个数据就会将它解析成js代码并找到页面上指定的函数去执行,实际上是服务端间接使用自己的数据间接调用了客户端的代码,达到实时更新客户端的目的。

    客户端代码如下:




    1
    2
    3
    4
    5
    6
    7
    8
    9
    function process(data){
                console.log(data);
            }
    var dataStream = function (url) {
        var ifr = document.createElement("iframe"),timer;
        ifr.src = url;
        document.body.appendChild(ifr);
    };
        dataStream('http://localhost:8088/htmlfile');




    客户端为了简单起见,定义对数据处理就是打印出来。

    服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    var http=require('http');
    var fs = require("fs");
    var count=0;
    var server=http.createServer(function(req,res){
        if(req.url=='/htmlfile'){
            res.setHeader('content-type','text/html');
            var timer=setInterval(function(){
                sendRandomData(timer,res);
            },2000);
     
        };
        if(req.url=='/'){
            fs.readFile("./htmlfile-stream.html","binary",function(err, file) {
                if (!err) {
                    res.writeHead(200, {'Content-Type':'text/html'});
                    res.write(file,"binary");
                    res.end();
                }
            });
        }
    }).listen(8088,'localhost');
    function sendRandomData(timer,res){
        var randomNum=Math.floor(10000*Math.random());
        console.log(randomNum.toString());
        if(count++==10){
            clearInterval(timer);
            res.end("<script type=\"text/javascript\">parent.process('"+randomNum.toString()+"')</script>");
        }
        res.write("<script type=\"text/javascript\">parent.process('"+randomNum.toString()+"')</script>");
    }



    服务端定时发送随机数给客户端,并调用客户端process函数。

    在IE5中测试结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_5.png 
    可以看到实现在低版本IE中客户端到服务器的请求-推送的即时通信。

    3.3.3 基于htmlfile的数据流通信

    又出现新问题了,在IE中,使用iframe请求服务端,服务端保持通信连接没有全部返回之前,浏览器title一直处于加载状态,并且底部也显示正在加载,这对于一个产品来讲用户体验是不好的,于是谷歌的天才们又想出了一中hack方式。就是在IE中,动态生成一个htmlfile对象,这个对象ActiveX形式的com组件,它实际上就是一个在内存中实现的HTML文档,通过将生成的iframe添加到这个内存中的HTMLfile中,并利用iframe的数据流通信方式达到上面的效果。同时由于HTMLfile对象并不是直接添加到页面上的,所以并没有造成浏览器显示正在加载的现象。代码如下。

    客户端:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function connect_htmlfile(url, callback) {
                var transferDoc = new ActiveXObject("htmlfile");
                transferDoc.open();
                transferDoc.write(
                                "<!DOCTYPE html><html><body><script  type=\"text/javascript\">" +
                                "document.domain='" + document.domain + "';" +
                                "<\/script><\/body><\/html>");
                transferDoc.close();
                var ifrDiv = transferDoc.createElement("div");
                transferDoc.body.appendChild(ifrDiv);
                ifrDiv.innerHTML = "<iframe src='" + url + "'><\/iframe>";
                transferDoc.callback=callback;
                setInterval(function () {}, 10000);
            }
            function prograss(data) {
                alert(data);
            }
            connect_htmlfile('http://localhost:8088/htmlfile',prograss);




    服务端传送给iframe的是这样子:




    1
    <script type=\"text/javascript\">callback.process('"+randomNum.toString()+"')</script>




    这样就在iframe流的原有方式下避免了浏览器的加载状态。

    解决方案3.4:SSE(服务器推送事件(Server-sent Events)

    为了解决浏览器只能够单向传输数据到服务端,HTML5提供了一种新的技术叫做服务器推送事件SSE(关于该技术详细介绍请参见《SSE技术详解:一种全新的HTML5服务器推送事件技术》),它能够实现客户端请求服务端,然后服务端利用与客户端建立的这条通信连接push数据给客户端,客户端接收数据并处理的目的。从独立的角度看,SSE技术提供的是从服务器单向推送数据给浏览器的功能,但是配合浏览器主动请求,实际上就实现了客户端和服务器的双向通信。它的原理是在客户端构造一个eventSource对象,该对象具有readySate属性,分别表示如下:

    • 0:正在连接到服务器;
    • 1:打开了连接;
    • 2:关闭了连接。


    同时eventSource对象会保持与服务器的长连接,断开后会自动重连,如果要强制连接可以调用它的close方法。可以它的监听onmessage事件,服务端遵循SSE数据传输的格式给客户端,客户端在onmessage事件触发时就能够接收到数据,从而进行某种处理,代码如下。

    客户端:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    var source=new EventSource('http://localhost:8088/evt');
        source.addEventListener('message',function(e) {
            console.log(e.data);
        },false);
        source.onopen=function(){
            console.log('connected');
        }
        source.onerror=function(err){
            console.log(err);
        }




    服务端:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    var http=require('http');
    var fs = require("fs");
    var count=0;
    var server=http.createServer(function(req,res){
        if(req.url=='/evt'){
            //res.setHeader('content-type', 'multipart/octet-stream');
            res.writeHead(200, {"Content-Type":"tex" +
                "t/event-stream","Cache-Control":"no-cache",
                'Access-Control-Allow-Origin':'*',
                "Connection":"keep-alive"});
            var timer=setInterval(function(){
                if(++count==10){
                    clearInterval(timer);
                    res.end();
                }else{
                    res.write('id: ' + count + '\n');
                    res.write("data: " +new Date().toLocaleString() + '\n\n');
                }
            },2000);
     
        };
        if(req.url=='/'){
            fs.readFile("./sse.html","binary",function(err, file) {
                if (!err) {
                    res.writeHead(200, {'Content-Type':'text/html'});
                    res.write(file,"binary");
                    res.end();
                }
            });
        }
    }).listen(8088,'localhost');



    注意:这里服务端发送的数据要遵循一定的格式,通常是id:(空格)数据(换行符)data:(空格)数据(两个换行符),如果不遵循这种格式,实际上客户端是会触发error事件的。这里的id是用来标识每次发送的数据的id,是强制要加的。

    结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_6.png 

    以上就是比较常用的客户端服务端双向即时通信的解决方案,下面再来看如何实现跨域。

    四、跨域解决办法

    关于跨域是什么,限于篇幅所限,这里不做介绍,网上有很多详细的文章,这里只列举解决办法。

    解决方案4.1:基于XHR的COSR(跨域资源共享)

    CORS(跨域资源共享)是一种允许浏览器脚本向出于不同域名下服务器发送请求的技术,它是在原生XHR请求的基础上,XHR调用open方法时,地址指向一个跨域的地址,在服务端通过设置’Access-Control-Allow-Origin’:’*’响应头部告诉浏览器,发送的数据是一个来自于跨域的并且服务器允许响应的数据,浏览器接收到这个header之后就会绕过平常的跨域限制,从而和平时的XHR通信没有区别。该方法的主要好处是在于客户端代码不用修改,服务端只需要添加’Access-Control-Allow-Origin’:’*’头部即可。适用于ff,safari,opera,chrome等非IE浏览器。跨域的XHR相比非跨域的XHR有一些限制,这是为了安全所需要的,主要有以下限制:

    • 客户端不能使用setRequestHeader设置自定义头部;
    • 不能发送和接收cookie;
    • 调用getAllResponseHeaders()方法总会返回空字符串。


    以上这些措施都是为了安全考虑,防止常见的跨站点脚本攻击(XSS)和跨站点请求伪造(CSRF)。

    客户端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    var polling=function(){
            var xhr=new XMLHttpRequest();
            xhr.onreadystatechange=function(){
                if(xhr.readyState==4)
                    if(xhr.status==200){
                        console.log(xhr.responseText);
                    }
                }
        xhr.open('get','http://localhost:8088/cors');
        xhr.send(null);
        };
        setInterval(function(){
            polling();
        },1000);




    服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    var http=require('http');
    var fs = require("fs");
    var server=http.createServer(function(req,res){
        if(req.url=='/cors'){
                res.writeHead(200, {'Content-Type':'text/plain','Access-Control-Allow-Origin':'http://localhost'});
                res.end(new Date().toString());
        }
        if(req.url=='/jsonp'){
     
        }
    }).listen(8088,'localhost');
    server.on('connection',function(socket){
        console.log("客户端连接已经建立");
    });
    server.on('close',function(){
        console.log('服务器被关闭');
    });



    注意服务端需要设置头部Access-Control-Allow-Origin为需要跨域的域名。

    这里为了测试在端口8088上监听请求,然后让客户端在80端口上请求服务,结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_7.png 

    解决方案4.2:基于XDR的CORS

    对于IE8-10,它是不支持使用原生的XHR对象请求跨域服务器的,它自己实现了一个XDomainRequest对象,类似于XHR对象,能够发送跨域请求,它主要有以下限制:

    • cookie不会随请求发送,也不会随响应返回;
    • 只能设置请求头部信息中的Content-Type字段;
    • 不能访问响应头部信息;
    • 只支持Get和Post请求;
    • 只支持IE8-IE10。


    客户端请求代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    var polling=function(){
            var xdr=new XDomainRequest();
            xdr.onload=function(){
                console.log(xdr.responseText);
            };
            xdr.onerror=function(){
                console.log('failed');
            };
            xdr.open('get','http://localhost:8088/cors');
            xdr.send(null);
        };
        setInterval(function(){
            polling();
        },1000);




    服务端代码和同上,在IE8中测试结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_8.png 

    解决方案4.3:基于JSONP的跨域

    这种方式不需要在服务端添加Access-Control-Allow-Origin头信息,其原理是利用HTML页面上script标签对跨域没有限制的特点,让它的src属性指向服务端请求的地址,其实是通过script标签发送了一个http请求,服务器接收到这个请求之后,返回的数据是自己的数据加上对客户端JS函数的调用,其原理类似于我们上面所说的iframe流的方式,客户端浏览器接收到返回的脚本调用会解析执行,从而达到更新界面的目的。

    客户端代码如下:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    function callback(data){
            console.log("获得的跨域数据为:"+data);
        }
        function sendJsonp(url){
            var oScript=document.createElement("script");
            oScript.src=url;
            oScript.setAttribute('type',"text/javascript");
            document.getElementsByTagName('head')[0].appendChild(oScript);
        }
        setInterval(function(){
            sendJsonp('http://localhost:8088/jsonp?cb=callback');
        },1000);




    服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var http=require('http');
    var url=require('url');
    var server=http.createServer(function(req,res){
        if(/\/jsonp/.test(req.url)){
            var urlData=url.parse(req.url,true);
            var methodName=urlData.query.cb;
            res.writeHead(200,{'Content-Type':'application/javascript'});
            //res.end("<script type=\"text/javascript\">"+methodName+"("+new Date().getTime()+");</script>");
            res.end(methodName+"("+new Date().getTime()+");");
            //res.end(new Date().toString());
        }
    }).listen(8088,'localhost');
    server.on('connection',function(socket){
        console.log("客户端连接已经建立");
    });
    server.on('close',function(){
        console.log('服务器被关闭');
    });



    注意这里服务端输出的数据content-type首部要设定为application/javascript,否则某些浏览器会将其当做文本解析。

    结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_9.png 

    五、WebSocket

    在上面的这些解决方案中,都是利用浏览器单向请求服务器或者服务器单向推送数据到浏览器这些技术组合在一起而形成的hack技术,在HTML5中,为了加强web的功能,提供了websocket技术,它不仅是一种web通信方式,也是一种应用层协议。它提供了浏览器和服务器之间原生的双全工跨域通信,通过浏览器和服务器之间建立websocket连接(实际上是TCP连接),在同一时刻能够实现客户端到服务器和服务器到客户端的数据发送。关于该技术的原理,请参见:《WebSocket详解(一):初步认识WebSocket技术》、《WebSocket详解(二):技术原理、代码演示和应用案例》、《WebSocket详解(三):深入WebSocket通信协议细节》,此处就不在赘述了,直接给出代码。在看代码之前,需要先了解websocket整个工作过程。

    首先是客户端new 一个websocket对象,该对象会发送一个http请求到服务端,服务端发现这是个webscoket请求,会同意协议转换,发送回客户端一个101状态码的response,以上过程称之为一次握手,经过这次握手之后,客户端就和服务端建立了一条TCP连接,在该连接上,服务端和客户端就可以进行双向通信了。这时的双向通信在应用层走的就是ws或者wss协议了,和http就没有关系了。所谓的ws协议,就是要求客户端和服务端遵循某种格式发送数据报文(帧),然后对方才能够理解。

    关于ws协议要求的数据格式官网指定如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_10.png 

    其中比较重要的是FIN字段,它占用1位,表示这是一个数据帧的结束标志,同时也下一个数据帧的开始标志。opcode字段,它占用4位,当为1时,表示传递的是text帧,2表示二进制数据帧,8表示需要结束此次通信(就是客户端或者服务端哪个发送给对方这个字段,就表示对方要关闭连接了)。9表示发送的是一个ping数据。mask占用1位,为1表示masking-key字段可用,masking-key字段是用来对客户端发送来的数据做unmask操作的。它占用0到4个字节。Payload字段表示实际发送的数据,可以是字符数据也可以是二进制数据。

    所以不管是客户端和服务端向对方发送消息,都必须将数据组装成上面的帧格式来发送。

    首先来看服务端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    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
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    //握手成功之后就可以发送数据了
    var crypto = require('crypto');
    var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
    var server=require('net').createServer(function (socket) {
        var key;
        socket.on('data',function (msg) {
            if (!key) {
                //获取发送过来的Sec-WebSocket-key首部
                key = msg.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
                key = crypto.createHash('sha1').update(key + WS).digest('base64');
                socket.write('HTTP/1.1 101 Switching Protocols\r\n');
                socket.write('Upgrade: WebSocket\r\n');
                socket.write('Connection: Upgrade\r\n');
                //将确认后的key发送回去
                socket.write('Sec-WebSocket-Accept: ' + key + '\r\n');
                //输出空行,结束Http头
                socket.write('\r\n');
            }else {
                var msg=decodeData(msg);
                console.log(msg);
                //如果客户端发送的操作码为8,表示断开连接,关闭TCP连接并退出应用程序
                if(msg.Opcode==8){
                    socket.end();
                    server.unref();
                }else{
                    socket.write(encodeData({FIN:1,
                        Opcode:1,
                        PayloadData:"接受到的数据为"+msg.PayloadData}));
                }
     
            }
        });
    });
        server.listen(8000,'localhost');
    //按照websocket数据帧格式提取数据
    function decodeData(e){
        var i=0,j,s,frame={
            //解析前两个字节的基本数据
            FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7,
            PayloadLength:e[i++]&0x7F
        };
        //处理特殊长度126和127
        if(frame.PayloadLength==126)
            frame.length=(e[i++]<<8)+e[i++];
        if(frame.PayloadLength==127)
            i+=4,//长度一般用四字节的整型,前四个字节通常为长整形留空的
                frame.length=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++];
        //判断是否使用掩码
        if(frame.Mask){
            //获取掩码实体
            frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]];
            //对数据和掩码做异或运算
            for(j=0,s=[];j<frame.PayloadLength;j++)
                s.push(e[i+j]^frame.MaskingKey[j%4]);
        }else s=e.slice(i,frame.PayloadLength);//否则直接使用数据
        //数组转换成缓冲区来使用
        s=new Buffer(s);
        //如果有必要则把缓冲区转换成字符串来使用
        if(frame.Opcode==1)s=s.toString();
        //设置上数据部分
        frame.PayloadData=s;
        //返回数据帧
        return frame;
    }
    //对发送数据进行编码
    function encodeData(e){
        var s=[],o=new Buffer(e.PayloadData),l=o.length;
        //输入第一个字节
        s.push((e.FIN<<7)+e.Opcode);
        //输入第二个字节,判断它的长度并放入相应的后续长度消息
        //永远不使用掩码
        if(l<126)s.push(l);
        else if(l<0x10000)s.push(126,(l&0xFF00)>>2,l&0xFF);
        else s.push(
                127, 0,0,0,0, //8字节数据,前4字节一般没用留空
                    (l&0xFF000000)>>6,(l&0xFF0000)>>4,(l&0xFF00)>>2,l&0xFF
            );
        //返回头部分和数据部分的合并缓冲区
        return Buffer.concat([new Buffer(s),o]);
    }



    服务端通过监听data事件来获取客户端发送来的数据,如果是握手请求,则发送http 101响应,否则解析得到的数据并打印出来,然后判断是不是断开连接的请求(Opcode为8),如果是则断开连接,否则将接收到的数据组装成帧再发送给客户端。

    客户端代码:




    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    window.onload=function(){
            var ws=new WebSocket("ws://127.0.0.1:8088");
            var oText=document.getElementById('message');
            var oSend=document.getElementById('send');
            var oClose=document.getElementById('close');
            var oUl=document.getElementsByTagName('ul')[0];
            ws.onopen=function(){
                oSend.onclick=function(){
                    if(!/^\s*$/.test(oText.value)){
                        ws.send(oText.value);
                    }
                };
     
            };
            ws.onmessage=function(msg){
              var str="<li>"+msg.data+"</li>";
              oUl.innerHTML+=str;
            };
            ws.onclose=function(e){
                console.log("已断开与服务器的连接");
                ws.close();
            }
        }



    客户端创建一个websocket对象,在onopen时间触发之后(握手成功后),给页面上的button指定一个事件,用来发送页面input当中的信息,服务端接收到信息打印出来,并组装成帧返回给日客户端,客户端再append到页面上。

    客户结果如下:
    新手入门贴:史上最全Web端即时通讯技术原理详解_11.png 

    服务端输出结果:
    新手入门贴:史上最全Web端即时通讯技术原理详解_12.png 

    从上面可以看出,WebSocket在支持它的浏览器上确实提供了一种全双工跨域的通信方案,所以在各以上各种方案中,我们的首选无疑是WebSocket。

    结束语

    上面论述了这么多对于IM应用开发所涉及到的通信方式,在实际开发中,我们通常使用的是一些别人写好的实时通讯的库,比如socket.iosockjs,他们的原理就是将上面(还有一些其他的如基于Flash的push)的一些技术进行了在客户端和服务端的封装,然后给开发者一个统一调用的接口。这个接口在支持websocket的环境下使用websocket,在不支持它的时候启用上面所讲的一些hack技术。

    从实际来讲,单独使用本文上述所讲的任何一种技术(WebSocket除外)达不到我们在文章开头提出的低延时,双全工、跨域的全部要求,只有把他们组合起来才能够很好地工作,所以通常情况下,这些库都是在不同的浏览器上采用各种不同的组合来实现实时通讯的。

    下面是sockjs在不同浏览器下面采取的不同组合方式:

    新手入门贴:史上最全Web端即时通讯技术原理详解_13.png 

    从图上可以看出,对于现代浏览器(IE10+,chrome14+,Firefox10+,Safari5+以及Opera12+)都是能够很好的支持WebSocket的,其余低版本浏览器通常使用基于XHR(XDR)的polling(streaming)或者是基于iframe的的polling(streaming),对于IE6\7来讲,它不仅不支持XDR跨域,也不支持XHR跨域,所以只能够采取jsonp-polling的方式。

    (本文同步发布于:http://www.52im.net/thread-338-1-1.html

    作者:Jack Jiang (点击作者姓名进入Github) 
    出处:http://www.52im.net/space-uid-1.html 

    展开全文
  • 即时通讯

    2019-08-23 11:44:03
    国内主流IM即时通讯平台综合评测 1 网易云信 https://netease.im/ 2 环信 https://www.easemob.com/ 3 融云 https://www.rongcloud.cn/ 4 极光 https://www.jiguang.cn/ 5 容联云通讯 https://www.yuntongxun.com/ 6...

    国内主流IM即时通讯平台综合评测

    1 网易云信

    https://netease.im/

    2 环信

    https://www.easemob.com/

    3 融云

    https://www.rongcloud.cn/

    4 极光

    https://www.jiguang.cn/

    5 容联云通讯

    https://www.yuntongxun.com/

    6 LeanCloud

    https://leancloud.cn/

    7 亲加通讯云

    http://www.gotye.com.cn/

    8 aurora-imui 通用的即时通讯(IM)UI 库

    Aurora IMUI 是个通用的即时通讯(IM)UI 库,不特定于任何 IM SDK。

    本 UI 库提供了消息列表、输入视图等常用组件,支持常见的消息类型:文字、图片、语音、视频等。默认包含多套界面风格,也能根据自己的需要自定义。

    项目地址:https://github.com/jpush/aurora-imui/blob/master/README_zh.md

    9 国内主流IM即时通讯平台综合评测

    相关参考资料来源于 企业服务汇
    http://www.chiefmore.com/yuntongxun/3643.jhtml

    服务平台 IM即时通讯功能服务与性能 IM即时通讯功能接入与部署方式 IM即时通讯功能收费
    环信 目前环信可以提供比较完善的人与人之间的聊天与消息功能,不过在人与智能机器人聊天方面仅支持最基础的机器人服务,适用于在线客服类产品,在这一点上与容联云通讯和网易云信相比有一定的不足。 在这四家服务商中,环信唯一提供React Native demo接口来方便企业集成实现其IM功能,这对于一些使用JavaScript和React来开发原生的iOS和Android产品应用的企业来说十分友好,它可以帮助企业有效提升产品IM功能的开发效率;另外环信支持上述全部的公有云、私有云、专有云、托管云以及海外部署方式,企业在部署时面临的选择性非常灵活 目前环信、容联云通讯、极光推送、网易云信这四家云通讯平台,在IM即时通讯功能的收费价格方面,均按照基础IM功能(通常按照产品日活数收费)、增值IM功能(最典型的是实时音视频通话功能,按照使用分钟数收费)以及扩展IM功能分别进行收费。企业在选购时,需要根据自己的产品类型、实际需要的IM即时通讯功能以及实际的产品日活数来进行综合选择。
    容联云通讯 目前容联云通讯提供非常丰富的聊天与消息功能支持人与智能机器之间的单聊、群聊以及闲聊语料、天气查询等个性化聊天功能,可有效提升用户使用体验和产品活跃度;不过容联云通讯在性能水平方面,目前暂不支持智能反垃圾服务和敏感词过滤服务,企业对于用户在聊天过程中产生的消息可能无法做到有效的实时监控,从而不利于企业构建良好的网络聊天环境 目前容联云通讯仅支持最基础的接入方式,并且还支持专有云部署方式,与其他服务商相比并无特别之处。 目前环信、容联云通讯、极光推送、网易云信这四家云通讯平台,在IM即时通讯功能的收费价格方面,均按照基础IM功能(通常按照产品日活数收费)、增值IM功能(最典型的是实时音视频通话功能,按照使用分钟数收费)以及扩展IM功能分别进行收费。企业在选购时,需要根据自己的产品类型、实际需要的IM即时通讯功能以及实际的产品日活数来进行综合选择。
    极光 目前极光推送不提供聊天室功能(在视频直播类产品中经常用到)、实时音视频通话功能(在熟人社交/在线教育/在线医疗等需要直接通话才能实现有效沟通的产品中经常用到)以及人与智能机器人聊天等功能仅可以为企业提供最基础的聊天与消息功能,与其他服务商相比具有较为明显的不足,因此仅能够满足企业产品最基本的IM即时通讯需求,适用于有轻量级的IM即时通讯功能需求的企业 目前与其他三家服务商相比,极光推送支持的接入与部署方式均是最少的,在这一点上有一定的不足。 目前环信、容联云通讯、极光推送、网易云信这四家云通讯平台,在IM即时通讯功能的收费价格方面,均按照基础IM功能(通常按照产品日活数收费)、增值IM功能(最典型的是实时音视频通话功能,按照使用分钟数收费)以及扩展IM功能分别进行收费。企业在选购时,需要根据自己的产品类型、实际需要的IM即时通讯功能以及实际的产品日活数来进行综合选择。
    网易云信 与其他三家服务商相比,目前网易云信提供的IM聊天与消息功能最为完善,并且网易云信还唯一支持用户托管功能,可以帮助企业有效维护闲置的用户资料/关系;另外网易云信提供的人与智能机器人聊天功能也比较丰富,也支持人机之间的单聊、群聊等功能,适用于在线教育、企业协同办公以及智能客服等产品 目前网易云信与其他三家服务商相比,支持的接入方式最为全面,并且唯一支持macOS、Cocos-2d和Unity这三个平台系统的接入,在这一点有较大的竞争优势。 目前环信、容联云通讯、极光推送、网易云信这四家云通讯平台,在IM即时通讯功能的收费价格方面,均按照基础IM功能(通常按照产品日活数收费)、增值IM功能(最典型的是实时音视频通话功能,按照使用分钟数收费)以及扩展IM功能分别进行收费。企业在选购时,需要根据自己的产品类型、实际需要的IM即时通讯功能以及实际的产品日活数来进行综合选择。
    服务平台 IM即时通讯功能服务与性能 IM即时通讯功能接入与部署方式 IM即时通讯功能收费
    融云 与其他两家服务商相比,融云唯一提供非常完整的实时音视频通话功能,并且支持企业基于此功能实现视频会议功能,在这一点融云有着较大的竞争优势。同时融云还支持在后台为企业提供完善的后台数据统计功能,方便企业直接查看用户活跃以及费用消耗方面的数据。 与其他两家服务商相比,目前融云唯一支持macOS、Linux等平台系统的接入,对于基于这两大平台开发产品的企业非常友好,企业可以实现快速接入;另外融云也可以单独提供专有云部署服务,通过为企业提供专有的服务集群和海外数据中心,来充分满足企业的特殊需求,企业在部署方式方面选择性很强。 目前融云、LeanCloud、亲加通讯云这三家云通讯平台,在IM即时通讯功能的收费价格方面,其收费方式和收费标准各不一样,具体如下:融云主要是按照产品日活数按月收费;LeanCloud主要是按照产品日活数按天收费(其中美国和中国分别收费);而亲加通讯云因为本身仅专注于为企业的视频直播和游戏类产品提供服务,因此按照直播时的使用带宽峰值以及产品日活数分别收费。企业在选购时,需要明确自己产品的类型、需要的IM即时通讯功能以及目前比较稳定的产品日活数来综合对比进行选择。
    LeanCloud 目前LeanCloud不支持实时音视频通话功能,仅为企业提供最基础的聊天服务;同时LeanCloud在海外仅支持美国用户使用其服务,不支持全球范围内的其他国家或地区使用,与另外两家服务商相比有一定的不足。 目前LeanCloud仅支持最基础最常见的接入与部署方式,与其他两家服务商相比表现较为一般。 目前融云、LeanCloud、亲加通讯云这三家云通讯平台,在IM即时通讯功能的收费价格方面,其收费方式和收费标准各不一样,具体如下:融云主要是按照产品日活数按月收费;LeanCloud主要是按照产品日活数按天收费(其中美国和中国分别收费);而亲加通讯云因为本身仅专注于为企业的视频直播和游戏类产品提供服务,因此按照直播时的使用带宽峰值以及产品日活数分别收费。企业在选购时,需要明确自己产品的类型、需要的IM即时通讯功能以及目前比较稳定的产品日活数来综合对比进行选择。
    亲加通讯云 亲加通讯云与其他两家服务商最大的不同在于——亲加通讯云本身仅专注于为企业的视频直播以及游戏类产品提供IM即时通讯服务,这一点企业在选购时需要特别注意。另外亲加通讯云可以为企业提供最基础的聊天功能以及实时音频通话功能,通常适用于游戏类的产品 与其他两家服务商相比,目前亲加通讯云最大的不同在于——在接入方式上唯一支持Unity和Cocos-2d这两大手游引擎的接入,因此非常适用于开发游戏类产品的企业 目前融云、LeanCloud、亲加通讯云这三家云通讯平台,在IM即时通讯功能的收费价格方面,其收费方式和收费标准各不一样,具体如下:融云主要是按照产品日活数按月收费;LeanCloud主要是按照产品日活数按天收费(其中美国和中国分别收费);而亲加通讯云因为本身仅专注于为企业的视频直播和游戏类产品提供服务,因此按照直播时的使用带宽峰值以及产品日活数分别收费。企业在选购时,需要明确自己产品的类型、需要的IM即时通讯功能以及目前比较稳定的产品日活数来综合对比进行选择。
    展开全文
  • 这是一款类似微信的即时通信软件,可以实现的基本内容是收发消息和收发文件,当然可以对文件进行断点续传等操作。通讯基础是tcp Socket,但是基于RRQMSocketFramework,已经解决了粘包的问题,不仅如此,对于文件...
  • 小程序即时通讯——文本、语音输入控件(一)集成近期一直在做微信小程序,业务上要求在小程序里实现即时通讯的功能。这部分功能需要用到文本和语音输入及一些语音相关的手势操作。所以我写了一个控件来处理这些操作...
  • 一个完善的即时通讯模块需要具备哪些功能? 前言 前提需求,目前一个系统,由于产品需求,需要在系统中实现用户之间的即时通讯。那么,这个即时通讯的子系统,需要实现哪些功能呢? 功能列表 单人对单人实时聊天 ...
  • 即时通讯实现原理即时通讯(Instant Messenger,简称IM)软件多是基于TCP/IP和UDP进行通讯的,TCP/IP和UDP都是建立在更低层的IP协议上的两种通讯传输协议。前者是以数据流的形式,将传输数据经分割、打包后,通过两...
  • 即使通讯是目前移动端最为流行的通讯方式,这些通讯app开发公司也提供了...即时通讯是一个终端连网一个即时通讯网路的服务。即时通讯不同于e-mail,在于它的交谈是即时的。大部分的即时通讯服务提供了Presence Awa...
  • Android即时通讯(一)

    2019-04-26 23:47:00
    一、即时通讯 什么是即时通讯即时通讯,又称实时通讯 即时通信(Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流 即时通讯在开发中使用的...
  • 关于聊天室项目聊天室项目,也被称为即时通讯(IM)。其原理是服务器是一直在启动状态的线程,不断的从客户端(App)获取消息,收到消息后,进行类型和发送目标判断,以发送到群组或者单聊的方式,客户端收到消息后...
  • 1、即时通讯简介 即时通讯(Instant Messaging,简称IM...即时通讯按使用用途分为企业即时通讯和网站即时通讯,根据装载的对象又可分为手机即时通讯和PC即时通讯,手机即时通讯代表是微信、QQ、短信、陌陌。 在这篇文章
  • t-io: 百万级TCP长连接即时通讯框架,让天下没有难开发的即时通讯 t-io: 百万级TCP长连接即时通讯框架,让天下没有难开发的即时通讯 t-io是基于jdk aio实现的易学易用、稳定、性能强悍、将多线程运用到极致、内置...
  • 今天,在这篇文章中,我将介绍一款我自主开发的即时通讯软件flamingo(中文:火烈鸟),并开源其服务器和pc客户端代码。以此来对前几篇文章中说到的理论进行实践。 代码在github和csdn.net上各上传了一份: ...
  • 即时通讯技术简介

    2016-03-03 20:32:47
    即时通讯技术简介  即时通讯技术(IM)支持用户在线实时交谈。如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在其中输入信息并让交谈双方都看到交谈的内容。大多数常用的即时通讯发送程序都会...
  • PHP 即时通讯项目

    2020-07-30 23:33:06
    顾名思义 JJ 使用 Jabber 协议(XMPP) ,因此可以 和 包括 GTalk 在内的 其它任何 XMPP 即时通讯软件 聊天。 JJ 项目的初衷在于 演示 桌面窗口 和 PHP 多任务处理,这两大 在长期在 PHP程序员 眼中 几乎是“不可能”...
1 2 3 4 5 ... 20
收藏数 59,574
精华内容 23,829
关键字:

即时通讯