成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術(shù)文章
文章詳情頁

基于.NET 7 的 QUIC 實(shí)現(xiàn) Echo 服務(wù)的詳細(xì)過程

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

前言

隨著今年6月份的 HTTP/3 協(xié)議的正式發(fā)布,它背后的網(wǎng)絡(luò)傳輸協(xié)議 QUIC,憑借其高效的傳輸效率和多路并發(fā)的能力,也大概率會取代我們熟悉的使用了幾十年的 TCP,成為互聯(lián)網(wǎng)的下一代標(biāo)準(zhǔn)傳輸協(xié)議。

在去年 .NET 6 發(fā)布的時(shí)候,已經(jīng)可以看到 HTTP/3 和 Quic 支持的相關(guān)內(nèi)容了,但是當(dāng)時(shí) HTTP/3 的 RFC 還沒有定稿,所以也只是預(yù)覽功能,而 Quic 的 API 也沒有在 .NET 6 中公開。

在最新的 .NET 7 中,.NET 團(tuán)隊(duì)公開了 Quic API,它是基于 MSQuic 庫來實(shí)現(xiàn)的 , 提供了開箱即用的支持,命名空間為 System.Net.Quic。

Quic API

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

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

QuicConnection

表示一個(gè) QUIC 連接,本身不發(fā)送也不接收數(shù)據(jù),它可以打開或者接收多個(gè)QUIC 流。

QuicListener

用來監(jiān)聽入站的 Quic 連接,一個(gè) QuicListener 可以接收多個(gè) Quic 連接。

QuicStream

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

小試牛刀

下面是一個(gè)客戶端和服務(wù)端應(yīng)用使用 Quic 通信的示例。

1.分別創(chuàng)建了 QuicClient 和 QuicServer 兩個(gè)控制臺程序。

項(xiàng)目的版本為 .NET 7, 并且設(shè)置 EnablePreviewFeatures = true。

下面創(chuàng)建了一個(gè) QuicListener,監(jiān)聽了本地端口 9999,指定了 ALPN 協(xié)議版本。

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

因?yàn)?Quic 需要 TLS 加密,所以要指定一個(gè)證書,GenerateManualCertificate 方法可以方便地創(chuàng)建一個(gè)本地的測試證書。

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;}

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

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

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

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

接下來,使用 System.IO.Pipeline 處理流數(shù)據(jù),讀取行數(shù)據(jù),并回復(fù)一個(gè) ack 消息。

Console.WriteLine();await ProcessLinesAsync(stream);Console.ReadKey();      // 處理流數(shù)據(jù)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)){    // 讀取行數(shù)據(jù)    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();} 

以上就是服務(wù)端的完整代碼了。

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

直接使用 QuicConnection.ConnectAsync 連接到服務(wù)端。

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

創(chuàng)建一個(gè)出站的雙向流。

// 打開一個(gè)出站的雙向流var stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); var reader = PipeReader.Create(stream);var writer = PipeWriter.Create(stream);  

后臺讀取流數(shù)據(jù),然后循環(huán)寫入數(shù)據(jù)。

// 后臺讀取流數(shù)據(jù)_ = ProcessLinesAsync(stream);Console.WriteLine(); // 寫入數(shù)據(jù)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 和服務(wù)端一樣,使用 System.IO.Pipeline 讀取流數(shù)據(jù)。

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)){     // 處理行數(shù)據(jù)    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();}

到這里,客戶端和服務(wù)端的代碼都完成了,客戶端使用 Quic 流發(fā)送了一些消息給服務(wù)端,服務(wù)端收到消息后在控制臺輸出,并回復(fù)一個(gè) Ack 消息,因?yàn)槲覀儎?chuàng)建了一個(gè)雙向流。

程序的運(yùn)行結(jié)果如下

我們上面說到了一個(gè) QuicConnection 可以創(chuàng)建多個(gè)流,并行傳輸數(shù)據(jù)。

改造一下服務(wù)端的代碼,支持接收多個(gè) 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();  

對于客戶端,我們用多個(gè)線程創(chuàng)建多個(gè) Quic 流,并同時(shí)發(fā)送消息。

默認(rèn)情況下,一個(gè) Quic 連接的流的限制是 100,當(dāng)然你可以設(shè)置 QuicConnectionOptions 的 MaxInboundBidirectionalStreams 和 MaxInboundUnidirectionalStreams 參數(shù)。

for (int j = 0; j < 5; j++){    _ = Task.Run(async () => {// 創(chuàng)建一個(gè)出站的雙向流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 地址找到,希望對您有用!

到此這篇關(guān)于基于 .NET 7 的 QUIC 實(shí)現(xiàn) Echo 服務(wù)的文章就介紹到這了,更多相關(guān).NET 7 實(shí)現(xiàn) Echo 服務(wù)內(nèi)容請搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!

標(biāo)簽: ASP.NET
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
久久午夜免费电影| 亚洲一区在线观看免费观看电影高清| 精品国产免费久久 | 久久国产欧美精品| 91影视在线播放| 视频一区视频二区中文字幕| 精品国产区一区| 色素色在线综合| 亚洲性人人天天夜夜摸| 国产成人免费9x9x人网站视频| 一区二区三区高清| 日韩欧美一区在线| 国产麻豆日韩| 欧美婷婷在线| www.日韩在线| 日本麻豆一区二区三区视频| 国产亚洲精品福利| 天天av天天翘天天综合网色鬼国产| 欧美一区二区三区四区在线观看地址| 久久美女高清视频| 欧美一区高清| 国产伦精品一区二区三| 精品999成人| 欧美人与禽性xxxxx杂性| 成人激情开心网| 久久精品国产在热久久| 亚洲国产精品久久人人爱 | 国产精品一区二区免费不卡 | 亚洲欧美中日韩| 日本一区二区三区久久久久久久久不| 91久久久久| 欧美一级免费大片| 香蕉乱码成人久久天堂爱免费| 久久精品视频在线看| 97久久精品人人澡人人爽| 久久久久久麻豆| 国产精品国产精品| 亚洲码国产岛国毛片在线| 欧美日韩免费在线视频| 99av国产精品欲麻豆| 欧美午夜一区二区三区| 亚洲精品国产成人久久av盗摄| 在线视频精品| 国产高清精品在线| 日韩欧美一区在线| 欧美日韩综合不卡| 亚洲一区在线免费| 免费国产一区二区| 亚洲国产精品一区二区尤物区| 中文av一区特黄| 欧美性淫爽ww久久久久无| 欧美在线播放高清精品| 99视频有精品| 高清不卡一二三区| 黑丝一区二区三区| 欧美在线观看天堂一区二区三区| 国产99精品国产| 99久久久国产精品| 欧美1级日本1级| 亚洲视频在线二区| 美女久久网站| 欧美私人免费视频| 欧美精品一区二区精品网| 国产嫩草影院久久久久| 亚洲精选免费视频| 日本不卡在线视频| 国产精品一区二区久久精品爱涩| 国产91对白在线观看九色| 成人黄色国产精品网站大全在线免费观看| 国产福利一区二区三区在线视频| 国产盗摄女厕一区二区三区| 欧美成人国产| 亚洲在线观看| 88在线观看91蜜桃国自产| 欧美大黄免费观看| 亚洲欧洲精品一区二区三区不卡| 亚洲一区二区精品3399| 麻豆精品在线视频| 91在线国产观看| 久久精品系列| 欧美大片在线观看| 亚洲精品日韩综合观看成人91| 日本女人一区二区三区| 91在线小视频| 久久蜜桃资源一区二区老牛| 欧美日韩国产精品成人| 日本一区二区三区四区| 亚洲国产综合在线| 成人免费av资源| 国产一区二区三区免费不卡 | 精品乱人伦小说| 亚洲一区二区欧美| 99精品视频一区二区| 免费日韩视频| 国产拍欧美日韩视频二区| 日本不卡高清视频| 狠色狠色综合久久| 欧美日韩综合一区| 亚洲欧美一区二区三区孕妇| 国产福利一区二区| 免费一区二区三区| 国产亚洲制服色| 精品在线视频一区| 国产精品久久久久久久久久妞妞 | 久久91精品久久久久久秒播| 午夜精品久久99蜜桃的功能介绍| 色狠狠色噜噜噜综合网| 国产精品素人视频| 成人黄色在线视频| 在线看不卡av| 亚洲已满18点击进入久久| av色综合久久天堂av综合| 色婷婷综合久久久久中文一区二区 | 在线精品视频免费播放| 国产精品嫩草影院com| 久久国产精品99久久久久久老狼 | 久久午夜视频| 国产日本欧洲亚洲| 成人小视频免费在线观看| 91高清视频免费看| 亚洲精品va在线观看| 91美女片黄在线| 精品久久一二三区| 国产一区在线观看麻豆| 久久一区二区精品| 亚洲福中文字幕伊人影院| 午夜精品久久| 久久久久久久久久久久久女国产乱| 国产乱码精品一区二区三区五月婷| 久久久777| 亚洲成人手机在线| 午夜亚洲影视| 亚洲一区二区三区不卡国产欧美| 亚洲国产精品毛片| 欧美激情在线观看视频免费| 国产成人夜色高潮福利影视| 欧美日韩久久一区| 国内精品国产成人国产三级粉色| 欧美午夜片在线观看| 捆绑紧缚一区二区三区视频 | 天堂久久久久va久久久久| 亚洲综合欧美日韩| 亚洲123区在线观看| 蘑菇福利视频一区播放| 亚洲一区二区三区中文字幕 | 在线观看av一区二区| 视频一区中文字幕国产| 欧洲精品一区二区三区在线观看| 日韩激情视频网站| 在线精品视频免费播放| 国产在线国偷精品免费看| 精品视频在线免费看| 国产精品18久久久久久久久| 日韩一级二级三级精品视频| 国产98色在线|日韩| 国产亚洲精品aa午夜观看| 国内在线观看一区二区三区| 亚洲永久精品大片| 色香蕉成人二区免费| 激情综合五月天| 亚洲精品在线网站| 欧美国产专区| 亚洲精品伦理在线| 欧美亚洲综合网| 成人午夜激情在线| 国产精品欧美一区喷水| 国产偷久久久精品专区| 免费av网站大全久久| 欧美大白屁股肥臀xxxxxx| 91亚洲资源网| 亚洲国产精品久久久久秋霞影院| 久久亚洲国产精品一区二区| 国产精品一区专区| 精品国产乱码久久久久久夜甘婷婷| 欧美1区2区3区| 亚洲动漫第一页| 日韩午夜av电影| 国产精品一页| 成人av免费在线观看| av激情亚洲男人天堂| 国产91高潮流白浆在线麻豆| 懂色av中文一区二区三区| 丁香亚洲综合激情啪啪综合| 不卡的av中国片| 99国产精品久久久| 91视频www| 欧美视频成人| 亚洲黄色av| 国产精品一区二区在线观看| 夜夜嗨网站十八久久 | 一区二区三区欧美激情| 99精品福利视频| 国产69精品久久777的优势| 亚洲欧美区自拍先锋| 欧美精品在线视频| 亚洲最新在线| 午夜国产精品视频| 成人理论电影网| 麻豆精品在线观看| 成人av在线播放网站|