using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Net; using System.Timers; using PushSharp; using PushSharp.Apple; namespace ConsoleApplication1 { //class Program //{ // static void Main(string[] args) // { // X509Certificate2 cert = new X509Certificate2("123.p12", "8dbmds"); // X509CertificateCollection certificate = new X509CertificateCollection(); // certificate.Add(cert); // //发布模式, 主机地址是 gateway.push.apple.com // //开发模式, 主机地址是 gateway.sandbox.push.apple.com // TcpClient client = new TcpClient("gateway.sandbox.push.apple.com", 2195); // SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ServerCertificateValidationCallback), null); // //方法AuthenticateAsClient()可能会引起异常,我们需要try..catch..起来 // try // { // //SslStream参考 // //https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2 // sslStream.AuthenticateAsClient("gateway.sandbox.push.apple.com", certificate, SslProtocols.Default, false); // } // catch (Exception e) // { // Console.WriteLine("Exception Message: {0} ", e.Message); // sslStream.Close(); // } // //把值赋给payload // PushNotificationPayload payload = new PushNotificationPayload(); // payload.deviceToken = "b606c95c4b3956de89d67ecfa02811ebc5d1a09c6f832c3be67b1d9554a66683"; // payload.badge = 56789; // payload.sound = "default"; // payload.message = "This message was pushed by C# platform."; // Push(payload, sslStream); // } // //这是握手后的回调 // private static bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) // { // if (sslPolicyErrors == SslPolicyErrors.None) // { // Console.WriteLine("Specified Certificate is accepted."); // return true; // } // Console.WriteLine("Certificate error : {0} ", sslPolicyErrors); // return false; // } // //然后调用Push()方法 // public static void Push(PushNotificationPayload payload, SslStream _sslStream) // { // string payloadStr = payload.PushPayload(); // string deviceToken = payload.deviceToken; // MemoryStream memoryStream = new MemoryStream(); // BinaryWriter writer = new BinaryWriter(memoryStream); // writer.Write((byte)0); //The command // writer.Write((byte)0); //deviceId长度的第一个字节,大头字节序第一个字节 // writer.Write((byte)32); //deviceId长度,大头字节序第二个字节 // //方法DataWithDeviceToken() , [具体看源码](https://github.com/Victor-Studio/PushNotification) // byte[] deviceTokenBytes = DataWithDeviceToken(deviceToken.ToUpper()); // writer.Write(deviceTokenBytes); // writer.Write((byte)0); //payload的长度的第一个字节,大头字节序的第一个字节 // writer.Write((byte)payloadStr.Length); //payload的长度,大头字节序的第二个字节 // byte[] bytes = Encoding.UTF8.GetBytes(payloadStr); // writer.Write(bytes); // writer.Flush(); // _sslStream.Write(memoryStream.ToArray()); // _sslStream.Flush(); // Thread.Sleep(3000); // //方法ReadMessage() , 具体看[本库的源码](https://github.com/Victor-Studio/PushNotification) // string result = ReadMessage(_sslStream); // Console.WriteLine("server said: " + result); // _sslStream.Close(); // } // static string ReadMessage(SslStream sslStream) // { // // Read the message sent by the client. // // The client signals the end of the message using the // // "<EOF>" marker. // byte[] buffer = new byte[2048]; // StringBuilder messageData = new StringBuilder(); // int bytes = -1; // do // { // // Read the client's test message. // bytes = sslStream.Read(buffer, 0, buffer.Length); // // Use Decoder class to convert from bytes to UTF8 // // in case a character spans two buffers. // Decoder decoder = Encoding.UTF8.GetDecoder(); // char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)]; // decoder.GetChars(buffer, 0, bytes, chars, 0); // messageData.Append(chars); // // Check for EOF or an empty message. // if (messageData.ToString().IndexOf("<EOF>") != -1) // { // break; // } // } // while (bytes != 0); // return messageData.ToString(); // } //} class Program { public static DateTime? Expiration { get; set; } public static readonly DateTime DoNotStore = DateTime.MinValue; private static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static string DeviceToken = "手机标识码"; public const int DEVICE_TOKEN_BINARY_SIZE = 32; public const int DEVICE_TOKEN_STRING_SIZE = 64; public const int MAX_PAYLOAD_SIZE = 256; private static X509Certificate certificate; private static X509CertificateCollection certificates; static void Main(string[] args) { Console.WriteLine("1"); // //发布模式, 主机地址是 gateway.push.apple.com // //开发模式, 主机地址是 gateway.sandbox.push.apple.com string hostIP = "gateway.sandbox.push.apple.com";// int port = 2195; string password = "证书密码";// string certificatepath = "证书路径.p12";//bin/debug string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, certificatepath); byte[] b = System.IO.File.ReadAllBytes(p12Filename); certificate = new X509Certificate2(System.IO.File.ReadAllBytes(p12Filename), password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); certificates = new X509CertificateCollection(); certificates.Add(certificate); Console.WriteLine("2"); TcpClient apnsClient = new TcpClient(); apnsClient.Connect(hostIP, port); Console.WriteLine("3"); SslStream apnsStream = new SslStream(apnsClient.GetStream(), false, new RemoteCertificateValidationCallback(validateServerCertificate), new LocalCertificateSelectionCallback(selectLocalCertificate)); try { Console.WriteLine("4"); //APNs已不支持SSL 3.0 apnsStream.AuthenticateAsClient(hostIP, certificates, System.Security.Authentication.SslProtocols.Tls, false); Console.WriteLine("5"); } catch (System.Security.Authentication.AuthenticationException ex) { Console.WriteLine("error+" + ex.Message); } if (!apnsStream.IsMutuallyAuthenticated) { Console.WriteLine("error:Ssl Stream Failed to Authenticate!"); } if (!apnsStream.CanWrite) { Console.WriteLine("error:Ssl Stream is not Writable!"); } Console.WriteLine("6"); //Console.WriteLine(ReadMessage(apnsStream)); Console.WriteLine("7"); //Console.WriteLine(ReadMessage(apnsStream)); Byte[] message = ToBytes(); apnsStream.Write(message); Console.WriteLine("8"); Console.WriteLine(ReadMessage(apnsStream)); Console.ReadLine(); } static string ReadMessage(SslStream sslStream) { // Read the message sent by the client. // The client signals the end of the message using the // "<EOF>" marker. byte[] buffer = new byte[2048]; StringBuilder messageData = new StringBuilder(); int bytes = -1; do { // Read the client's test message. bytes = sslStream.Read(buffer, 0, buffer.Length); // Use Decoder class to convert from bytes to UTF8 // in case a character spans two buffers. Decoder decoder = Encoding.UTF8.GetDecoder(); char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)]; decoder.GetChars(buffer, 0, bytes, chars, 0); messageData.Append(chars); // Check for EOF or an empty message. if (messageData.ToString().IndexOf("<EOF>") != -1) { break; } } while (bytes != 0); return messageData.ToString(); } public static byte[] ToBytes() { // Without reading the response which would make any identifier useful, it seems silly to // expose the value in the object model, although that would be easy enough to do. For // now we'll just use zero. int identifier = 0; byte[] identifierBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(identifier)); // APNS will not store-and-forward a notification with no expiry, so set it one year in the future // if the client does not provide it. int expiryTimeStamp = -1;//过期时间戳 if (Expiration != DoNotStore) { //DateTime concreteExpireDateUtc = (Expiration ?? DateTime.UtcNow.AddMonths(1)).ToUniversalTime(); DateTime concreteExpireDateUtc = (Expiration ?? DateTime.UtcNow.AddSeconds(20)).ToUniversalTime(); TimeSpan epochTimeSpan = concreteExpireDateUtc - UNIX_EPOCH; expiryTimeStamp = (int)epochTimeSpan.TotalSeconds; } byte[] expiry = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(expiryTimeStamp)); byte[] deviceToken = new byte[DeviceToken.Length / 2]; for (int i = 0; i < deviceToken.Length; i++) deviceToken[i] = byte.Parse(DeviceToken.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); if (deviceToken.Length != DEVICE_TOKEN_BINARY_SIZE) { Console.WriteLine("Device token length error!"); } byte[] deviceTokenSize = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Convert.ToInt16(deviceToken.Length))); //string str = "{\"aps\":{\"alert\":\"这是测试消息!!\",\"badge\":1,\"sound\":\"default\"}}"; string str = "{\"aps\" : {\"alert\" : {\"title\" : \"标题\",\"body\" : \"文本\"},\"sound\" : \"default\",\"badge\" : 0}}"; byte[] payload = Encoding.UTF8.GetBytes(str); byte[] payloadSize = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Convert.ToInt16(payload.Length))); List<byte[]> notificationParts = new List<byte[]>(); //1 Command notificationParts.Add(new byte[] { 0x01 });// Enhanced notification format command notificationParts.Add(identifierBytes); notificationParts.Add(expiry); notificationParts.Add(deviceTokenSize); notificationParts.Add(deviceToken); notificationParts.Add(payloadSize); notificationParts.Add(payload); return BuildBufferFrom(notificationParts); } private static byte[] BuildBufferFrom(IList<byte[]> bufferParts) { int bufferSize = 0; for (int i = 0; i < bufferParts.Count; i++) bufferSize += bufferParts[i].Length; byte[] buffer = new byte[bufferSize]; int position = 0; for (int i = 0; i < bufferParts.Count; i++) { byte[] part = bufferParts[i]; Buffer.BlockCopy(bufferParts[i], 0, buffer, position, part.Length); position += part.Length; } return buffer; } private static bool validateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; // Dont care about server's cert } private static X509Certificate selectLocalCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) { return certificate; } } }