C#实现APNS消息推送


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;
}
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值