服务器端的主要工作是:从客户端获取到deviceToken,随同需要发送的内容,按IOS规定格式打包发送。
1.先获得证书(从客户端开发人员要来的)Certificates.p12 通过输入命令生成 PEM 格式
openssl pkcs12 -clcerts -nokeys -out cert.pem -in aps_development.p12 //会要求提供密码,密码为空,直接回车即可 openssl pkcs12 -nocerts -out key.pem -in aps_development.p12 //会要求设置密码和确认设置密码。这里我们用是的是123456 openssl rsa -in key.pem -out key.unencrypted.pem //会要求输入之前设置的密码 cat cert.pem key.unencrypted.pem > iostest_push_dev.pem
2. deviceToken 是一个字符串。形如:<304eb5c5 5b8a36ba a072a5ff b6e9ada9 9d5a1b8c 89f9f1c1 ac8cc3dd fe9a86fd>
注意:ios要求token的格式是binary,所以发送前需要转换下,且要跳过 空格 和 "<", ">"
3、代码实现:
#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> #include <arpa/inet.h> #include <string> using namespace std; // 证书文件 #define CERTFILE "cert/aps_developer_identity.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 bytes void 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(string _token, string _payload) { const char *token = _token.c_str(); string payloadstr = "{\"aps\":{\"alert\":\""; payloadstr += string(_payload); payloadstr += "\"}}"; cout<<"pushStr: "<< payloadstr; const char *payload = payloadstr.c_str(); 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"); return -1; } if (BIO_do_connect(conn) <= 0) { error("Error connection to remote machine"); return -1; } if (!(ssl = SSL_new(ctx))) { error("Error creating an SSL contexxt"); return -1; } SSL_set_bio(ssl, conn, conn); if (SSL_connect(ssl) <= 0) { error("Error connecting SSL object"); return -1; } printf("SSL Connection opened\n"); char tokenBytes[32]; char message[293]; unsigned long msgLength; token2bytes(token, tokenBytes); msgLength = packMessage(message, 0, tokenBytes, payload); int reValue = SSL_write(ssl, message, (int)msgLength); SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); return reValue; }