APNS IOS 消息推送处理失效的Token
在开发苹果推送服务时候,要合理的控制ios设备的Token,而这个Token是由苹果服务器Apns产生的,就是每次app问Apns要Token,由苹果服务器产生的Token会记录到Apns里面,我们需要根据该Token进行制定设备的消息推送,所有Token需要我们自己去记录和管理,每个设备对应唯一的Token,而app的用户登录会有自己约束的别名,与该tokne进行关系绑定,这样按该别名进行推送,就可以找到对应的Token,进而推送到该iso设备上,对应失效的Token我们需要访问苹果的feedbackServer,拿取失效的Token,然后把本地记录的失效token进行移除。
注意事项:
1.建议和feedback服务器建立长连接,连接过于频繁有可能被当做攻击(简简单单的做一些测试时没有关系的);所有在实际开发完成后,我们基本上可以半天与feedback服务器建立一次socket连接,拿取失效的token,
2.获取的token是在上次你给你的应用发推送失败时加feedback服务的,里面会返回失败的具体时间.
3.返回的数据由三部分组成,请看下面的图:
构中包含三个部分,第一部分是一个上次发推送失败的时间戳,第二个部分是device_token的长度,第三部分就是失效的device_token
/// <summary> /// FeedbackService /// </summary> public class FeedbackService { public FeedbackService(ApnsConfiguration configuration) { Configuration = configuration; } public ApnsConfiguration Configuration { get; private set; } public delegate void FeedbackReceivedDelegate(string deviceToken, DateTime timestamp); public event FeedbackReceivedDelegate FeedbackReceived; public void Check() { var encoding = Encoding.ASCII; var certificate = Configuration.Certificate; var certificates = new X509CertificateCollection(); certificates.Add(certificate); var client = new TcpClient(Configuration.FeedbackHost, Configuration.FeedbackPort); var stream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslErrs) => { return true; }, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => { return certificate; }); stream.AuthenticateAsClient(Configuration.FeedbackHost, certificates, System.Security.Authentication.SslProtocols.Tls, false); //Set up byte[] buffer = new byte[4096]; int recd = 0; var data = new List<byte>(); //Get the first feedback recd = stream.Read(buffer, 0, buffer.Length); //Continue while we have results and are not disposing while (recd > 0) { // Add the received data to a list buffer to work with (easier to manipulate) for (int i = 0; i < recd; i++) data.Add(buffer[i]); //Process each complete notification "packet" available in the buffer while (data.Count >= (4 + 2 + 32)) // Minimum size for a valid packet { var secondsBuffer = data.GetRange(0, 4).ToArray(); var tokenLengthBuffer = data.GetRange(4, 2).ToArray(); // Get our seconds since epoch // Check endianness and reverse if needed if (BitConverter.IsLittleEndian) Array.Reverse(secondsBuffer); var seconds = BitConverter.ToInt32(secondsBuffer, 0); //Add seconds since 1970 to that date, in UTC var timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds); //flag to allow feedback times in UTC or local, but default is local if (!Configuration.FeedbackTimeIsUTC) timestamp = timestamp.ToLocalTime(); if (BitConverter.IsLittleEndian) Array.Reverse(tokenLengthBuffer); var tokenLength = BitConverter.ToInt16(tokenLengthBuffer, 0); if (data.Count >= 4 + 2 + tokenLength) { var tokenBuffer = data.GetRange(6, tokenLength).ToArray(); // Strings shouldn't care about endian-ness... this shouldn't be reversed //if (BitConverter.IsLittleEndian) // Array.Reverse (tokenBuffer); var token = BitConverter.ToString(tokenBuffer).Replace("-", "").ToLower().Trim(); // Remove what we parsed from the buffer data.RemoveRange(0, 4 + 2 + tokenLength); // Raise the event to the consumer var evt = FeedbackReceived; if (evt != null) evt(token, timestamp); } else { continue; } } //Read the next feedback recd = stream.Read(buffer, 0, buffer.Length); } try { stream.Close(); stream.Dispose(); } catch { } try { client.Client.Shutdown(SocketShutdown.Both); client.Client.Dispose(); } catch { } try { client.Close(); } catch { } } }
下面是处理逻辑:
/// <summary> /// 处理失效的Token逻辑信息 /// </summary> public class TokenProvider { private FeedbackService fs = null; private int hour = 12; private string CID; public TokenProvider(ApnsConfiguration cf, string CID) { this.fs = fs = new FeedbackService(cf); this.CID = CID; try { int hour = int.Parse(ConfigurationManager.AppSettings["ManagerTokenHour"]);//Token的控制时间 } catch { hour = 12; } } /// <summary> /// 开启处理失效的Token逻辑信息 /// </summary> /// <param name="cf"></param> public void Init() { try { Thread thsub = new Thread(new ThreadStart(() => { while (true) { try { fs.Check(); } catch (Exception ex) { LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = " fs.Check() Error! CID=" + CID, ExInfo = ex }); } Thread.Sleep(hour * 60 * 60 * 1000); } })); fs.FeedbackReceived += fs_FeedbackReceived; thsub.Start(); LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = "Open TokenProvider! CID=" + CID }); } catch (Exception ex) { LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = " Open TokenProvider Error! CID=" + CID, ExInfo = ex }); } } /// <summary> /// 处理失效的Token信息 /// </summary> /// <param name="deviceToken"></param> /// <param name="timestamp"></param> private void fs_FeedbackReceived(string deviceToken, DateTime timestamp) { try { p_DeleteToken p = new p_DeleteToken(deviceToken); if (p.ExecutionDelete()) { LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = "Delete lose token success >> " + deviceToken }); } else { LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = "Delete lose token error >> " + deviceToken, ExInfo = null }); }; } catch (Exception ex) { LogInfoProvider.config.Logs.Add(new LogClass() { LogStr = "fs_FeedbackReceived Error! CID=" + CID, ExInfo = ex }); } } }
博客内容仅代表个人观点,如发现阐述有误,麻烦指正,谢谢!