附上原文地址 :http://blog.csdn.net/ghosc/article/details/7897201
原理
简单的说,app要单独实现消息动态更新,一种是轮询,这对用户来说会带来额外的流量。另一种方案是push,app client和server直接保持一个长连接,有新的消息时server push给app client。
这两种通过app自身实现的“push”都会面临以下问题:
- 进程被关闭后无法在发送请求
- 大量app开启网络连接对用户的流量和电池都会带来大量消耗
- 通讯安全问题需要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,一步步来即可。这一过程会生成文件:
- CertificateSigningRequest.certSigningRequest 用于生成SSL证书的中间文件,用后可删除
- aps_developer_identity.cer SSL证书文件,这一证书会关联app id,利用该证书可将消息发送到对应的app。
- [xxx].mobileprovision和上述SSL证书文件绑定的app provision文件,请安装到测试设备。
双击aps_developer_identity.cer将证书文件导入到Keychain Access。
java/php/c++需要的SSL证书和密钥生成如下:
- 将keychain access中的aps_developer_identity.cer导出为[xxx].p12
- 终端下执行:
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; void
error(const
char
*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"); } return
ctx;} //
将deviceToken字符串转成对应的binary bytesvoid
token2bytes(const
char
*token, char
*bytes){ int
val; while
(*token) { sscanf(token,
"%2x",
&val); *(bytes++)
= (char)val; token
+= 2; while
(*token == '
')
{ //
skip space ++token; } }} //
打包消息unsigned
long
packMessage(char
*message, const
unsigned char
command, const
char
*tokenBytes, const
char
*payload){ unsigned
long
payloadLength = strlen(payload); unsigned
short
networkTokenLength = htons(32); unsigned
short
networkPayloadLength = htons(payloadLength); memcpy(message,
&command, sizeof(unsigned
char)); message
+= sizeof(unsigned
char); memcpy(message,
&networkTokenLength, sizeof(unsigned
short)); message
+= sizeof(unsigned
short); memcpy(message,
tokenBytes, 32); message
+= 32; memcpy(message,
&networkPayloadLength, sizeof(unsigned
short)); message
+= sizeof(unsigned
short); memcpy(message,
payload, payloadLength); return
payloadLength + 37;} int
push(const
char
*token, const
char
*payload){ char
tokenBytes[32]; char
message[293]; unsigned
long
msgLength; token2bytes(token,
tokenBytes); msgLength
= packMessage(message, 0, tokenBytes, payload); return
SSL_write(ssl, message, (int)msgLength);} int
main (int
argc, const
char
* argv[]){ char
token[] = "2b2474e5
ac7670f3 08fabf3a 9c1d1295 ed50e9aa f11b941a d6e3d213 4f535408"; char
payload[] = "{\"aps\":{\"alert\":\"Hello
world!!!\",\"badge\":1}}"; char
payload2[] = "{\"aps\":{\"alert\":\"Hello
kitty!!!\",\"badge\":12}}"; char
host[] = "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 int
ret = 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); return
0;}
编译运行:
1
|
g++ main.cpp -o pushServer -lssl -lcrypto
|