1. 程式人生 > >iOS 進階開發— 原生APNS配置以及server實現(c++版本)

iOS 進階開發— 原生APNS配置以及server實現(c++版本)

關於iOS APNS的server provider 一直沒有看到關於c++版本的,不過現在的各種第三方APNS已經很成熟了,比如百度的雲推送SDK等等,這裡簡單總結一下如何實現iOS原生的APNS server provider.

第一步,生成證書,如何生成.p12證書這裡就不細說了,如果不清楚的請查閱其他文件,或者評論裡留言,如果問的多的話我再加上

openssl pkcs12 -clcerts -nokeys -out cert.pem -in Certificate.p12 
provide new password if asked. 
openssl pkcs12 -nocerts -out key.pem -in Certificate.p12 
provide new password if asked. 
cat cert.pem key.unencrypted.pem > ck.pem

測試版本要生成developer 證書,釋出的要對應的生成釋出版本的。

生成.pem證書之後,把pem證書放到你的server provider工程中去。

第二步:server provider實現(c++版本)

要實現server provider, 需要先編譯安裝openssl, 因為Apple 的APNS是基於openssl實現的。

先來看標頭檔案實現

/*
 * Auth The Croods
 */
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <winsock.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/x509.h>

// certificate
#define CERTFILE "./apns_dev.pem"
#define SSL_CTX_LOAD_VERIFY_LOCATIONS_FAILED  -1
#define BIO_DO_CONNECT_FAILED                 -2
#define SSL_GET_VERIFY_RESULT_FAILED          -3

class APNS_Croods
{
private:
    SSL_CTX                *m_pctx;
    SSL                    *m_pssl;
    const SSL_METHOD       *m_pmeth;
    X509                   *m_pserver_cert;
    EVP_PKEY               *m_pkey;
    BIO *bio;

public:
    APNS_Croods(void);
    ~APNS_Croods(void);
    int APNS_protal();
    void Reset();
    int pushmessage(const char *token, const char *payload);
    void token2bytes(const char *token, char *bytes);
};
接下來看類實現:
#include "APNS_Croods.h"
APNS_Croods::APNS_Croods(void)
{
    m_pctx            = NULL;
    m_pssl            = NULL;
    m_pmeth            = NULL;
    m_pserver_cert    = NULL;
    m_pkey            = NULL;
    bio                = NULL;
}

APNS_Croods::APNS_Croods(void)
{
    Reset();
}

int APNS_Croods::pushmessage(const char *token, const char *payload){
    char tokenBytes[32];
    char message[293];
    int msgLength;
    
    token2bytes(token, tokenBytes);
    
    unsigned char command = 0;
    size_t payloadLength = strlen(payload);
    char *pointer = message;
    unsigned short networkTokenLength = htons((u_short)32);
    unsigned short networkPayloadLength = htons((unsigned short)payloadLength);
    memcpy(pointer, &command, sizeof(unsigned char));
    pointer +=sizeof(unsigned char);   
    memcpy(pointer, &networkTokenLength, sizeof(unsigned short)); 
    pointer += sizeof(unsigned short);
    memcpy(pointer, tokenBytes, 32);
    pointer += 32;
    memcpy(pointer, &networkPayloadLength, sizeof(unsigned short));
    pointer += sizeof(unsigned short);
    memcpy(pointer, payload, payloadLength);
    pointer += payloadLength;
    msgLength = (int)(pointer - message);
    int ret = SSL_write(m_pssl, message, msgLength);

    return ret;
}

int APNS_Croods::APNS_protal()
{
    //
    char token[] = "8da412ec d60b4a8d ea08a8f5 31f0e832 ba87c072 8920cf7d 2e436f40 c367205b";
    char payload[] = "{\"aps\":{\"alert\":\"Hello world!!! message from c++\",\"badge\":1}}";    
    char host[] = "gateway.sandbox.push.apple.com:2195";

    /*
    * Lets get nice error messages
    */
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();

    /*
    * Setup all the global SSL stuff
    */
    SSL_library_init();

    m_pctx = SSL_CTX_new(SSLv23_client_method());
    if (SSL_CTX_use_certificate_chain_file(m_pctx, CERTFILE) != 1) { 
        printf("Error loading certificate from file\n");  
        return -1;
    }    
    if (SSL_CTX_use_PrivateKey_file(m_pctx, CERTFILE, SSL_FILETYPE_PEM) != 1) {   
        printf("Error loading private key from file\n");   
        return -2;
    }
    bio = BIO_new_connect(host);
    if (!bio) {  
        printf("Error creating connection BIO\n"); 
        return -3;
    }
    if (BIO_do_connect(bio) <= 0) {   
        printf("Error connection to remote machine\n");  
        return -4;
    }
    if (!(m_pssl = SSL_new(m_pctx))) {  
        printf("Error creating an SSL contexxt\n");   
        return -5;
    }

    SSL_set_bio(m_pssl, bio, bio);
    int slRc = SSL_connect(m_pssl);
    if (slRc <= 0) {
        printf("Error connecting SSL object>>%d\n", slRc);
        return -6;
    }
    int ret = pushmessage(token,payload);
  
    printf("push ret[%d]\n", ret);
    Reset();
    return 0;
}

// change deviceToken string to binary bytes
void APNS_Croods::token2bytes(const char *token, char *bytes){
    int val;
    while (*token) {
        sscanf_s(token, "%2x", &val);
        *(bytes++) = (char)val;
        token += 2;
        while (*token == ' ') {
            // skip space
            ++token;
        }
    }
}

void APNS_Croods::Reset()
{
    if(m_pssl)
    {
        SSL_shutdown(m_pssl);
        SSL_free(m_pssl);
        m_pssl = NULL;
    }
    if(m_pctx)
    {
        SSL_CTX_free(m_pctx);
        m_pctx = NULL;
    }
}

注意:
char host[] = "gateway.sandbox.push.apple.com:2195";
這是apple APNS的developer 介面,如果是釋出的話,要改成相應的釋出介面,具體官網查詢一下吧

下邊接著來看,iOS客戶端應用如何實現程式啟動時讀取通知內容:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

NSDictionary * remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    NSLog(@">>>>remoteNotification>>%@>>>>>launchOptions>>>%@", remoteNotification,launchOptions);
    if (remoteNotification!=nil) {
        NSLog(@">>>>remoteNotification.userInfo>>%@", [[remoteNotification objectForKey:@"aps"]objectForKey:@"alert"]);
        NSString *alertBody = [[remoteNotification objectForKey:@"aps"]objectForKey:@"alert"];
    }

}


簡單羅列了一下,但具體思路應該大家都能看懂了,如有不明白的,請留言