电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

基于.NET 7 的 QUIC 實現 Echo 服務的詳細過程

瀏覽:188日期:2022-06-09 10:15:58
目錄
  • 前言
  • Quic API
  • 小試牛刀

前言

隨著今年6月份的 HTTP/3 協議的正式發布,它背后的網絡傳輸協議 QUIC,憑借其高效的傳輸效率和多路并發的能力,也大概率會取代我們熟悉的使用了幾十年的 TCP,成為互聯網的下一代標準傳輸協議。

在去年 .NET 6 發布的時候,已經可以看到 HTTP/3 和 Quic 支持的相關內容了,但是當時 HTTP/3 的 RFC 還沒有定稿,所以也只是預覽功能,而 Quic 的 API 也沒有在 .NET 6 中公開。

在最新的 .NET 7 中,.NET 團隊公開了 Quic API,它是基于 MSQuic 庫來實現的 , 提供了開箱即用的支持,命名空間為 System.Net.Quic。

Quic API

下面的內容中,我會介紹如何在 .NET 中使用 Quic。

下面是 System.Net.Quic 命名空間下,比較重要的幾個類。

QuicConnection

表示一個 QUIC 連接,本身不發送也不接收數據,它可以打開或者接收多個QUIC 流。

QuicListener

用來監聽入站的 Quic 連接,一個 QuicListener 可以接收多個 Quic 連接。

QuicStream

表示 Quic 流,它可以是單向的 (QuicStreamType.Unidirectional),只允許創建方寫入數據,也可以是雙向的(QuicStreamType.Bidirectional),它允許兩邊都可以寫入數據。

小試牛刀

下面是一個客戶端和服務端應用使用 Quic 通信的示例。

1.分別創建了 QuicClient 和 QuicServer 兩個控制臺程序。

項目的版本為 .NET 7, 并且設置 EnablePreviewFeatures = true。

下面創建了一個 QuicListener,監聽了本地端口 9999,指定了 ALPN 協議版本。

Console.WriteLine("Quic Server Running...");// 創建 QuicListenervar 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()}    }) });  

因為 Quic 需要 TLS 加密,所以要指定一個證書,GenerateManualCertificate 方法可以方便地創建一個本地的測試證書。

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 expiresif (DateTime.Parse(cert.GetExpirationDateString(), null) < DateTimeOffset.UtcNow){    cert = null;}    }    if (cert == null)    {// generate a new certvar 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 purposereq.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection{    new("1.3.6.1.5.5.7.3.1") // serverAuth}, false));// Adds usagereq.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false));// Adds subject alternate namesreq.CertificateExtensions.Add(sanBuilder.Build());// Signusing var crt = req.CreateSelfSigned(now, now.AddDays(14)); // 14 days is the max duration of a certificate for thiscert = new(crt.Export(X509ContentType.Pfx));// Savestore.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;}

阻塞線程,直到接收到一個 Quic 連接,一個 QuicListener 可以接收多個 連接。

var connection = await listener.AcceptConnectionAsync();Console.WriteLine($"Client [{connection.RemoteEndPoint}]: connected");

接收一個入站的 Quic 流, 一個 QuicConnection 可以支持多個流。

var stream = await connection.AcceptInboundStreamAsync();Console.WriteLine($"Stream [{stream.Id}]: created");

接下來,使用 System.IO.Pipeline 處理流數據,讀取行數據,并回復一個 ack 消息。

Console.WriteLine();await ProcessLinesAsync(stream);Console.ReadKey();      // 處理流數據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)){    // 讀取行數據    ProcessLine(line);    // 寫入 ACK 消息    await writer.WriteAsync(Encoding.UTF8.GetBytes($"Ack: {DateTime.Now.ToString("HH:mm:ss")} \n"));}       reader.AdvanceTo(buffer.Start, buffer.End); if (result.IsCompleted){    break;}     }    Console.WriteLine($"Stream [{stream.Id}]: completed");    await reader.CompleteAsync();      await writer.CompleteAsync();    } bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line){     SequencePosition? position = buffer.PositionOf((byte)"\n");    if (position == null)    {line = default;return false;    }         line = buffer.Slice(0, position.Value);    buffer = buffer.Slice(buffer.GetPosition(1, position.Value));    return true;} void ProcessLine(in ReadOnlySequence<byte> buffer){    foreach (var segment in buffer)    {Console.WriteLine("Recevied -> " + System.Text.Encoding.UTF8.GetString(segment.Span));    }    Console.WriteLine();} 

以上就是服務端的完整代碼了。

接下來我們看一下客戶端 QuicClient 的代碼。

直接使用 QuicConnection.ConnectAsync 連接到服務端。

Console.WriteLine("Quic Client Running...");await Task.Delay(3000);// 連接到服務端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); 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(); 

ProcessLinesAsync 和服務端一樣,使用 System.IO.Pipeline 讀取流數據。

async Task ProcessLinesAsync(QuicStream stream){    while (true)    {ReadResult result = await reader.ReadAsync();ReadOnlySequence<byte> buffer = result.Buffer;while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line)){     // 處理行數據    ProcessLine(line);}     reader.AdvanceTo(buffer.Start, buffer.End);      if (result.IsCompleted){    break;}    }    await reader.CompleteAsync();    await writer.CompleteAsync();} bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line){     SequencePosition? position = buffer.PositionOf((byte)"\n");    if (position == null)    {line = default;return false;    }     line = buffer.Slice(0, position.Value);    buffer = buffer.Slice(buffer.GetPosition(1, position.Value));    return true;}void ProcessLine(in ReadOnlySequence<byte> buffer){    foreach (var segment in buffer)    {Console.Write("Recevied -> " + System.Text.Encoding.UTF8.GetString(segment.Span));Console.WriteLine();    }    Console.WriteLine();}

到這里,客戶端和服務端的代碼都完成了,客戶端使用 Quic 流發送了一些消息給服務端,服務端收到消息后在控制臺輸出,并回復一個 Ack 消息,因為我們創建了一個雙向流。

程序的運行結果如下

我們上面說到了一個 QuicConnection 可以創建多個流,并行傳輸數據。

改造一下服務端的代碼,支持接收多個 Quic 流。

var cts = new CancellationTokenSource();while (!cts.IsCancellationRequested){    var stream = await connection.AcceptInboundStreamAsync();    Console.WriteLine($"Stream [{stream.Id}]: created");    Console.WriteLine();    _ = ProcessLinesAsync(stream); } Console.ReadKey();  

對于客戶端,我們用多個線程創建多個 Quic 流,并同時發送消息。

默認情況下,一個 Quic 連接的流的限制是 100,當然你可以設置 QuicConnectionOptions 的 MaxInboundBidirectionalStreams 和 MaxInboundUnidirectionalStreams 參數。

for (int j = 0; j < 5; j++){    _ = Task.Run(async () => {// 創建一個出站的雙向流var stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);       var writer = PipeWriter.Create(stream); Console.WriteLine(); await Task.Delay(2000);var message = $"Hello Quic [{stream.Id}] \n";Console.Write("Send -> " + message);await writer.WriteAsync(Encoding.UTF8.GetBytes(message));await writer.CompleteAsync();     });  } 

最終程序的輸出如下

完整的代碼可以在下面的 github 地址找到,希望對您有用!

到此這篇關于基于 .NET 7 的 QUIC 實現 Echo 服務的文章就介紹到這了,更多相關.NET 7 實現 Echo 服務內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!

標簽: ASP.NET
主站蜘蛛池模板: 「银杏树」银杏树行情价格_银杏树种植_山东程锦园林 | 齿轮减速机电机一体机_齿轮减速箱加电机一体化-德国BOSERL蜗轮蜗杆减速机电机生产厂家 | 气动绞车,山东气动绞车,气动绞车厂家-烟台博海石油机械有限公司 气动隔膜泵厂家-温州永嘉定远泵阀有限公司 | 科普仪器菏泽市教育教学仪器总厂| 硅PU球场、篮球场地面施工「水性、环保、弹性」硅PU材料生产厂家-广东中星体育公司 | 【直乐】河北石家庄脊柱侧弯医院_治疗椎间盘突出哪家医院好_骨科脊柱外科专业医院_治疗抽动症/关节病骨伤权威医院|排行-直乐矫形中医医院 | 哈希PC1R1A,哈希CA9300,哈希SC4500-上海鑫嵩实业有限公司 | 全自动端子机|刺破式端子压接机|全自动双头沾锡机|全自动插胶壳端子机-东莞市傅氏兄弟机械设备有限公司 | 背压阀|减压器|不锈钢减压器|减压阀|卫生级背压阀|单向阀|背压阀厂家-上海沃原自控阀门有限公司 本安接线盒-本安电路用接线盒-本安分线盒-矿用电话接线盒-JHH生产厂家-宁波龙亿电子科技有限公司 | 智能终端_RTU_dcm_北斗星空自动化科技 | 福州甲醛检测-福建室内空气检测_环境检测_水质检测-福建中凯检测技术有限公司 | POS机官网 - 拉卡拉POS机免费办理|官网在线申请入口 | 冷镦机-多工位冷镦机-高速冷镦机厂家-温州金诺机械设备制造有限公司 | 爱德华真空泵油/罗茨泵维修,爱发科-比其尔产品供应东莞/杭州/上海等全国各地 | 多物理场仿真软件_电磁仿真软件_EDA多物理场仿真软件 - 裕兴木兰 | 合肥白癜风医院_[治疗白癜风]哪家好_合肥北大白癜风医院 | 万师讲师网-优质讲师培训师供应商,讲师认证,找讲师来万师 | 陕西自考报名_陕西自学考试网 | 中医中药治疗血小板减少-石家庄血液病肿瘤门诊部 | 断桥铝破碎机_铝合金破碎机_废铁金属破碎机-河南鑫世昌机械制造有限公司 | 沙盘模型公司_沙盘模型制作公司_建筑模型公司_工业机械模型制作厂家 | 传递窗_超净|洁净工作台_高效过滤器-传递窗厂家广州梓净公司 | 真空干燥烘箱_鼓风干燥箱 _高低温恒温恒湿试验箱_光照二氧化碳恒温培养箱-上海航佩仪器 | 浇钢砖,流钢砖_厂家价低-淄博恒森耐火材料有限公司 | 复合土工膜厂家|hdpe防渗土工膜|复合防渗土工布|玻璃纤维|双向塑料土工格栅-安徽路建新材料有限公司 | 车充外壳,车载充电器外壳,车载点烟器外壳,点烟器连接头,旅行充充电器外壳,手机充电器外壳,深圳市华科达塑胶五金有限公司 | LINK FASHION 童装·青少年装展 河南卓美创业科技有限公司-河南卓美防雷公司-防雷接地-防雷工程-重庆避雷针-避雷器-防雷检测-避雷带-避雷针-避雷塔、机房防雷、古建筑防雷等-山西防雷公司 | 东莞螺杆空压机_永磁变频空压机_节能空压机_空压机工厂批发_深圳螺杆空压机_广州螺杆空压机_东莞空压机_空压机批发_东莞空压机工厂批发_东莞市文颖设备科技有限公司 | 定做大型恒温循环水浴槽-工业用不锈钢恒温水箱-大容量低温恒温水槽-常州精达仪器 | 深圳活动策划公司|庆典策划|专业公关活动策划|深圳艺典文化传媒 重庆中专|职高|技校招生-重庆中专招生网 | 不锈钢轴流风机,不锈钢电机-许昌光维防爆电机有限公司(原许昌光维特种电机技术有限公司) | 广东成考网-广东成人高考网| 变色龙PPT-国内原创PPT模板交易平台 - PPT贰零 - 西安聚讯网络科技有限公司 | 通辽信息港 - 免费发布房产、招聘、求职、二手、商铺等信息 www.tlxxg.net | 精密五金冲压件_深圳五金冲压厂_钣金加工厂_五金模具加工-诚瑞丰科技股份有限公司 | 南京PVC快速门厂家南京快速卷帘门_南京pvc快速门_世界500强企业国内供应商_南京美高门业 | 上海诺狮景观规划设计有限公司 | 等离子表面处理机-等离子表面活化机-真空等离子清洗机-深圳市东信高科自动化设备有限公司 | 便民信息网_家电维修,家电清洗,开锁换锁,本地家政公司 | DNA亲子鉴定_DNA基因检测中心官方预约平台-严选好基因网 | 进口试验机价格-进口生物材料试验机-西安卡夫曼测控技术有限公司 |