Apple Push Notification Service (APNS)原理与实现方案

来源:互联网 发布:淘宝搜索排名黑科技 编辑:程序博客网 时间:2024/05/29 16:52

附上原文地址 :http://blog.csdn.net/ghosc/article/details/7897201

原理

简单的说,app要单独实现消息动态更新,一种是轮询,这对用户来说会带来额外的流量。另一种方案是push,app client和server直接保持一个长连接,有新的消息时server push给app client。

这两种通过app自身实现的“push”都会面临以下问题:

  1. 进程被关闭后无法在发送请求
  2. 大量app开启网络连接对用户的流量和电池都会带来大量消耗
  3. 通讯安全问题需要app自己提供解决方案

APNS为app开发商提供了一个统一的消息通知平台。apple和每个iOS设备之间保持一个长连接。开发商将消息发送给APNS,APNS将该消息发送到指定的iOS设备,iOS设备展示消息、启动相应的app。


从以上过程可知,为通过APNS实现消息push,一个消息需要标识发送到“哪台iOS设备”的“哪个app”。这两个分别由设备id(deviceToken)和SSL的证书文件(开启消息push的app需要配置一个SSL证书)标识。消息结构体如下:

实现方案

详细过程请参考http://mobiforge.com/developing/story/programming-apple-push-notification-services,一步步来即可。这一过程会生成文件:

  1. CertificateSigningRequest.certSigningRequest 用于生成SSL证书的中间文件,用后可删除
  2. aps_developer_identity.cer SSL证书文件,这一证书会关联app id,利用该证书可将消息发送到对应的app。
  3. [xxx].mobileprovision和上述SSL证书文件绑定的app provision文件,请安装到测试设备。

双击aps_developer_identity.cer将证书文件导入到Keychain Access。

java/php/c++需要的SSL证书和密钥生成如下:

  1. 将keychain access中的aps_developer_identity.cer导出为[xxx].p12
  2. 终端下执行:
    1
    openssl pkcs12 -in[xxx].p12 -out [xxx].pem -nodes

将最终生成的pem文件提供给后台server

后台代码示例

app client请参考上述链接中的示例。

push server:

object c版本 http://stefan.hafeneger.name/download/PushMeBabySource.zip

java版本 http://code.google.com/p/javapns/

php版本 http://code.google.com/p/apns-php/

现在后台技术主要还是c/c++吧,网上一直没找到合适的,自己实现demo,代码如下:

/** * @file main.cpp * @author Maxwin * @description TestPushServer */ #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <openssl/ssl.h>#include <openssl/rand.h>#include <openssl/bio.h>#include <openssl/err.h>#include <openssl/x509.h> // 证书文件#define CERTFILE "max.pem" SSL *ssl;SSL_CTX *ctx; voiderror(constchar *msg){    printf("[ERROR]:%s\n", msg);    exit(1);} SSL_CTX *setup_client_ctx(){    ctx = SSL_CTX_new(SSLv23_method());    if(SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1) {        error("Error loading certificate from file\n");    }    if(SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1) {        error("Error loading private key from file\n");    }    returnctx;} // 将deviceToken字符串转成对应的binary bytesvoidtoken2bytes(constchar *token, char*bytes){    intval;    while(*token) {        sscanf(token,"%2x", &val);        *(bytes++) = (char)val;         token += 2;        while(*token == ' ') { // skip space            ++token;        }    }} // 打包消息unsignedlongpackMessage(char*message, constunsigned charcommand, constchar *tokenBytes, constchar *payload){    unsignedlongpayloadLength = strlen(payload);    unsignedshortnetworkTokenLength = htons(32);    unsignedshortnetworkPayloadLength = htons(payloadLength);     memcpy(message, &command, sizeof(unsignedchar));    message += sizeof(unsignedchar);    memcpy(message, &networkTokenLength, sizeof(unsignedshort));    message += sizeof(unsignedshort);    memcpy(message, tokenBytes, 32);    message += 32;    memcpy(message, &networkPayloadLength, sizeof(unsignedshort));    message += sizeof(unsignedshort);    memcpy(message, payload, payloadLength);     returnpayloadLength + 37;} intpush(constchar *token, constchar *payload){    chartokenBytes[32];    charmessage[293];    unsignedlongmsgLength;     token2bytes(token, tokenBytes);    msgLength = packMessage(message, 0, tokenBytes, payload);    returnSSL_write(ssl, message, (int)msgLength);} intmain (intargc, constchar * argv[]){    chartoken[] = "2b2474e5 ac7670f3 08fabf3a 9c1d1295 ed50e9aa f11b941a d6e3d213 4f535408";    charpayload[] = "{\"aps\":{\"alert\":\"Hello world!!!\",\"badge\":1}}";    charpayload2[] = "{\"aps\":{\"alert\":\"Hello kitty!!!\",\"badge\":12}}";     charhost[] = "gateway.sandbox.push.apple.com:2195";     BIO *conn;     // init    SSL_library_init();    ctx = setup_client_ctx();    conn = BIO_new_connect(host);    if(!conn) {        error("Error creating connection BIO\n");    }     if(BIO_do_connect(conn) <= 0) {        error("Error connection to remote machine");    }     if(!(ssl = SSL_new(ctx))) {        error("Error creating an SSL contexxt");    }     SSL_set_bio(ssl, conn, conn);    if(SSL_connect(ssl) <= 0) {        error("Error connecting SSL object");    }     printf("SSL Connection opened\n");     // push message    intret = push(token, payload);    printf("push ret[%d]\n", ret);     // push [Hello kitty] after 5s    sleep(5);    ret = push(token, payload2);    printf("push2 ret[%d]\n", ret);     printf("Close SSL Connection\n");     SSL_shutdown(ssl);    SSL_free(ssl);    SSL_CTX_free(ctx);     return0;}

编译运行:

1
g++ main.cpp -o pushServer -lssl -lcrypto
Views: (715)


 

原创粉丝点击