前言

Apple 升级了后台推送接口,使用 http2 协议,提高了 payload 的最大大小(4k),本文介绍新版 APNS 实现方法

基于 okhttp 框架

http2 框架

不要使用 okhttp3 的 Request 类直接发送 post 请求,因为 http3 底层虽然使用了 ConnectionPool,可以设置 keep alive 和 keep alive duration,但是超过 keep alive duration,链接还是会断开,而 Apple 官方建议保持长链接!

所以最好自建 socket 长链接,使用 okhttp3 底层的 FramedConnection 类来直接发送 http2
请求,并通过定时 PING 帧来保持链接

在实际开发中,Apple 的 development 环境也非常不稳定,经常 链接超时 和 ssl 握手超时,大多数情况下只能建立一个链接,第二个连接要么连不上,要么在 ssl 握手断开

实现

Http2ApnsConnection

Http2ApnsConnection 类负责 ssl socket 链接的建立,心跳包发送以及通过 http2 multiple stream 在一个 frame 中发送多条 push notification

创建 ssl socket
private Socket createSocket() throws IOException {
    debug("connect socket");
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress(host, port));
    debug("socket connected");

    SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(
            socket, host, port, true);
    sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"});
    sslSocket.setKeepAlive(true);

    debug("start ssl handshake");
    sslSocket.startHandshake();
    debug("handshake success");

    return sslSocket;
}
创建 frame connection
private void createFramedConnection() throws IOException {
    debug("createFramedConnection");
    Socket socket = createSocket();
    framedConnection = new FramedConnection.Builder(true)
            .socket(socket)
            .protocol(Protocol.HTTP_2)
            .listener(this)
            .build();
    framedConnection.sendConnectionPreface();
    framedConnectionAlive = true;
    pingFuture = pingService.scheduleAtFixedRate(new PingTask(), 0, PING_PERIOD, TimeUnit.SECONDS);
}
发送 http2 header
private void sendHeader(String token, int contentLength) throws IOException {
    // 创建 http2 header,参考 apple apns 开发文档
    List<Header> headers = Arrays.asList(METHOD_POST_HEADER,
            SCHEME_HEADER,
            USER_AGENT_HEADER,
            CONTENT_TYPE_HEADER,
            new Header(":path", "/3/device/" + token),
            new Header("authority", host),
            new Header("content-length", String.valueOf(contentLength)));

    // 创建 stream
    framedStream = framedConnection.newStream(headers, true, true);
    framedStream.readTimeout().timeout(timeOut, TimeUnit.MILLISECONDS);
    framedStream.writeTimeout().timeout(timeOut, TimeUnit.MILLISECONDS);
}
发送 http2 data
private void sendData(byte[] bytes) throws IOException {
    Buffer buffer = new Buffer();
    buffer.write(bytes);
    framedStream.getSink().write(buffer, bytes.length);
    framedStream.getSink().flush();
}

Http2ApnsConnectionPool

Http2ApnsService

基于 netty 框架

整体代码结构和 基于 okhttp 框架的差不多,可以参考 https://github.com/black-bamb...

总结


xingpingz
122 声望64 粉丝

博学,审问,慎思,明辨,力行