阅读量: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; } } }