【发布时间】:2015-04-01 21:39:22
【问题描述】:
下午好,
我正在尝试创建一个连接到 Apple 推送通知服务 (APNS) 并发送推送通知的套接字程序。
我想知道是否有人可以帮助我实际编写 APNS 通知。
您可以在下面看到我使用 Scott Klement 的套接字编程教程帮助我完成的程序。
可能与所有这些相关的代码在 cmets 中标记为“APNS Notification Format Begin”、“Initialize”和“Formulate message to APNS”,尽管我包含了整个程序以供参考。
我已阅读 Apple 网站 Provider Requirements 上的提供商要求,但我仍然无法让一切正常运行。
我的程序编译,当我调试它/运行我通过建立实际连接的步骤时,我“认为”这部分没问题。
我希望在 item3 数据结构中的变量 errid 中从 Apple 取回错误标识符。 Apple 在提供者要求中声明,如果出现错误,他们会返回状态代码,但该特定变量仍保留在 1077952576
还有很多我不确定的事情
1) 我已获得作为 Base64 字符串的设备令牌。该字符串长度为 40 个奇数字符,但据我了解,Apple 声明设备令牌长度应为 32 个字节。在 RPGLE 中,1 个字符不是代表 1 个字节吗?如果是这种情况,那么我不能只将我的变量令牌声明为
D token 32a
因为它会被切断?
2) 在我的变量 errid 中从 Apple 接收正确的错误标识符时,有没有人能告诉我我做错了什么?
任何帮助将不胜感激
H DFTACTGRP(*NO) BNDDIR('QC2LE')
D/copy socket_h
D/copy gskssl_h
*==============================================================*
* APNS Notification Format Begin
*==============================================================*
D request s 1000a varying
D framehdr ds
D command 3I 0 inz(2)
D framelen 10I 0
D framedta s 500a varying
D item1 ds
D itemid1 3I 0 inz(1)
D itemlen1 5I 0 inz(%size(token))
D token 64a
D item2 ds
D itemid2 3I 0 inz(2)
D itemlen2 5I 0
D payload 100a varying
D item3 ds
D itemid3 3I 0 inz(3)
D itemlen3 5I 0 inz(%size(errid))
D errid 10I 0
D item4 ds
D itemid4 3I 0 inz(4)
D itemlen4 5I 0 inz(%size(expire))
D expire 10I 0 inz(0)
D item5 ds
D itemid5 3I 0 inz(5)
D itemlen5 5I 0 inz(%size(priority))
D priority 10I 0 inz(10)
*==============================================================*
* APNS Notification Format End
*==============================================================*
D gsk_strerror PR * extproc('gsk_strerror')
D gsk_ret_value 10I 0 value
D CreateEnv PR like(gsk_handle)
D ConnSock PR 10I 0
d host 256A const
D port 10I 0 value
D UpgradeSock PR like(gsk_handle)
D SslEnv like(gsk_handle) value
D sock 10I 0 value
D CloseSsl PR
D Handle like(gsk_handle) value
D CloseSslEnv PR
D SslEnv like(gsk_handle) value
D ReportError PR
D EscapeMsg PR
D errMsg s 80A varying
D CRLF c x'0d25'
D env s like(gsk_handle)
D s s 10I 0
D connto ds likeds(sockaddr_in)
D SslSock s like(gsk_handle)
D cmd s 400A
D len s 10I 0
D bytesSent s 10I 0
D Reply s 1000A
D bytesRead s 10I 0
D left s 10I 0
D buf s *
D received s 10I 0
D dataPos s 10I 0
D wait s 1A
D rc s 10I 0
/free
// Initialize
token = 'MyDevToken';
payload = '{"aps":{"alert":"You have mail"}}';
itemlen1 = %len(payload);
framedta = item1 + item2 + item3 + item4 + item5;
framelen = %len(framedta);
request = framehdr + framedta;
// Create SSL Environment
env = CreateEnv();
If (env = *NULL);
EscapeMsg();
Endif;
// Connect a socket to an SSL server (using normal socket calls)
// NOTE: Sandbox is the development environment
s = ConnSock('gateway.sandbox.push.apple.com': 2195);
// Upgrade the socket to SSL
SSLSock = UpgradeSock(env: s);
If (SSLSock = *NULL);
EscapeMsg();
Endif;
// **** Formulate message to APNS ******
len = %len(%trimr(request));
callp gsk_secure_soc_write(SSLSock
: %addr(request)
: len
: bytesSent);
// Close everything and end the program
CloseSsl(SslSock);
CloseSslEnv(Env);
*inlr = *on;
/end-free
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CreateEnv(): Create an SSL environment for client sockets
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CreateEnv B
D CreateEnv PI like(gsk_handle)
D rc s 10I 0
D SslEnv s like(Gsk_handle)
/free
// Create an SSL environment with default values:
rc = gsk_environment_open(SslEnv);
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
return *NULL;
Endif;
// Instruct environment to use the *SYSTEM certificate store
rc = gsk_attribute_set_buffer( SslEnv
: GSK_OS400_APPLICATION_ID
: 'SUMITOMO_APNS_PUSH'
:0 );
If (rc<>GSK_OK);
errMsg = %str(gsk_strerror(rc));
gsk_environment_close( SslEnv );
return *NULL;
Endif;
//Tell the environment that this is a client connection
rc = gsk_attribute_set_enum( SslEnv
: GSK_SESSION_TYPE
: GSK_CLIENT_SESSION );
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
gsk_environment_close( SslEnv );
return *NULL;
Endif;
// Activate the new environment
rc = gsk_environment_init( SslEnv );
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
gsk_environment_close( SslEnv );
return *NULL;
Endif;
return SslEnv;
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* ConnSock(): Create a TCP Socket and connect to a host
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P ConnSock B
D ConnSock PI 10I 0
d host 256A const
D port 10I 0 value
D s s 10I 0
D addr s 10U 0
/free
// look up host
addr = inet_addr(%trim(host));
If (addr = INADDR_NONE);
p_hostent = gethostbyname(%trim(host));
If (p_hostent = *NULL);
errMsg = 'Host not found!';
EscapeMsg();
Endif;
addr = h_addr;
Endif;
// Create a socket
s = socket(AF_INET: SOCK_STREAM: IPPROTO_IP);
If (s < 0);
ReportError();
Endif;
// connect to the host
connto = *ALLx'00';
connto.sin_family = AF_INET;
connto.sin_addr = addr;
connto.sin_port = port;
If (connect(s: %addr(Connto): %size(connto)) = -1);
callp close(S);
ReportError();
Endif;
return s;
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* UpgradeSock(): Upgrade a socket to use SSL
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P UpgradeSock B
D UpgradeSock PI like(gsk_handle)
D SslEnv like(gsk_handle) value
D sock 10I 0 value
D Handle s like(Gsk_handle)
/free
rc = gsk_secure_soc_open(SslEnv: Handle);
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
return *NULL;
Endif;
rc = gsk_attribute_set_numeric_value(Handle
: GSK_HANDSHAKE_TIMEOUT
: 30 );
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
gsk_secure_soc_close(Handle);
return *NULL;
Endif;
rc = gsk_secure_soc_init( Handle );
If (rc <> GSK_OK);
errMsg = %str(gsk_strerror(rc));
gsk_secure_soc_close(Handle);
return *NULL;
Endif;
return Handle;
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CloseSsl(): Close an SSL socket
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CloseSsl B
D CloseSsl PI
D Handle like(gsk_handle) value
/free
gsk_secure_Soc_close( handle);
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CloseSslEnv(): Close SSL Environment
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CloseSslEnv B
D CloseSslEnv PI
D SslEnv like(gsk_handle) value
/free
gsk_environment_close( SslEnv );
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* EscapeMsg(): Send an escape message w/reason for SSL failure
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P EscapeMsg B
D EscapeMsg PI
D SndPgmMsg PR ExtPgm('QMHSNDPM')
D MessageID 7A Const
D QualMsgF 20A Const
D MsgData 256A Const
D MsgDtaLen 10I 0 Const
D MsgType 10A Const
D CallStkEnt 10A Const
D CallStkCnt 10I 0 Const
D MessageKey 4A
D ErrorCode 1A
D ErrorCode DS
D BytesProv 10I 0 inz(0)
D BytesAvail 10I 0 inz(0)
D wwTheKey S 4A
/free
SndPgmMsg( 'CPF9897'
: 'QCPFMSG *LIBL'
: errMsg
: %len(%trimr(errMsg))
: '*ESCAPE'
: '*CTLBDY'
: 1
: wwTheKey
: ErrorCode );
/end-free
P E
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* ReportError(): Send an escape message explaining any errors
* that occurred.
*
* This function requires binding directory QC2LE in order
* to access the __errno() function.
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P ReportError B
D ReportError PI
D get_errno PR * ExtProc('__errno')
D ptrToErrno s *
D errno s 10I 0 based(ptrToErrno)
D QMHSNDPM PR ExtPgm('QMHSNDPM')
D MessageID 7A Const
D QualMsgF 20A Const
D MsgData 1A Const
D MsgDtaLen 10I 0 Const
D MsgType 10A Const
D CallStkEnt 10A Const
D CallStkCnt 10I 0 Const
D MessageKey 4A
D ErrorCode 8192A options(*varsize)
D ErrorCode DS qualified
D BytesProv 1 4I 0 inz(0)
D BytesAvail 5 8I 0 inz(0)
D MsgKey S 4A
D MsgID s 7A
/free
ptrToErrno = get_errno();
MsgID = 'CPE' + %char(errno);
QMHSNDPM( MsgID
: 'QCPFMSG *LIBL'
: ' '
: 0
: '*ESCAPE'
: '*PGMBDY'
: 1
: MsgKey
: ErrorCode );
/end-free
P E
【问题讨论】:
-
您遇到了什么错误?你应该看看 Scott Klement 的 HTTP API,如果没有别的,也许可以借用调试日志。
-
嘿查尔斯,感谢您的回复。我目前对变量 cmd 的声明是一个字符字段。在这一点上,我只是不确定将其声明为什么。我应该为通知的每个部分(命令、帧长度、帧数据等)创建变量作为二进制字段吗?我们从未在我们的应用程序中使用过二进制字段,所以我对此并不熟悉。此外,我还查看了 Scott 的 HTTPAPI,但它似乎只适用于以 HTTPS 开头的 URL,就 SSL 而言。我尝试将它与我的代码中的苹果 URL 结合使用,并在这些行中收到错误 2)
-
是的,我不认为 HTTP API 级别的接口对你有什么好处。但在此之下,Scott 定义了一个您可以直接使用的 TCP 通信接口。参见 COMMSSLR4、COMMTCPR4。
标签: ios sockets notifications apple-push-notifications rpgle