欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Android 心跳包初探:理解心跳机制

最编程 2024-08-08 22:40:02
...

转自:http://blog.****.net/rabbit_in_android/article/details/50119809

在写之前,我们首先了解一下为什么android维护长连接需要心跳机制,首先我们知道,维护任何一个长连接都需要心跳机制,客户端发送一个心跳给

服务器,服务器给客户端一个心跳应答,这样就形成客户端服务器的一次完整的握手,这个握手是让双方都知道他们之间的连接是没有断开,客户端是在线

的。如果超过一个时间的阈值,客户端没有收到服务器的应答,或者服务器没有收到客户端的心跳,那么对客户端来说则断开与服务器的连接重新建立一个

连接,对服务器来说只要断开这个连接即可。那么在智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同的目的呢?原因就在于智能手机使用的

是移动无线网络,那么我们在讲长连接之前我们首先要了解无线移动网络的特点。

1.无线移动网络的特点:

        当一台智能手机连上移动网络时,其实并没有真正连接上Internet,运营商分配给手机的IP其实是运营商的内网IP,手机终端要连接上Internet还必须通过运营

商的网关进行IP地址的转换,这个网关简称为NAT(NetWork Address Translation),简单来说就是手机终端连接Internet 其实就是移动内网IP,端口,外网IP之间

相互映射。相当于在手机终端在移动无线网络这堵墙上打个洞与外面的Internet相连。原理图如下:(来源网络)



        GGSN(GateWay GPRS Support Note 网关GPRS支持节点)模块就实现了NAT功能,由于大部分的移动无线网络运营商为了减少网关NAT映射表的负荷,如

果一个链路有一段时间没有通信时就会删除其对应表,造成链路中断,正是这种刻意缩短空闲连接的释放超时,原本是想节省信道资源的作用,没想到让互联网

的应用不得以远高于正常频率发送心跳来维护推送的长连接。这也是为什么会有之前的信令风暴,微信摇收费的传言,因为这类的应用发送心跳的频率是很短的,

既造成了信道资源的浪费,也造成了手机电量的快速消耗。

2.android系统的推送和IOS的推送有什么区别:

        首先我们必须知道,所有的推送功能必须有一个客户端和服务器的长连接,因为推送是由服务器主动向客户端发送消息,如果客户端和服务器之间不存在一个长连接那么服务器是无法来主动连接客户端的。因而推送功能都是基于长连接的基础是上的。

        IOS长连接是由系统来维护的,也就是说苹果的IOS系统在系统级别维护了一个客户端和苹果服务器的长链接,IOS上的所有应用上的推送都是先将消息推送到苹果的服务器然后将苹果服务器通过这个系统级别的长链接推送到手机终端上,这样的的几个好处为:1.在手机终端始终只要维护一个长连接即可,而且由于这个长链接是系统级别的不会出现被杀死而无法推送的情况。2.省电,不会出现每个应用都各自维护一个自己的长连接。3.安全,只有在苹果注册的开发者才能够进行推送,等等。

       android的长连接是由每个应用各自维护的,但是google也推出了和苹果技术架构相似的推送框架,C2DM,云端推送功能,但是由于google的服务器不在中国境内,其他的原因你懂的。所以导致这个推送无法使用,android的开发者不得不自己去维护一个长链接,于是每个应用如果都24小时在线,那么都得各自维护一个长连接,这种电量和流量的消耗是可想而知的。虽然国内也出现了各种推送平台,但是都无法达到只维护一个长连接这种消耗的级别。

3.推送的实现方式:

一:客户端不断的查询服务器,检索新内容,也就是所谓的pull 或者轮询方式

二:客户端和服务器之间维持一个TCP/IP长连接,服务器向客户端push

三:服务器又新内容时,发送一条类似短信的信令给客户端,客户端收到后从服务器中下载新内容,也就是SMS的推送方式

苹果的推送系统和googleC2DM推送系统其实都是在系统级别维护一个TCP/IP长连接,都是基于第二种的方式进行推送的。第三种方式由于运营商没有免费开放这种信令导致了这种推送在成本上是无法接受的,虽然这种推送的方式非常的稳定,高效和及时。

如果想了解android中各种推送方式请参考这个链接:Android实现推送方式解决方案 这篇博客已经介绍的非常好了。



所谓的心跳包就是客户端定时放送简单的信息给服务器端,告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务器端,服务器端回复一个固定信息。如果服务器端几分钟后没有收到客户端信息则视客户端断开。比如有些通信软件长时间不适用,要想知道它的状态是在线还是离线,就需要心跳包,定时发包收包。

    心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活在。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。

     在TCP机制里面,本身是存在有心跳包机制的,也就是TCP选项:SO_KEEPALIVE. 系统默认是设置的2小时的心跳频率。



Socket长连接+心跳检测:

http://blog.****.net/zh724738989/article/details/42007099



心跳包的机制,其实就是传统的长连接。或许有的人知道消息推送的机制,消息推送也是一种长连接 ,是将数据有服务器端推送到客户端这边从而改变传统的“拉”的请求方式。下面我来介绍一下安卓和客户端两个数据请求的方式

       1、push  这个也就是有服务器推送到客户端这边  现在有第三方技术 比如极光推送。

       2、pull   这种方式就是客户端向服务器发送请求数据(http请求)

一、首先服务器和客户端有一次“握手”

[javascript]  view plain  copy
  1. public void connect()  
  2.       {  
  3.         LogUtil.e(TAG, "准备链接...");  
  4.         InetAddress serverAddr;  
  5.         try {  
  6.             socket = new Socket(Config.Host, Config.SockectPort);  
  7.             _connect = true;  
  8.             mReceiveThread = new ReceiveThread();  
  9.             receiveStop = false;  
  10.             mReceiveThread.start();  
  11.             LogUtil.e(TAG, "链接成功.");  
  12.   
  13.         } catch (Exception e) {  
  14.             LogUtil.e(TAG, "链接出错." + e.getMessage().toString());  
  15.             e.printStackTrace();  
  16.         }  
  17.     }  
二、下面就要开启一个线程  去不断读取服务器那边传过来的数据  采用Thread去实现

[javascript]  view plain  copy
  1. private class ReceiveThread extends Thread {  
  2.         private byte[] buf;  
  3.         private String str = null;  
  4.   
  5.         @Override  
  6.         public void run() {  
  7.             while (true) {  
  8.                 try {  
  9.                     // LogUtil.e(TAG, "监听中...:"+socket.isConnected());  
  10.                     if (socket!=null && socket.isConnected()) {  
  11.   
  12.                         if (!socket.isInputShutdown()) {  
  13.                             BufferedReader inStream = new BufferedReader(  
  14.                                     new InputStreamReader(  
  15.                                             socket.getInputStream()));  
  16.                             String content = inStream.readLine();                              
  17.                             if (content == null)  
  18.                                 continue;  
  19.                             LogUtil.e(TAG, "收到信息:" + content);  
  20.                             LogUtil.e(TAG, "信息长度:"+content.length());  
  21.                             if (!content.startsWith("CMD:"))  
  22.                                 continue;  
  23.                             int spacePos = content.indexOf(" ");  
  24.                             if (spacePos == -1)  
  25.                                 continue;  
  26.                             String cmd = content.substring(4, spacePos);  
  27. //                            String body = StringUtil.DecodeBase64(content  
  28. //                                    .substring(spacePos));  
  29.                             String body = content.substring(spacePos).trim();  
  30.                             LogUtil.e(TAG, "收到信息(CMD):" + cmd);  
  31.                             LogUtil.e(TAG, "收到信息(BODY):" + body);  
  32.                             if (cmd.equals("LOGIN"))  
  33.                            {  
  34.                                 // 登录  
  35.                                 ReceiveLogin(body);  
  36.                                 continue;  
  37.                             }  
  38.                               if (cmd.equals("KEEPLIVE")) {  
  39.                                 if (!body.equals("1")) {  
  40.                                     Log.e(TAG, "心跳时检测到异常,重新登录!");  
  41.                                     socket = null;  
  42.                                     KeepAlive();  
  43.                                 } else {  
  44.                                     Date now = Calendar.getInstance().getTime();  
  45.                                     lastKeepAliveOkTime = now;  
  46.                                 }  
  47. 上一篇: 全面解析 Websocket:原理、应用与心跳包机制,一网打尽你需要知道的一切!

    下一篇: C++ 实现TCP Socket心跳包的简易教程