基于boost 的苹果apns消息推送实现(1)

1. 为了当时测试,做了2份C++代码实现,一份是基于boost的实现 较完整,一份是C++加Openssl实现(可以用,不少细节需要调整)

2. 本模块只涉及apns客户端部分

3. 涉及boost的主要模块有boost bind,boost asio,boost ssl,boost deadline_timer, boost 正则表达式

4. 有一点需要注意,最初使用的sslv23/sslv2/sslv3只能和apple 建立连接,但一直是handshake失败,郁闷了相当长一段时间

最后换tls连接,握手成功!

摘自apple的一段文档:

Provider-to-Service Connection Trust

Connection trust between a provider and APNs is also established through TLS peer-to-peer authentication. The procedure is similar to that described in Service-to-Device Connection Trust. The provider initiates a TLS connection, gets the server certificate from APNs, and validates that certificate. Then the provider sends its provider certificate to APNs, which validates it on its end. Once this procedure is complete, a secure TLS connection has been established; APNs is now satisfied that the connection has been made by a legitimate provider

5.苹果官方文档 https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW9

6. 这一篇文章是C++加 Opnessl原始代码实现:(正在写)

boost版本源码:

1. IS_SERVER_LOG_ERROR 宏是用来打日志,由于是项目中要用的模块,牵涉较多 ,就不提供此宏定义了**

2. 代码中接口 sub_utf8_string_of 用来截断utf8字符串,汉字是3个字节避免出现不完整截断汉字的情况

apple_apns_asio_ssl_client.hpp

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/regex.hpp>
#include <boost/timer.hpp>

#define IS_SERVER_LOG_ERROR
//void      apple_apns_reconnect_handler    (const boost::system::error_code& error );
std::string apple_apns_ssl_password_callback( std::size_t max_length, boost::asio::ssl::context::password_purpose purpose);

// 返回开始地址和字节数;
// src_utf8_str_len 源字节数,-1 等效 strlen(src_utf8_str) ;
// max_bytes        获取的字符串所占的最大字节数(不含0字符);
//                  -1 表示截取到src_utf8_str_len所对应的最后一个字符;
std::pair<const char*, int> sub_utf8_string_of(const char* src_utf8_str, int src_utf8_str_len, int max_bytes);

class apple_apns_asio_ssl_client
{
public:
    apple_apns_asio_ssl_client( boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>& endpoint_iterator )
        : socket_(io_service, context)
        , endpoint_iterator_(endpoint_iterator)
        , reconnect_callback_impl_(io_service)
        , is_handshake_ok_(false)
        , is_handshake_ing_(false)
        , is_post_recv_req_(false)
        , is_post_reconnect_req_( false)
    {
        //ptr_io_service_ = &io_service;
        memset(&response_packet,0,sizeof(response_packet));
        socket_.set_verify_mode(boost::asio::ssl::verify_peer);
        socket_.set_verify_callback(
            boost::bind(&apple_apns_asio_ssl_client::verify_certificate, this, _1, _2));
    }

    bool verify_certificate(bool preverified,
        boost::asio::ssl::verify_context& ctx)
    {
        // The verify callback can be used to check whether the certificate that is
        // being presented is valid for the peer. For example, RFC 2818 describes
        // the steps involved in doing this for HTTPS. Consult the OpenSSL
        // documentation for more details. Note that the callback is called once
        // for each certificate in the certificate chain, starting from the root
        // certificate authority.

        // In this example we will simply print the certificate‘s subject name.
        char subject_name[256];
        X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());

        X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
        IS_SERVER_LOG_COUT_TRACE(string("Verifying").append(subject_name).append("\n").c_str() );

        char issuer_name[256] = {};
        X509_NAME_oneline(X509_get_issuer_name(cert),issuer_name,256);
        IS_SERVER_LOG_COUT_TRACE(string("issuer name: ").append(issuer_name).append("\n").c_str() );

        char peer_CN[256] = {0};
        X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, peer_CN, 255);

        int err = X509_STORE_CTX_get_error(ctx.native_handle());
        if ( err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_CERT_UNTRUSTED )
        {
            return 1;           // this is ok!
        }

        return preverified;
    }

    void handle_connect(const boost::system::error_code& error,
        boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
    {
        if (!error)
        {
            socket_.async_handshake(boost::asio::ssl::stream_base::client,
                boost::bind(&apple_apns_asio_ssl_client::handle_handshake, this,
                boost::asio::placeholders::error));

        }
        else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
        {
            //socket_.lowest_layer().close();
            boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
            socket_.lowest_layer().async_connect(endpoint,
                boost::bind(&apple_apns_asio_ssl_client::handle_connect, this,
                boost::asio::placeholders::error, ++endpoint_iterator));
        }
        else
        {
            //is_handshake_ing_ = false;
            set_reconnect_handler();
            //std::cout << "logic_asio_ssl_client connect failed: " << error.message() << "\n";
            IS_SERVER_LOG_ERROR(string("logic_asio_ssl_client connect failed: ").append(error.message()).c_str());
        }
    }

    void handle_handshake(const boost::system::error_code& error)
    {
        set_handshake_ing(false);
        if ( !error )
        {
            IS_SERVER_LOG_COUT_TRACE("apple_apns_asio_ssl_client Handshake success");
            set_handshale_ok(true);
            //auto_push_message();
        }
        else
        {
            IS_SERVER_LOG_FATAL_ERROR(string("apple_apns_asio_ssl_client Handshake failed").append(error.message()).c_str());
            set_reconnect_handler();
            //reconnect();
        }
    }

    void handle_write(const boost::system::error_code& error,
        size_t bytes_transferred)
    {
        if (!error)
        {
            async_recv();
            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Write success: %d transferred") % bytes_transferred ).c_str() );
        }
        else
        {
            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Write reconnect: %s") % error.message() ).c_str() );
            //if ( error.value() == 10054 ) {   // disconnect
                set_reconnect_handler();
                //reconnect();
            //}
        }
    }

    void handle_read(const boost::system::error_code& error,
        size_t bytes_transferred)
    {

        do
        {
            if ( error )
                break;
            if ( bytes_transferred == 0)
                break;

            IS_SERVER_LOG_COUT_TRACE(
                boost::str( boost::format("apple_apns_asio_ssl_client Read %d byte") % bytes_transferred ).c_str() );

            if ( bytes_transferred == 6)        // apple response
            {
                memcpy(&response_packet.command,    reply_+0, 1);
                memcpy(&response_packet.status,     reply_+1, 1);
                memcpy(&response_packet.identityfer,reply_+2, 4);
                break;
            }

            set_post_recv_req(false);
            return;

        }while(false);

        set_reconnect_handler();

        IS_SERVER_LOG_COUT_TRACE(
            boost::str( boost::format("apple_apns_asio_ssl_client Read failed: %s ") % error.message() ).c_str() );
    }

    void set_reconnect_handler( )
    {
        if ( is_post_reconnect_req() )      {
            return ;
        }

        set_post_reconnect_req( true );
        reconnect( );
        reconnect_callback_impl_.expires_from_now(boost::posix_time::seconds(5));
        reconnect_callback_ impl_.async_wait(boost::bind(&apple_apns_asio_ssl_client::reset_reconnect_flag,
            this, boost::asio::placeholders::error));
    }

    void reset_reconnect_flag(const boost::system::error_code& error )
    {
        set_post_reconnect_req( false );

        if ( error != boost::asio::error::operation_aborted )       // Timer was not cancelled, take necessary action.
        {
            //reconnect( );
        }
        else
        {
            IS_SERVER_LOG_ERROR(string("is.server.service.apple_apns_asio_ssl_client.reset_reconnect_flag ")
                .append(error.message()).c_str());
        }
    }

public:
    void auto_push_message( )
    {
        if ( !is_handshake_ok() )
            return;

        static boost::int32_t s_cur_time = clock();
        if ( clock() - s_cur_time >= 1000000)
        {
            //s_cur_time = clock( );
            //char szPayLoad[100] = { };
            //#pragma warning(disable:4996)
            //sprintf(szPayLoad,"中文测试 apple apns test by boost asio ssl %d",time(NULL));
            //#pragma warning(default:4996)
            //async_pushmessage("d2eb47674417c05c5a6f474bddef0391242e1c4d9ea3385e8f55c427d3c7d2ed",szPayLoad,apns_push_msg_reply ,1 ,time(NULL)+24*3600);
        }
    }

    void async_connect( )
    {
        if ( is_handshake_ing( ) || is_handshake_ok( ) )
            return ;

        set_handshake_ing(true);

        boost::asio::ip::tcp::resolver::iterator  endpoint_iterator = endpoint_iterator_;
        boost::asio::async_connect(socket_.lowest_layer(),endpoint_iterator,
            boost::bind(&apple_apns_asio_ssl_client::handle_connect, this,
            boost::asio::placeholders::error, ++endpoint_iterator));

    }

    bool async_send(const char* szbuffer ,boost::int32_t buffer_size)
    {
        if ( !szbuffer || buffer_size  <= 0)
            return false;

        boost::asio::async_write(socket_ ,
            boost::asio::buffer(szbuffer/*request_*/, buffer_size),
            boost::bind(&apple_apns_asio_ssl_client::handle_write, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

        return true;
    }

    bool async_recv( )
    {
        if ( is_post_recv_req() )       {
            IS_SERVER_LOG_FATAL_ERROR( boost::str( boost::format("apple_apns_asio_ssl_client has been posted") ).c_str() );
            return false;
        }

        set_post_recv_req(true);
        socket_.async_read_some(
            boost::asio::buffer(reply_, max_len_receive),
            boost::bind(&apple_apns_asio_ssl_client::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

        return true;
    }

    void disconnect( )
    {
        set_handshale_ok(false);
        set_handshake_ing(false);
        set_post_recv_req(false);
        //socket_.get_io_service().stop();
        //socket_.get_io_service().reset();
        try
        {
            socket_.lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
            socket_.lowest_layer().close();
        }
        catch (std::exception* e)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch : ").
                append(e->what()).c_str() );
        }
        catch(boost::exception& e)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch : ").
                append( boost::diagnostic_information(e) ).c_str() );
        }
        catch(...)
        {
            IS_SERVER_LOG_ERROR( std::string("is.server.service.apple_apns_asio_ssl_client.disconnect.catch ").
                append("").c_str() );
        }

    }

    void reconnect( )
    {
        disconnect();
        async_connect();
    }

public:
    enum apns_push_msg_type
    {
        apns_push_msg_reply     = 1,        // apple will reply
        apns_push_msg_noreply   = 2,        // apple not reply
    };

private:
    enum
    {
        max_len_receive     = 1024,         // max len to receive
        mus_len_command     = 1,            // required
        mus_len_identifier  = 4,            // optional
        mus_len_expiry      = 4,            // optional
        mus_len_tokenlen    = 2,            // required
        mus_len_token       = 32,           // required must be 32
        mus_len_payloadlen  = 2,            // required
        max_len_payload     = 256,          // required The payload must not exceed 256 bytes and must not be null-terminated
        max_len_apns_msg_noreply    = mus_len_command + mus_len_tokenlen + mus_len_token + mus_len_payloadlen + max_len_payload,
        max_len_apns_msg_reply      = max_len_apns_msg_noreply + mus_len_identifier + mus_len_expiry,
    };

    struct apns_error_response_packet
    {
        char command;
        char status;
        boost::uint32_t identityfer;
    };

    boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  socket_;
    boost::asio::ip::tcp::resolver::iterator                endpoint_iterator_;
    boost::asio::deadline_timer                             reconnect_callback_impl_;

    //typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> boost_asio_ssl_stream_socket;
    //boost::shared_ptr<boost_asio_ssl_stream_socket>   socket_;
    //char                          request_[max_len_receive];
    char                            reply_[max_len_receive];
    bool                            is_handshake_ok_;           // 握手成功
    bool                            is_handshake_ing_;          // 正在握手
    bool                            is_post_recv_req_;              //
    bool                            is_post_reconnect_req_;     // 已经投递了重连请求
    apns_error_response_packet      response_packet;
    //boost::timer                  reconnect_interval_;
    //boost::asio::io_service*      ptr_io_service_;

public:
    bool is_handshake_ok       (       )    { return is_handshake_ok_;       }
    void set_handshale_ok      (bool b )    { is_handshake_ok_ = b;          }
    bool is_handshake_ing      (       )    { return is_handshake_ing_;      }
    void set_handshake_ing     (bool b )    { is_handshake_ing_ = b;         }
    bool is_post_recv_req      (       )    { return is_post_recv_req_;      }
    void set_post_recv_req     (bool b )    { is_post_recv_req_ = b;         }
    bool is_post_reconnect_req (       )    { return is_post_reconnect_req_; }
    void set_post_reconnect_req( bool b)    { is_post_reconnect_req_ = b;    }
public:

    // 字符串替换
    void fix_payload_message_symbol( string& fix_symbol, const string& strFind, const string& strReplace)
    {
        size_t nPosfind  = fix_symbol.find(strFind.c_str());
        while ( nPosfind != string::npos && nPosfind <= fix_symbol.length() )
        {
            fix_symbol.replace(nPosfind,1,strReplace.c_str());
            nPosfind = fix_symbol.find(strFind.c_str(),nPosfind + strReplace.length());
        }
    }

    // 由于截断有可能最后一个字符恰好是一个落单的反斜杠,造成不符合jason格式,推送失败
    boost::int32_t check_payload_single_backslash( char* payload_body_out, boost::int32_t payload_body_len )
    {
        if ( !payload_body_out || payload_body_len <= 0 )
            return false;

        for (int i = 0; i < payload_body_len; )
        {
            if ( payload_body_out[i] == ‘\\‘ && i+1 < payload_body_len )    // 还没到结尾
            {
                i += 2;     continue;
            }

            if ( payload_body_out[i] == ‘\\‘ && i+1 == payload_body_len )   // payload_body_out size is max_len_payload 所以 i+1 也不会越界
            {
                payload_body_out[i] = ‘\0‘;
                return payload_body_len - 1;
            }

            ++i;
        }

        return payload_body_len;
    }

    // payload 不符合jason格式,会推送失败
    typedef const std::string   my_cstdstr;
    bool conform_payload_to_jason_format(my_cstdstr& strSource , std::string& strFromated)
    {
        // App 图片格式 <img src=\"/wx\" />
        //static my_cstdstr strFace_utf8    = ansi_to_utf8("<表情>");
        static my_cstdstr strFace_utf8 = "\74\350\241\250\346\203\205\76";      // <表情>

        // App 表情格式 <img src=\"http://221.228.248.245/t/2015-3-11/15/0_60916869_20150311000000151141_2d5651_19719\" />
        //static my_cstdstr strImage_utf8   = ansi_to_utf8("<图片>");
        static my_cstdstr strImage_utf8 = "\74\345\233\276\347\211\207\76";     // <图片>

        //boost_regex_format_string(strSource, "<img src=.+? />", strFace_utf8 , strImage_utf8, strFromated);
        boost::regex regExpFace ( "<img src=.+? />" /*,boost::regex::perl*/ );
        boost::regex regExpImage( "<img src=\"http://(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}.+? />" );
        strFromated = boost::regex_replace(strSource,   regExpImage,    strImage_utf8);     // regExpImage是regExpFace的子集,先替换掉。
        strFromated = boost::regex_replace(strFromated, regExpFace,     strFace_utf8);

        // 每一个反斜杠需要多加一个反斜杠去转译
        fix_payload_message_symbol(strFromated,string("\\"),string("\\\\"));

        // 把\n 和\r都替换成 \\n 和 \\r
        fix_payload_message_symbol(strFromated,string("\n"),string("\\n"));
        fix_payload_message_symbol(strFromated,string("\r"),string("\\r"));

        return true;
    }

    bool boost_regex_format_string( my_cstdstr& strSource, my_cstdstr& regExp , my_cstdstr& strReplce, my_cstdstr& strReplce2,std::string& strFromated)
    {
        boost::smatch   matchResults;
        boost::regex    boostRegExp( regExp /*,boost::regex::perl*/ );
        bool            hasFormated = false;
        std::string     strRepl;
        strFromated                       = strSource;
        std::string::const_iterator start = strFromated.begin();
        std::string::const_iterator end   = strFromated.end();
        while (boost::regex_search(start, end, matchResults, boostRegExp))
        {
            boost::int32_t nPosBegin    = matchResults[0].first  - strFromated.begin();
            boost::int32_t nPosEnd      = matchResults[0].second - strFromated.begin();
            boost::int32_t nNumRep      = nPosEnd - nPosBegin;

            string substr(matchResults[0].first, matchResults[0].second);
            if ( boost::regex_search(substr, boost::regex("http://(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}\\.(\\d){1,3}")) )
                strRepl = strReplce2;
            else
                strRepl = strReplce;

            //cout << nPosBegin << ‘ ‘ << nPosEnd << ‘ ‘ << matchResults[0] << endl;
            if ( strRepl.length() <= nNumRep )
            {
                hasFormated = true;
                strFromated.replace(nPosBegin, nNumRep, strRepl.c_str());
            }

            //start = matchResults[0].second;       // 上个匹配的下一个字节,节约下一次匹配的时间,如果要替换源串的字串的话这种方式不可行
            start   = strFromated.begin();          // 再次重新开始匹配,对于小字符串效率影响不大
            end     = strFromated.end();            // update end   itr
            if ( start >= end ) break;
        }

        return hasFormated;
    }

    // apple push message format 1
    // type:  | command | token len(big endian) | token(binary) | payload len(big endian) |  payload |
    // buff:  |---------|-----------------------|---------------|-------------------------|----------|
    // byte:  |    1    |            2          |      32       |            2            |   <=256  |
    int async_pushmessage( const string& szDeviceToken, const string& szPayloadComment , apns_push_msg_type push_type = apns_push_msg_noreply ,
                                boost::uint32_t msgIdentityfer = 0, boost::uint32_t msgTimeExpiry = 0 )
    {
        if ( !is_handshake_ok() )
            return 0;

        if ( szDeviceToken.empty() || szPayloadComment.empty())
            return 0;

        string strPayloadCommentNew;
        conform_payload_to_jason_format(szPayloadComment, strPayloadCommentNew);

        // build the payload body
        char    payload_body[max_len_payload]         = { };
        size_t  payload_body_len = build_payload_body_str_utf8(strPayloadCommentNew, payload_body);
        if ( payload_body_len <=0 || payload_body_len > max_len_payload)    {
            IS_SERVER_LOG_FATAL_ERROR( boost::str(
                boost::format("async_pushmessage_apns build_payload_body failed ! payload_body_len: %s") % payload_body_len ).c_str() );
            return 0;
        }

        char    tokenBytes[mus_len_token] = {};
        //char  message[max_len_apns_msg] = {};
        std::vector<char> vec_message;
        vec_message.reserve( max((boost::int32_t)max_len_apns_msg_reply, (boost::int32_t)max_len_apns_msg_noreply) );
        char    *message    = vec_message.data();
        char    *pointer    = vec_message.data();

        // 1. commnad
        unsigned char command = 0;
        if ( apns_push_msg_reply == push_type )
        {
            command = 1;
        }

        memcpy(pointer, &command, mus_len_command);
        pointer += mus_len_command;

        // 1.1 optional
        if ( push_type == apns_push_msg_reply )
        {
            // identityfer
            boost::uint32_t identityfer = msgIdentityfer;
            memcpy(pointer, &identityfer, mus_len_identifier);
            pointer += mus_len_identifier;

            // expiry
            boost::uint32_t tExpiry = msgTimeExpiry;
            memcpy(pointer, &tExpiry, mus_len_expiry);
            pointer += mus_len_expiry;
        }

        // 2. token len
        //size_t  tokenLen  = strlen(szDeviceToken);
        unsigned short networkTokenLen  = htons((u_short)mus_len_token);        // big endian
        memcpy(pointer, &networkTokenLen, mus_len_tokenlen);
        pointer += mus_len_tokenlen;

        // 3. token
        token2bytes(szDeviceToken.c_str(), tokenBytes);
        //strtol(szDeviceToken,(char**)(&tokenBytes),2);
        memcpy(pointer, tokenBytes, mus_len_token);
        pointer += mus_len_token;

        // 4. payload len
        unsigned short networkPayloadLen = htons((unsigned short)payload_body_len); // big endian
        memcpy(pointer, &networkPayloadLen, mus_len_payloadlen);
        pointer += mus_len_payloadlen;

        // 5. payload
        memcpy(pointer, payload_body, payload_body_len);
        pointer += payload_body_len;

        // 6. async send
        int msgLength  = 0;
        msgLength = (int)(pointer - message);
        async_send(message, msgLength);

        return msgLength;
    }

    boost::int32_t build_payload_body_str_notutf8(const char* src_not_utf8_str, char* payload_body_out )
    {
        char    payload_format[]                    = "{\"aps\":{\"alert\":\"%s\",\"badge\":1,\"sound\":\"default\"}}";
        char    payload_comment_cutoff[max_len_payload] = { };
        boost::int32_t  payload_format_len          = strlen(payload_format);
        std::string szPayloadComment_utf8           = src_not_utf8_str;
        szPayloadComment_utf8 = boost::locale::conv::between(src_not_utf8_str,"UTF-8","GBK");       //
        boost::int32_t  payload_comment_len         = szPayloadComment_utf8.length();
        boost::int32_t  payload_cutoff_len          = (payload_comment_len + payload_format_len - 2) - max_len_payload;
        boost::int32_t  payload_body_len            = 0;
        if ( payload_cutoff_len > 0)    {
    #ifdef __linux
        snprintf(payload_comment_cutoff, payload_comment_len - payload_cutoff_len, "%s",szPayloadComment_utf8.c_str());
    #else
        _snprintf(payload_comment_cutoff, payload_comment_len - payload_cutoff_len, "%s",szPayloadComment_utf8.c_str());
    #endif
            payload_body_len = max_len_payload;
        }
        else if ( payload_cutoff_len == 0)
        {
            payload_body_len = max_len_payload;
        }
        else
            memcpy(payload_comment_cutoff, szPayloadComment_utf8.c_str(), payload_comment_len);

    #ifdef __linux
        snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
    #else
        _snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
    #endif

        if ( payload_body_len <= 0) payload_body_len = strlen(payload_body_out);
        return payload_body_len;
    }

    boost::int32_t build_payload_body_str_utf8(const string& src_utf8_str, char* payload_body_out )
    {
        char    payload_format[]                    =
            "{"
                "\"aps\":"
                    "{"
                        "\"alert\":\"%s\","
                        "\"badge\":1,"
                        "\"sound\":\"default\""
                    "}"
            "}";

        char    payload_comment_cutoff[max_len_payload] = { };
        boost::int32_t  payload_format_len          = strlen(payload_format);
        boost::int32_t  payload_comment_len         = src_utf8_str.length();
        boost::int32_t  payload_cutoff_len          = (payload_comment_len + payload_format_len - 2) - max_len_payload;     // ‘%s‘ is take up 2 bytes
        boost::int32_t  payload_body_len            = 0;
        std::pair<const char*, int> pair_src_utf8_str_cutoff;
        if ( payload_cutoff_len > 0)
        {
            boost::int32_t new_comment_len          = payload_comment_len - payload_cutoff_len;
            pair_src_utf8_str_cutoff = sub_utf8_string_of(src_utf8_str.c_str(),src_utf8_str.length(), new_comment_len);
            memcpy(payload_comment_cutoff, pair_src_utf8_str_cutoff.first, pair_src_utf8_str_cutoff.second);
            // 由于截断有可能最后一个字符恰好是一个落单的反斜杠,造成不符合jason格式,推送失败。
            boost::int32_t fix_comment_len          = check_payload_single_backslash(payload_comment_cutoff, pair_src_utf8_str_cutoff.second);
            payload_body_len = payload_format_len + fix_comment_len - 2;        // ‘%s‘ is take up 2 bytes

        }
        else if ( payload_cutoff_len == 0)
        {
            memcpy(payload_comment_cutoff, src_utf8_str.c_str(), payload_comment_len);
            payload_body_len = max_len_payload;
        }
        else
        {
            memcpy(payload_comment_cutoff, src_utf8_str.c_str(), payload_comment_len);
        }

        #ifdef __linux
        snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
        #else
        _snprintf(payload_body_out, max_len_payload,payload_format, payload_comment_cutoff);
        #endif

        if ( payload_body_len <= 0) payload_body_len = strlen(payload_body_out);        // payload_body_out could null-terminated
        return payload_body_len;
    }

    void token2bytes(const char *token, char *bytes)
    {
        int val;
        while (*token)
        {
            #pragma warning(disable:4996)
            sscanf(token, "%2x", &val);
            #pragma warning(default:4996)

            *(bytes++) = (char)val;
            token += 2;
            while (*token == ‘ ‘) {
                ++token;                // skip space
            }
        }
    }
};

std::string apple_apns_ssl_password_callback( std::size_t max_length, boost::asio::ssl::context::password_purpose purpose)
{
    const char* szPasswd = "hc123";
    //const char* szPasswd =  the_app().ref_config().get_config_apple_apns_password().c_str();
    std::cout<< "password_callback password: " << szPasswd <<std::endl;
    return szPasswd;
}

// 暂未实现,为编译通过直接进行截断
std::pair<const char*, int> sub_utf8_string_of(const char* src_utf8_str, int src_utf8_str_len, int max_bytes)
{
    int nDis = src_utf8_str_len - max_bytes;
    if ( nDis >= 0 )
        return std::pair<const char*, int>(src_utf8_str,src_utf8_str_len);

    return std::pair<const char*, int>(src_utf8_str, max_bytes);
}
时间: 2024-10-30 11:43:42

基于boost 的苹果apns消息推送实现(1)的相关文章

基于C++ 的苹果apns消息推送实现(2)

1.本模块使用C++ 和 Openssl 代码 实现了一个简单的apns客户端 2.本文的姐妹篇:基于boost 的苹果apns消息推送实现(1) 3.最初使用的sslv23/sslv2/sslv3只能和apple 建立连接,但一直是handshake失败, 最后换tls连接,握手成功! original_ssl_client.h #ifndef original_ssl_client_h #define original_ssl_client_h #pragma once #include <

苹果apns消息推送机制

苹果apns消息推送机制[电薇:132乄8688乄4109][Q群780516296]声声叹!鲁能球迷被恒大踢服了 保住前三依旧是目标Space X发推秀宇航员进出臂 设计科幻美"力挺"被"断交"的台湾 马英九讽:无说服力深度|中日游泳PK中国差在哪?个别高手长期未突破日本防卫省敲定2019预算申请 总额5.2986万亿日-传软银决定不向电动汽车企业蔚来进行投资围堵P2P恶意逃废债 纳入征信"进行时"大连瓦房店市委书记被查 曾批落马前同僚教训深刻

IOS 基于APNS消息推送原理与实现(JAVA后台)--转

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

【纵横科技】基于dwr框架的reverseajax消息推送 有图有图!

原文:[纵横科技]基于dwr框架的reverseajax消息推送 有图有图! 源代码下载地址:http://www.zuidaima.com/share/1584228052847616.htm\ 网络的最初设计是不允许服务器向浏览器发起连接,所以从浏览器及时的获取数据是个棘手的问题. 反向Ajax是具备有从web服务器向浏览器异步的发送数据的能力. 实现原理是保持客服端与服务器的长连接. 这里就附上一个简单的例子咯,给大家玩玩. 本人使用的是NetBeans编辑器,有不会转到MyEclipse

转:IOS 基于APNS消息推送原理与实现(JAVA后台)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

iOS 基于APNS消息推送原理与实现(包括JAVA后台代码)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

IOS 基于APNS消息推送原理与实现(JAVA后台)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

基于Qt移动应用的消息推送服务原理与应用

说到移动应用,大家都觉得移动嘛,当然是Java和Object-c来做啦,什么推送啊,各种系统调用啊,其实不然?如果你了解Qt, 你就知道我说的不然,也有所道理. 说道几点 一.目前Android的移动的消息.通知推送 1)轮询(Pull)方式:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等.而且你还要考虑轮询的频率,如果太慢可能导致某些消息的延迟,如果太快,则会大量消耗网络带宽和电池. 2)SMS(Push)方式:在Android平

APNS消息推送实现

转自:http://blog.csdn.net/biaobiaoqi/article/details/8058503 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1. Provider:就是为指定IOS设备应用程序提供Push的服务器,(如果IOS设备的应用程序是客户端的话,那么Provider可以理解为服务端[消息的发起者]): 2. APNS:Apple Push Notification Service[苹果消息推送服务器]: 3. iPhone:用来接