iOS推送通知(Push Notification)的Erlang实现

本文来自:瑞仙的Erlang开发博客

原文链接:http://blog.csdn.net/zhongruixian/article/details/39528765


一、前言

关于cer/pem证书转换,网上很多资料,我这就不说了,

网上有PHP实现的Push Notification,可以参考,

为了更好的做PUSH服务定制,我这里以Erlang(gen_server)实现iOS Push Notification


二、协议


1、协议分析



表1

名称

长度

描述

Command

1 字节

固定值:2

Frame length

4 字节

上图所有的Item总字节数(注意:不是Item个数)

Frame data

变长

详见表2

表2

名称

长度

描述

Item ID

1 字节

详见表3

Item data length

2 字节

Item data 字节数

Item data

变长

详见表3

表3

Item ID

名称

长度

Item data

1

Device token

32 字节

从客户端获取的device token(64字节的hex字符串)

2

消息(payload

<= 256 字节

JSON格式,具体格式在这就不多说了,自己找吧

3

自定义的消息ID

4 字节

返回错误时,会把这个值透传回来

4

Expiration date

4 字节

UNIX时间戳

5

Priority

1 字节

传值10为立即发送

传值5为在Expiration date发送

2、协议数据打包实现

注意:网络字节序要有大端序

pack_frame(Cmd, Data) ->
    Len = byte_size(Data),
    <<Cmd:8, Len:16/big, Data/binary>>.


pack_frames(Frames) ->
    FramesBin = list_to_binary(Frames),
    FrameLen = byte_size(FramesBin),
    <<2:8/big, FrameLen:32/big, FramesBin/binary>>.


%% 16进制字符串转binary
hex_to_bin(Data) when is_binary(Data) ->
    hex_to_bin(binary_to_list(Data));
hex_to_bin(Data) ->
    hex_to_bin(Data, []).


hex_to_bin([H1, H2 | T], Rt) ->
    D1 = hex_to_oct(H1),
    D2 = hex_to_oct(H2),
    D = D1 bsl 4 + D2,
    hex_to_bin(T, [<<D:8>> | Rt]);
hex_to_bin([], Rt) ->
    list_to_binary(lists:reverse(Rt)).


hex_to_oct(N) when N >= 48, N =< 58 ->
	N - 48;
hex_to_oct(N) when N >= 65, N =< 70 ->
	N - 55;
hex_to_oct(N) when N >= 97, N =< 102 ->
	N - 87.


三、PUSH实现过程


1、SSL启动

ssl:start()

2、SSL连接(gen_server代码片段)

-record(state, { ssl_socket }).

start_link() ->
    gen_server:start_link({local, push}, ?MODULE, [], []).

init([]) ->
    {ok, #state{}}.

handle_info(connect, State) ->
    try
        TcpOptions = [binary, {packet, 0}, {nodelay, true}, {delay_send, true}, {exit_on_close, false}],
        SslOptions = [ {password, "1234"} ,{keyfile, "./dev_msttd_aps.pem"} ,{certfile, "./dev_msttd_aps.pem"},


        Options = TcpOptions ++ SslOptions,
        case ssl:connect("gateway.sandbox.push.apple.com", 2195, Options, 30000) of
            {ok, Socket} ->
                ?INFO("~npush connect OK:~n~w", [Socket]),
                {noreply, State#state{ssl_socket = Socket}};
            {error, Error} ->
                ?INFO("~w", [Error]),
                {noreply, State}
        end;
        catch T : X ->
            ?INFO("~p:~p~n~p", [T, X, erlang:get_stacktrace()]),
            {noreply, State}
    end;

3、向一个设备PUSH消息(gen_server代码片段)

handle_info({send, DeviceToken1}, State) ->
    try
        Time = util:unixtime(),
        DeviceToken = hex_to_bin(DeviceToken1),
        Payload = <<"{\"aps\":{\"alert\":\"《萌獸堂》萌獸等你來戰!\",\"sound\":\"default\"}}">>,
        PayloadLen = byte_size(Payload),
        Frame1 = pack_frame(1, DeviceToken),
        Frame2 = pack_frame(2, Payload),
        Frame3 = pack_frame(3, <<Time:32/big>>),
        Frame4 = pack_frame(4, <<Time:32/big>>),
        Frame5 = pack_frame(5, <<10:8/big>>),
        Data = pack_frames([Frame1, Frame2, Frame3, Frame4, Frame5]),
        case ssl:send(State#state.ssl_socket, Data) of
            ok -> ?INFO("send ok (~s)", [DeviceToken1]);
            Error -> ?INFO("send error:~p", [Error])
        end
        catch T : X ->
            ?INFO("~p:~p~n~p", [T, X, erlang:get_stacktrace()])
    end,
    {noreply, State};

4、错误处理(gen_server代码片段)

handle_info({ssl, _,<<8:8, StateCode:8, Id:32/big>>}, State) ->
    %% TODO: 出错啦,根据StateCode来具体处理吧
    ?INFO("Error: ~w - ~w", [StateCode, Id]),
    {noreply, State};

四、使用实例


push ! connect.
push ! {send, "8333801EBED044241D35C182F9F5CED47231D56A398AA1991060E3086699FE9F"}.

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。