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、协议分析
名称 |
长度 |
描述 |
---|---|---|
Command |
1 字节 |
固定值:2 |
Frame length |
4 字节 |
上图所有的Item总字节数(注意:不是Item个数) |
Frame data |
变长 |
详见表2 |
名称 |
长度 |
描述 |
---|---|---|
Item ID |
1 字节 |
详见表3 |
Item data length |
2 字节 |
Item data 字节数 |
Item data |
变长 |
详见表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 ! {send, "8333801EBED044241D35C182F9F5CED47231D56A398AA1991060E3086699FE9F"}.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。