服务器端Swift:制作机盖(5/6)

上次我谈到了部署和维护Linux服务器。 这次我们正在谈论如何与APNs通信门户进行通信。

什么是天篷?

Canopy具有适用于macOS和iOS的应用程序,可在此处使用。

APN

苹果推送通知服务。 为什么“ s”是小写的? 没人知道。

该服务是Internet上的黑匣子。 您的服务器安全地连接到该服务器,并发送小型JSON负载,每个负载均用于特定设备。 苹果然后将其发送到设备。 如果设备没有通电,它们将“保持”一会儿,如果有电,它将立即到达。

该连接是一个持久的HTTP2连接,HTTP2仍然是一个很新的东西,并且没有多少Internet使用它。 URLSession严格支持它,但它不会保持连接打开。 尽管有些人确实将URLSession用于APN,但您应注意:Apple承诺他们(连续打开和关闭许多连接)将其视为文字攻击,并会禁止您这样做。

实施APN

首先,请转到Apple供应门户,并获取一个APNs密钥。 它作为文件提供,将其传输到您的服务器。 确保它不以某种方式暴露在网络上,如果有人收到了,他们可以将用户通知作为您的应用发送给您,这可能会让您感到尴尬。

其次,您需要向服务器发送Apple为其客户端应用程序用户提供的设备令牌。 您之前已经看过此代码,现在您可以更好地了解其用途。

但是,除非计划将相同的通知发送给每个人,否则还需要某种方式将该令牌与您在服务器上使用的用户ID系统相关联。 因此也发送此信息。 不要忘记,您可以在两端使用相同的Codable结构! (请参阅第1部分)

完美的通知

Perfect提供了Perfect-Notifications,因此首先我使用了它。 易于设置和使用:

 导入PerfectNotifications 
 让configurationName =“ configurationName” 
让apnsKeyIdentifier =“ AB90CD56XY” //来自供应门户
让apnsTeamIdentifier =“ YX65DC09BA” //来自供应门户
让apnsPrivateKeyFilePath =“ ./APNsAuthKey_AB90CD56XY.p8”
  NotificationPusher.addConfigurationAPNS( 
名称:configurationName,
production:false,//上线时设置为“ true”!
keyId:apnsKeyIdentifier,
teamId:apnsTeamIdentifier,
privateKeyPath:apnsPrivateKeyFilePath)

然后,您可以发送通知:

  NotificationPusher(apnsTopic:“ your.bundle.id”)。pushAPNS( 
configurationName:配置名称,
deviceTokens:[deviceToken],
notificationItems:[.alertBody(“ Hello!”),.sound(“ default”)]){
在回应
打印(“ \(响应)”)
}

如果这是一项测试,请通过打印声明从电话应用程序中获取令牌并对其进行硬编码。 经过测试之后,您将需要开始存储和从数据库中获取令牌。 您将需要向服务器添加路由(请参阅第2部分),并将令牌发送到该端点,然后将该端点的该令牌存储在数据库中(请参阅第4部分)。

烦恼

几周后,我注意到,如果GitHub上连续发生许多事件,那么Perfect通知引擎将连续不断地发送推送通知,而我将不得不终止服务器。

我报告了该错误,Perfect维护人员提交修复程序的速度非常快,很遗憾,它不是完整的修复程序,我仍然可以体验到。 之后,维护人员停止与我交谈。 我本人想自己解决这个问题,但找出原因不在我自己之外,所以我开始研究其他选择。

蒸气具有APNs层,但使用它意味着将大部分蒸气拉入。 至此,我知道了与APN的通信如何很好地工作,并且我意识到我可以自己编写,因为我只需要实现全部需求的一部分。 例如,APN支持在同一个HTTP2连接中包含多个“管道”,此数目是有限的,您必须支持知道一次使用多少个管道。 Canopy尚不需要此通知,并且可能一段时间不会,相反,我一次只能发送一个通知,但是我的测试表明,我仍然可以这种方式每秒发送近600条通知。 (Apple声称,如果您的代码有效运行,您每秒可以发送5,000多个)。 同样,先进的APNs引擎将根据需要与Apple建立新的连接,我只需要一个。

重新发明轮子

RtW还不错。 自己动手时学到的东西最多。

首先,我需要一个HTTP2库。 完美自己做。 蒸气使用libcurl。 尽管我需要的libcurl是比Ubuntu 16.04随附的更新的版本,所以我必须自己configuremake install ,并将其make install/usr/local

Perfect已经将libcurl用于其他用途,因此导入此新版本实际上是自动的(也是因为Linux没有rpath所以它将首先使用库路径中的任何一个库,并使用/usr/local )。

这是我们准备连接的方法:

 让curlHandle:UnsafeMutableRawPointer 
var url:字符串
如果生产{
url =“ https://api.push.apple.com/3/device/”
}其他{
url =“ https://api.sandbox.push.apple.com/3/device/”
}
curlHandle = curl_easy_init()

url.withCString {
var str = UnsafeMutablePointer(变异:$ 0)
curlHelperSetOptString(curlHandle,CURLOPT_URL,str)
}
  curlHelperSetOptInt(curlHandle,CURLOPT_HTTP_VERSION, 
CURL_HTTP_VERSION_2_0)
curlHelperSetOptInt(curlHandle,CURLOPT_PORT,443)
curlHelperSetOptBool(curlHandle,CURLOPT_FOLLOWLOCATION,CURL_TRUE)
curlHelperSetOptBool(curlHandle,CURLOPT_POST,CURL_TRUE)
curlHelperSetOptBool(curlHandle,CURLOPT_HEADER,CURL_TRUE)

然后发送一些东西:

  var json:[String:Any] = // ... 
json.append(0)
json.withUnsafeMutableBytes {
_ = curlHelperSetOptString(curlHandle,CURLOPT_POSTFIELDS,$ 0)
}
curlHelperSetOptInt(curlHandle,CURLOPT_POSTFIELDSIZE,json.count-1)
  //标头 
var curlHeaders:UnsafeMutablePointer ?
curlHeaders = curl_slist_append(curlHeaders,“授权:不记名\(jwt)”)
curlHeaders = curl_slist_append(curlHeaders,“ User-Agent:Canopy,Codebase LLC”)
curlHeaders = curl_slist_append(curlHeaders,“ apns-topic:\(topic)”)
curlHeaders = curl_slist_append(curlHeaders,“ Accept:application / json”)
curlHeaders = curl_slist_append(curlHeaders,“ Content-Type:application / json; charset = utf-8”)
curlHelperSetOptHeaders(curlHandle,curlHeaders)
推迟{
curlHeaders.map(curl_slist_free_all)
}
  //发送 
curl_easy_perform(curlHandle)

kes。 不仅如此。 topic是您的应用程序的捆绑软件ID,对于Canopy而言,捆绑软件ID有所不同(macOS和iOS),但如果有,您可能可以对其进行硬编码。 json很奇怪,但已记录在案。 jwt是唯一的密钥,您必须每小时生成一次。 这是我的代码:

 现在让我们= Date() 
让有效负载:[字符串:任何] = [“ iss”:teamId,“ iat”:Int(now.timeIntervalSince1970)]
让jwt = JWTCreator(payload:有效负载)!
让pem =试试! PEMKey(pemPath:“ ./AuthKey_5354D789X6.p8”)
返回尝试! jwt.sign(alg:JWT.Alg.es256,键:pem,标头:[“ kid”:“ 5354D789X6”])

Perfect提供JWTCreatorPEMKey对象。 刘海是安全的,因为这里任何nil都将是开发错误。

现在,请确保您不要太频繁地创建新的jwts,Apple将关闭您的连接。 同时保持相同的curl_handle ,不要关闭它重新打开它。 您想要一个持久的连接。 如果Apple关闭它,libcurl将自动为您重新打开它,这非常方便。

您还必须做更多的事情,您需要收听Apple的响应,如果它们发送某些HTTP响应代码,则需要从数据库中删除这些令牌。 互联网认为,如果您忽略这些消息,Apple将开始限制您的连接,甚至更糟。

完成所有这些操作后,我的APNs系统已经运行了数周。 我很惊讶,但实际上我有libcurl的人要为此付出一切,他们付出了辛勤的工作。

不要自己写

如果需要APN,请选择“蒸气”。

嘿!

我是Max Howell,我想专职从事开放源码的写作,写作和全部工作。 我从事开源工作已有15年了,您可能已经使用过其中的一部分(有人自制吗?)。 我需要您的帮助才能继续,任何贡献都值​​得欢迎。 非常感谢。

https://patreon.com/mxcl

第6部分即将推出!

下周:最终想法,结论和后见之明。