Android推送的核心原理:长连接的简单实现

实际需求

移动端需要实时的获取服务器的数据

解决方案

  1. 轮询方式:应用程序开启定时的轮询,不停的向服务器请求数据。
  2. SMS push:发送二进制短信到移动终端,来达到通知终端的目的。客户端拦截这类短信,然后采取相应的操作
  3. 持久连接方式:应用程序与服务器建立一个长连接,使服务器可以随时的与手机端通信

方案分析

  • 方法一:轮询频率过高,则太过消耗性能,轮询频率低,则数据显示不及时,不可取
  • 方式二:android短信push的关键在于拦截短信,一旦拦截到了短信,可以利用android自带的特性去完成后续操作,比如可以Push notification,可以Toast,可以发起连接服务器的请求,客户端应用不在线时,可以发一个Intent启动该应用然后再进行处理。单由于是短信的方式,就势必要涉及通信收费的问题,当需要推送的消息多的时候,就会产生很大的额外开销
  • 方式三:这种方式可以比较好的解决上面两种方式的弊端,也是下文中要分析的方式

Socket

持久化连接就是所谓的长连接,现在基本上不会使用原生的Socket来实现长连接,都是使用第三方的推送服务,如极光推送、小米推送等,这里只是使用Socket来简要说明长连接的原理

概念

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个外观模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Android推送的核心原理:长连接的简单实现_第1张图片

交互如下:

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

长连接和短连接

整个通讯过程,客户端和服务端只用一个Socket对象,长期保持Socket的连接,这种称之为“长连接”;每一次请求都新建一个Socket,处理完成之后直接关闭Socket,这种称之为“短连接”。所以,其实区分长短连接就是:整个客户和服务端的通讯过程是利用一个Socket还是多个Socket进行的

我们在eclipse中新建两个Java工程SocketClient、SocketServer来来模拟客户端和服务器端

客户端:SocketClient
功能:既可以主动的向服务器端发送信息,也可以被动的接收服务器发来的消息

public class SocketClient {

    public static void main(String[] args) {
        SocketClient client = new SocketClient();
        client.start();
    }

    private void start() {
        BufferedReader inputReader = null;
        BufferedReader reader = null;
        BufferedWriter writer = null;
        Socket socket = null;

        try {
            // 连接目标
            socket = new Socket("192.168.1.101", 9898);
            // 读取socket的内容
            reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            // 写入socket的内容
            writer = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()));

            // 获取控制台输入
            inputReader = new BufferedReader(new InputStreamReader(System.in));

            // 对服务器的监听,收到来自服务器的内容
            startServerReplyListener(reader);

            // 控制台输入
            String inputContent;
            while (!(inputContent = inputReader.readLine()).equals("bye")) {
                //向服务器主动发送消息
                writer.write(inputContent+"\n");
                writer.flush();
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //注意这里的关闭socket是为了客户端出现异常的时候关闭,正常是执行try块的while()循环
                reader.close();
                writer.close();
                inputReader.close();
                socket.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void startServerReplyListener(final BufferedReader reader) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String response;
                    while ((response = reader.readLine()) != null) {
                        System.out.println(response);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

服务器:SocketServer
功能:可主动的接收客户端的信息,主动的推送消息给客户端

package com.zsk.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {

    public static void main(String[] args) {
        SocketServer socketServer = new SocketServer();
        socketServer.startServer();
    }

    private void startServer(){
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9898);
            System.out.println("服务已开启...");
            while(true){
                //当没有Socket连接时,阻塞
                socket = serverSocket.accept();
                //管理连接
                manageConnection(socket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                socket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void manageConnection(final Socket socket){
        new Thread(new Runnable() {
            @Override
            public void run() {
                BufferedReader reader = null;
                BufferedWriter writer = null;
                try {
                    System.out.println("client:"+socket.hashCode()+"已连接");
                    reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    String receivedMsg;

                    /**
                    这一段可以模拟服务器端主动推送消息,只不过太过粗糙
                    new Timer().schedule(new TimerTask() {
                        @Override
                        public void run() {

                            try {
                                writer.write("server reply: " + "服务器推送!!!" + "\n");
                                writer.flush();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                    }, 3000,3000);
                    */

                    //将接收到的数据,重新返回给客户端
                    while((receivedMsg = reader.readLine()) != null) {
                        System.out.println("client"+socket.hashCode()+receivedMsg);
                        writer.write("server reply: " + receivedMsg + "\n");
                        writer.flush();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally{
                    try {
                        reader.close();
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }   
        }).start();
    }
}

在服务器SocketServer中可以看到,每一次的连接都是新开一个线程,这就是很大的一个弊端,当有很多的连接的时候,比方说数十万的连接时,这种高并发的情况对于这种处理方法是不可取的,同时这种方式是阻塞式的,需要开线程去进行维护,考虑到这种情况,Java1.4之后新加了nio可以提供多路非阻塞式的高伸缩性网络,但是这种方式使用十分复杂,所以多是采用基于nio的第三方框架如Mina、Nety

https://www.cnblogs.com/wangcq/p/3520400.html
https://www.jianshu.com/p/b9f3f6a16911

你可能感兴趣的:(Android推送的核心原理:长连接的简单实现)