.net 8 使用 quic 协议通讯

avatar
作者
猴君
阅读量:1

debian环境安装 quic支持

# 1. 添加unstable仓库(如果您使用的是Debian的不稳定分支) sudo apt install apt-transport-https ca-certificates sudo wget -O /etc/apt/trusted.gpg.d/microsoft.gpg https://packages.microsoft.com/keys/microsoft.asc echo "deb [arch=amd64] https://packages.microsoft.com/debian/$(lsb_release -cs) prod-unstable main" | \      sudo tee /etc/apt/sources.list.d/msprod.list   # 2. 更新包列表 sudo apt update   # 3. 安装libmsquic sudo apt install libmsquic 

window环境要求

要求Windows 11、Windows Server 2022 或更新版本

代码

using System.Net.Quic; using System.Net.Security; using System.Net; using System.Runtime.Versioning; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; using System.Buffers; using System.Reflection.PortableExecutable; using System.IO.Pipelines; using System.Text;  namespace ConsoleApp1 {     internal class Program     {         [SupportedOSPlatform("linux")]         [SupportedOSPlatform("windows")]         [RequiresPreviewFeatures]         static async Task Main(string[] args)         {             // 创建 QuicListener             var listener = await QuicListener.ListenAsync(new QuicListenerOptions             {                 ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },                 ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 9999),                 ConnectionOptionsCallback = (connection, ssl, token) => ValueTask.FromResult(new QuicServerConnectionOptions()                 {                     DefaultStreamErrorCode = 0,                     DefaultCloseErrorCode = 0,                     ServerAuthenticationOptions = new SslServerAuthenticationOptions()                     {                         ApplicationProtocols = new List<SslApplicationProtocol>() { SslApplicationProtocol.Http3 },                         ServerCertificate = GenerateManualCertificate()//生成证书                     }                 })             });              _ = Task.Run(async () =>             {                 Console.WriteLine("Quic Client Running...");                  await Task.Delay(1000);                  // 连接到服务端                 var connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions                 {                     DefaultCloseErrorCode = 0,                     DefaultStreamErrorCode = 0,                     RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, 9999),                     ClientAuthenticationOptions = new SslClientAuthenticationOptions                     {                         ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },                         RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>                         {                             return true;                         }                     }                 });                  // 打开一个出站的双向流                 var stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);                 stream.ReadTimeout = 16000;                  var reader = PipeReader.Create(stream);                 var writer = PipeWriter.Create(stream);                  // 后台读取流数据                 _ = ProcessLinesAsync(stream);                  Console.WriteLine();                  // 写入数据                 for (int i = 0; i < 7; i++)                 {                     await Task.Delay(2000);                      var message = $"Hello Quic {i} \n";                      Console.Write("Send -> " + message);                      await writer.WriteAsync(Encoding.UTF8.GetBytes(message));                 }                  await writer.CompleteAsync();                  Console.ReadKey();             });              var connection = await listener.AcceptConnectionAsync();              Console.WriteLine($"Client [{connection.RemoteEndPoint}]: connected");              var stream = await connection.AcceptInboundStreamAsync();              Console.WriteLine($"Stream [{stream.Id}]: created");              await ProcessLinesAsync(stream);         }          // 处理流数据         [SupportedOSPlatform("linux")]         [SupportedOSPlatform("windows")]         [RequiresPreviewFeatures]         static async Task ProcessLinesAsync(QuicStream stream)         {             var reader = PipeReader.Create(stream);             var writer = PipeWriter.Create(stream);              while (true)             {                 ReadResult result = await reader.ReadAsync();                 ReadOnlySequence<byte> buffer = result.Buffer;                  while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))                 {                     // Process the line.                      ProcessLine(line);                      // Ack                      //await writer.WriteAsync(System.Text.Encoding.UTF8.GetBytes($"ack: {DateTime.Now.ToString("HH:mm:ss")} \n"));                 }                  // Tell the PipeReader how much of the buffer has been consumed.                 reader.AdvanceTo(buffer.Start, buffer.End);                  // Stop reading if there's no more data coming.                 if (result.IsCompleted)                 {                     break;                 }             }              Console.WriteLine($"Stream [{stream.Id}]: completed");              await reader.CompleteAsync();             await writer.CompleteAsync();         }          static bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)         {             // Look for a EOL in the buffer.             SequencePosition? position = buffer.PositionOf((byte)'\n');              if (position == null)             {                 line = default;                 return false;             }              // Skip the line + the \n.             line = buffer.Slice(0, position.Value);             buffer = buffer.Slice(buffer.GetPosition(1, position.Value));             return true;         }          static void ProcessLine(in ReadOnlySequence<byte> buffer)         {             foreach (var segment in buffer)             {                 Console.WriteLine("Recevied -> " + System.Text.Encoding.UTF8.GetString(segment.Span));             }              Console.WriteLine();         }          static X509Certificate2 GenerateManualCertificate()         {             X509Certificate2 cert = null;             var store = new X509Store("KestrelWebTransportCertificates", StoreLocation.CurrentUser);             store.Open(OpenFlags.ReadWrite);             if (store.Certificates.Count > 0)             {                 cert = store.Certificates[^1];                  // rotate key after it expires                 if (DateTime.Parse(cert.GetExpirationDateString(), null) < DateTimeOffset.UtcNow)                 {                     cert = null;                 }             }             if (cert == null)             {                 // generate a new cert                 var now = DateTimeOffset.UtcNow;                 SubjectAlternativeNameBuilder sanBuilder = new();                 sanBuilder.AddDnsName("localhost");                 using var ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);                 CertificateRequest req = new("CN=localhost", ec, HashAlgorithmName.SHA256);                 // Adds purpose                 req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection         {             new("1.3.6.1.5.5.7.3.1") // serverAuth          }, false));                 // Adds usage                 req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false));                 // Adds subject alternate names                 req.CertificateExtensions.Add(sanBuilder.Build());                 // Sign                 using var crt = req.CreateSelfSigned(now, now.AddDays(14)); // 14 days is the max duration of a certificate for this                 cert = new(crt.Export(X509ContentType.Pfx));                  // Save                 store.Add(cert);             }             store.Close();              var hash = SHA256.HashData(cert.RawData);             var certStr = Convert.ToBase64String(hash);             //Console.WriteLine($"\n\n\n\n\nCertificate: {certStr}\n\n\n\n"); // <-- you will need to put this output into the JS API call to allow the connection             return cert;         }     } }  

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!