iPhone推送功能的C语言实现

来源:互联网 发布:淘宝1块钱包邮 编辑:程序博客网 时间:2024/05/17 07:39

iPhone推送功能的C语言实现。

直接上源代码:

// ippush.c// 在Linux下编译:gcc -o ippush ippush.c -lssl#ifdef _WIN32# define WIN32_LEAN_AND_MEAN# include <windows.h>#else# include <sys/types.h># include <sys/socket.h># include <netinet/in.h># include <netdb.h># include <arpa/inet.h># include <string.h># include <unistd.h># include <fcntl.h># include <errno.h>#endif#include <stdlib.h>#include <stdint.h>#include <assert.h>#include <openssl/ssl.h>#include <openssl/bio.h>#include <openssl/err.h>#define MAX_PAYLOAD_SIZE 256#define TOKEN_SIZE 32void DeviceToken2Binary(const char* sz, const int len, unsigned char* const binary, const int size) {int i, val;const char* pin;char buf[3] = {0};assert(size >= TOKEN_SIZE);for (i = 0;i < len;i++){pin = sz + i * 2;buf[0] = pin[0];buf[1] = pin[1];val = 0;sscanf(buf, "%X", &val);binary[i] = val;}return;}void DeviceBinary2Token(const unsigned char* data, const int len, char* const token, const int size) {int i;assert(size > TOKEN_SIZE * 2);for (i = 0;i < len;i++){sprintf(token + i * 2, "%02x", data[i]);}return;}void Closesocket(int socket){#ifdef _WIN32closesocket(socket);#elseclose(socket);#endif}// 初始化ssl库,Windows下初始化WinSockvoid init_openssl(){#ifdef _WIN32WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);#endifSSL_library_init();ERR_load_BIO_strings();SSL_load_error_strings();OpenSSL_add_all_algorithms();}SSL_CTX* init_ssl_context(const char* clientcert, /* 客户端的证书 */const char* clientkey, /* 客户端的Key */const char* keypwd, /* 客户端Key的密码, 如果有的话 */const char* cacert) /* 服务器CA证书 如果有的话 */{// set up the ssl contextSSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());if (!ctx) {return NULL;}// certificateif (SSL_CTX_use_certificate_file(ctx, clientcert, SSL_FILETYPE_PEM) <= 0) {return NULL;}// keyif (SSL_CTX_use_PrivateKey_file(ctx, clientkey, SSL_FILETYPE_PEM) <= 0) {return NULL;}// make sure the key and certificate file matchif (SSL_CTX_check_private_key(ctx) == 0) {return NULL;}// load ca if existif (cacert) {if (!SSL_CTX_load_verify_locations(ctx, cacert, NULL)) {return NULL;}}return ctx;}// 建立TCP连接到服务器int tcp_connect(const char* host, int port){struct hostent *hp;struct sockaddr_in addr;int sock = -1;// 解析域名if (!(hp = gethostbyname(host))) {return -1;}memset(&addr, 0, sizeof(addr));addr.sin_addr = *(struct in_addr*)hp->h_addr_list[0];addr.sin_family = AF_INET;addr.sin_port = htons(port);if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){return -1;}if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {return -1;}return sock;}// 实现SSL握手,建立SSL连接SSL* ssl_connect(SSL_CTX* ctx, int socket){SSL *ssl = SSL_new(ctx);BIO *bio = BIO_new_socket(socket, BIO_NOCLOSE);SSL_set_bio(ssl, bio, bio);if (SSL_connect(ssl) <= 0) {return NULL;}return ssl;}// 验证服务器证书// 首先要验证服务器的证书有效,其次要验证服务器证书的CommonName(CN)与我们// 实际要连接的服务器域名一致int verify_connection(SSL* ssl, const char* peername){int result = SSL_get_verify_result(ssl);if (result != X509_V_OK) {fprintf(stderr, "WARNING! ssl verify failed: %d", result);return -1;}X509 *peer;char peer_CN[256] = {0};peer = SSL_get_peer_certificate(ssl);X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 255);if (strcmp(peer_CN, peername) != 0) {fprintf(stderr, "WARNING! Server Name Doesn't match, got: %s, required: %s", peer_CN,peername);}return 0;}void json_escape(char*  str){int n;char  buf[1024];n = strlen(str) * sizeof(char) + 100;assert(n < sizeof(buf));strncpy(buf, str, n);buf[n] = '\0';char *found = buf;while (*found != '\0'){if('\\' == *found || '"' == *found || '\n' == *found || '/' == *found)*str++ = '\\';if('\n' == *found)*found = 'n';*str++ = *found++;       }*str='\0';return;}// Payload example// {"aps":{"alert" : "You got your emails.","badge" : 9,"sound" : "default"}}int build_payload(char* buffer, int* plen, char* msg, int badage, const char * sound){int n;char buf[2048];char str[2048] = "{\"aps\":{\"alert\":\"";n = strlen(str);if (msg) {strcpy(buf, msg);json_escape(buf);n = n + sprintf(str+n, "%s", buf);}n = n + sprintf(str+n, "%s%d", "\",\"badge\":", badage);if (sound) {n = n + sprintf(str+n, "%s", ",\"sound\":\"");strcpy(buf, sound);json_escape(buf);n = n + sprintf(str+n, "%s%s", buf, "\"");}strcat(str, "}}");n = strlen(str);if (n > *plen) {*plen = n;return -1;}if (n < *plen) {strcpy(buffer, str);} else {strncpy(buffer, str, *plen);}*plen = n;return *plen;}// 第一种形式的包int build_output_packet(char* buf, int buflen, /* 输出的缓冲区及长度 */const char* tokenbinary, /* 二进制的Token */char* msg, /* 要发送的消息 */int badage, /* 应用图标上显示的数字 */const char * sound) /* 设备收到时播放的声音,可以为空 */{assert(buflen >= 1 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE);char * pdata = buf;// command*pdata = 0;// token lengthpdata++;*(uint16_t*)pdata = htons(TOKEN_SIZE);// token binarypdata += 2;memcpy(pdata, tokenbinary, TOKEN_SIZE);pdata += TOKEN_SIZE;int payloadlen = MAX_PAYLOAD_SIZE;if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) < 0) {msg[strlen(msg) - (payloadlen - MAX_PAYLOAD_SIZE)] = '\0';payloadlen = MAX_PAYLOAD_SIZE;if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) <= 0) {return -1;}}*(uint16_t*)pdata = htons(payloadlen);return 1 + 2 + TOKEN_SIZE + 2 + payloadlen;}int send_message(SSL *ssl, const char* token, char* msg, int badage, const char* sound){int n;char buf[1 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE];unsigned char binary[TOKEN_SIZE];int buflen = sizeof(buf);n = strlen(token);DeviceToken2Binary(token, n, binary, TOKEN_SIZE);buflen = build_output_packet(buf, buflen, (const char*)binary, msg, badage, sound);if (buflen <= 0) {return -1;}return SSL_write(ssl, buf, buflen);}int build_output_packet_2(char* buf, int buflen, /* 缓冲区及长度 */uint32_t messageid, /* 消息编号 */uint32_t expiry, /* 过期时间 */const char* tokenbinary, /* 二进制Token */char* msg, /* message */int badage, /* badage */const char * sound) /* sound */{assert(buflen >= 1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE);char * pdata = buf;// command*pdata = 1;// messageidpdata++;*(uint32_t*)pdata = messageid;// expiry timepdata += 4;*(uint32_t*)pdata = htonl(expiry);// token lengthpdata += 4;*(uint16_t*)pdata = htons(TOKEN_SIZE);// token binarypdata += 2;memcpy(pdata, tokenbinary, TOKEN_SIZE);pdata += TOKEN_SIZE;int payloadlen = MAX_PAYLOAD_SIZE;if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) < 0) {msg[strlen(msg) - (payloadlen - MAX_PAYLOAD_SIZE)] = '\0';payloadlen = MAX_PAYLOAD_SIZE;if (build_payload(pdata + 2, &payloadlen, msg, badage, sound) <= 0) {return -1;}}*(uint16_t*)pdata = htons(payloadlen);return 1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + payloadlen;}int send_message_2(SSL *ssl, const char* token, uint32_t id, uint32_t expire, char* msg, int badage, const char* sound){int i, n;char buf[1 + 4 + 4 + 2 + TOKEN_SIZE + 2 + MAX_PAYLOAD_SIZE];unsigned char binary[TOKEN_SIZE];int buflen = sizeof(buf);n = strlen(token);printf("token length : %d, TOKEN_SIZE = %d\n token = %s\n", n, TOKEN_SIZE, token);DeviceToken2Binary(token, n, binary, TOKEN_SIZE);for (i = 0; i < TOKEN_SIZE; i++)printf("%d ", binary[i]);printf("\n");buflen = build_output_packet_2(buf, buflen, id, expire,(const char*)binary, msg, badage, sound);if (buflen <= 0) {return -1;}n = SSL_write(ssl, buf, buflen);return  n;}int main(int argc, char** argv){int  i, n;char buf[1024];char * msg = NULL;if (argc > 1) msg = argv[1];init_openssl();// 初始化Context// develop.pem是我们的证书和Key,为了方便使用,我们把证书和Key写到同一个文件中// 并取水了Key的密码保护// entrust_2048_ca.pem是苹果证书的CA,它并不在Openssl的根证书中,所以需要我们手动指定,不然会无法验证// 详细:http://www.entrust.net/developer/index.cfmSSL_CTX *ctx = init_ssl_context("develop.pem", "develop.pem", NULL, "entrust_2048_ca.pem");if (!ctx) {fprintf(stderr, "init ssl context failed: %s\n",ERR_reason_error_string(ERR_get_error()));return -1;}// 连接到测试服务器const char* host = "gateway.sandbox.push.apple.com";const int port = 2195;int socket = tcp_connect(host, port);if (socket < 0) {fprintf(stderr, "failed to connect to host %s\n",strerror(errno));return -1;}// SSL连接SSL *ssl = ssl_connect(ctx, socket);if (!ssl) {fprintf(stderr, "ssl connect failed: %s\n",ERR_reason_error_string(ERR_get_error()));Closesocket(socket);return -1;}// 验证服务器证书if (verify_connection(ssl, host) != 0) {fprintf(stderr, "verify failed\n");Closesocket(socket);return 1;}uint32_t msgid = 1;uint32_t expire = time(NULL) + 24 * 3600; // expire 1 dayprintf("main, expire = %d\n", expire);if (!msg) {msg = "hello\nThis is a test message";}// 发送一条消息const char* token = "0a8b9e7cbe68616cd5470e4c8abb4c1a3f4ba2bee4ca113ff02ae2c325948b8a";n = send_message_2(ssl, token, msgid++, expire, msg, 1, "default");printf("after send_message_2, n = %d\n", n);if (n <= 0) {fprintf(stderr, "send failed: %s\n", ERR_reason_error_string(ERR_get_error()));}else{printf("send sucessfully.\n");}// 接收苹果推送服务器发来的数据://n = recv(socket, buf, sizeof(buf), MSG_DONTWAIT); //非阻塞模式接收  n = read(socket, buf, sizeof(buf)); //阻塞模式接收printf("from APNS, n = %d\n", n);for(i=0; i<n; i++)printf("%d ", buf[i]);printf("\n");// 关闭连接SSL_shutdown(ssl);Closesocket(socket);printf("exit\n");return 0;}



0 0
原创粉丝点击