На основе предоставленной ссылки, которая на данный момент кажется единственной применение быстрой технологии на данный момент я начал реализовывать серверные и клиентские приложения как консольные приложения в .net 8. Это должно быть лишь простым доказательством концепции регулярной отправки строк с сервера на клиент.
Проблема:
Клиент запускается и создает соединение, я верю серверу, я не смог это проверить .
Но сервер на линии:
Код: Выделить всё
var connection = await listener.AcceptConnectionAsync();Сервер:
Program.cs
Код: Выделить всё
// See https://aka.ms/new-console-template for more information
using System.Net.Quic;
using System.Net;
using System.Threading;
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using QuickServerDemo;
bool isRunning = true;
Console.WriteLine("Starting QUIC server...");
try
{
// Define the certificate path assuming it's in the current directory
string certPath = Path.Combine(Directory.GetCurrentDirectory(), "server_cert.pem");
string keyPath = Path.Combine(Directory.GetCurrentDirectory(), "server_key.pem");
// Ensure certificate exists before attempting to load it
if (!File.Exists(certPath))
{
Console.WriteLine($"Certificate file not found at {certPath}");
return;
}
// Load the certificate
X509Certificate2 serverCertificate = new X509Certificate2(certPath);
// First, check if QUIC is supported.
if (!QuicConnection.IsSupported)
{
Console.WriteLine("QUIC is not supported, check for presence of libmsquic and support of TLS 1.3.");
return;
}
/*
Microsoft example QuicListener - server side class that accepts incoming connections from the clients
*/
// Share configuration for each incoming connection.
// This represents the minimal configuration necessary.
var serverConnectionOptions = new QuicServerConnectionOptions
{
// Used to abort stream if it's not properly closed by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultStreamErrorCode = 0x0A, // Protocol-dependent error code.
// Used to close the connection if it's not done by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultCloseErrorCode = 0x0B, // Protocol-dependent error code.
// Same options as for server side SslStream.
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
// Specify the application protocols that the server supports. This list must be a subset of the protocols specified in QuicListenerOptions.ApplicationProtocols.
ApplicationProtocols = [new SslApplicationProtocol("protocol1")],
// Server certificate, it can also be provided via ServerCertificateContext or ServerCertificateSelectionCallback.
ServerCertificate = SelfSignedCertificate.CreateSelfSignedCertificate()
}
};
// Initialize, configure the listener and start listening.
var listener = await QuicListener.ListenAsync(new QuicListenerOptions
{
// Define the endpoint on which the server will listen for incoming connections. The port number 0 can be replaced with any valid port number as needed.
ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
// List of all supported application protocols by this listener.
ApplicationProtocols = [new SslApplicationProtocol("protocol1")],
// Callback to provide options for the incoming connections, it gets called once per each connection.
ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(serverConnectionOptions)
});
// Accept and process the connections.
while (isRunning)
{
// Accept will propagate any exceptions that occurred during the connection establishment,
// including exceptions thrown from ConnectionOptionsCallback, caused by invalid QuicServerConnectionOptions or TLS handshake failures.
//var connection = await listener.AcceptConnectionAsync();
// Process the connection...
// Work done with all different types of streams.
var connection = await listener.AcceptConnectionAsync();
// Open a QuicStream and pass to the common method.
var quicStream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
await WorkWithStreamAsync(quicStream);
await listener.DisposeAsync();
}
}
catch (Exception e)
{
throw new Exception(e.Message,e);
}
/*
Microsoft example QuicStream
*/
async Task WorkWithStreamAsync(Stream stream)
{
// This will dispose the stream at the end of the scope.
await using (stream)
{
// Simple echo, read data and send them back.
byte[] buffer = new byte[1024];
// The loop stops when read returns 0 bytes as is common for all streams.
while (true)
{
string originalString = "Hello, World! Send: " + DateTime.UtcNow.ToString();
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(originalString);
await stream.WriteAsync(byteArray);
}
//while ((count = await stream.ReadAsync(buffer)) > 0)
//{
// await stream.WriteAsync(buffer.AsMemory(0, count));
//}
}
}
Program.cs
Код: Выделить всё
using System.Net.Quic;
using System.Net.Security;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using QuickClientDemo;
internal class Program
{
private static async Task Main(string[] args)
{
try
{
/*
Microsoft example QuicConnection
*/
bool isRunning = true;
Console.WriteLine("Starting QUIC client...");
// Define the certificate path assuming it's in the current directory
string certPath = Path.Combine(Directory.GetCurrentDirectory(), "server_cert.pem");
string keyPath = Path.Combine(Directory.GetCurrentDirectory(), "server_key.pem");
// Ensure certificate exists before attempting to load it
if (!File.Exists(certPath))
{
Console.WriteLine($"Certificate file not found at {certPath}");
return;
}
// Load the certificate
X509Certificate2 serverCertificate = new X509Certificate2(certPath);
// First, check if QUIC is supported.
if (!QuicConnection.IsSupported)
{
Console.WriteLine("QUIC is not supported, check for presence of libmsquic and support of TLS 1.3.");
return;
}
// Share configuration for each incoming connection.
// This represents the minimal configuration necessary.
var serverConnectionOptions = new QuicServerConnectionOptions
{
// Used to abort stream if it's not properly closed by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultStreamErrorCode = 0x0A, // Protocol-dependent error code.
// Used to close the connection if it's not done by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultCloseErrorCode = 0x0B, // Protocol-dependent error code.
// Same options as for server side SslStream.
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
// Specify the application protocols that the server supports. This list must be a subset of the protocols specified in QuicListenerOptions.ApplicationProtocols.
ApplicationProtocols = [new SslApplicationProtocol("protocol1")],
// Server certificate, it can also be provided via ServerCertificateContext or ServerCertificateSelectionCallback.
ServerCertificate = SelfSignedCertificate.CreateSelfSignedCertificate(),
}
};
// Initialize, configure the listener and start listening.
var listener = await QuicListener.ListenAsync(new QuicListenerOptions
{
// Define the endpoint on which the server will listen for incoming connections. The port number 0 can be replaced with any valid port number as needed.
ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
// List of all supported application protocols by this listener.
ApplicationProtocols = [new SslApplicationProtocol("protocol1")],
// Callback to provide options for the incoming connections, it gets called once per each connection.
ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(serverConnectionOptions)
});
// This represents the minimal configuration necessary to open a connection.
var clientConnectionOptions = new QuicClientConnectionOptions
{
// End point of the server to connect to.
RemoteEndPoint = listener.LocalEndPoint,
// Used to abort stream if it's not properly closed by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultStreamErrorCode = 0x0A, // Protocol-dependent error code.
// Used to close the connection if it's not done by the user.
// See https://www.rfc-editor.org/rfc/rfc9000#section-20.2
DefaultCloseErrorCode = 0x0B, // Protocol-dependent error code.
// Optionally set limits for inbound streams.
MaxInboundUnidirectionalStreams = 10,
MaxInboundBidirectionalStreams = 100,
// Same options as for client side SslStream.
ClientAuthenticationOptions = new SslClientAuthenticationOptions
{
// List of supported application protocols.
ApplicationProtocols = [new SslApplicationProtocol("protocol1")],
// The name of the server the client is trying to connect to. Used for server certificate validation.
TargetHost = "localhost",
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
}
};
// Initialize, configure and connect to the server.
var connection = await QuicConnection.ConnectAsync(clientConnectionOptions);
Console.WriteLine($"Connected {connection.LocalEndPoint} --> {connection.RemoteEndPoint}");
// Open a bidirectional (can both read and write) outbound stream.
// Opening a stream reserves it but does not notify the peer or send any data. If you don't send data, the peer
// won't be informed about the stream, which can cause AcceptInboundStreamAsync() to hang. To avoid this, ensure
// you send data on the stream to properly initiate communication.
var outgoingStream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
// Work with the outgoing stream ...
// To accept any stream on a client connection, at least one of MaxInboundBidirectionalStreams or MaxInboundUnidirectionalStreams of QuicConnectionOptions must be set.
while (isRunning)
{
// Accept an inbound stream.
var incomingStream = await connection.AcceptInboundStreamAsync();
// Work with the incoming stream ...
byte[] buffer = new byte[1024];
await incomingStream.ReadAsync(buffer);
string recoveredString = System.Text.Encoding.UTF8.GetString(buffer);
Console.WriteLine(recoveredString + "Received: " + DateTime.UtcNow.ToString());
}
// Close the connection with the custom code.
await connection.CloseAsync(0x0C);
// Dispose the connection.
await connection.DisposeAsync();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
}
(Я знаю, что это грязно, но сейчас это всего лишь прототип)
SelfSignedCertificate. cs на основе этой проблемы
(один и тот же класс присутствует в обоих проектах, в зависимости от того, закомментирована ли на сервере или клиенте другая строка с Oid).
SelfSignedCertificate.cs
Код: Выделить всё
public static class SelfSignedCertificate
{
public static X509Certificate2 CreateSelfSignedCertificate()
{
using var rsa = RSA.Create();
var certificateRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
certificateRequest.CertificateExtensions.Add(
new X509BasicConstraintsExtension(
certificateAuthority: false,
hasPathLengthConstraint: false,
pathLengthConstraint: 0,
critical: true
)
);
certificateRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(
keyUsages:
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign,
critical: false
)
);
certificateRequest.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
//new Oid("1.3.6.1.5.5.7.3.2"), // TLS Client auth
new Oid("1.3.6.1.5.5.7.3.1") // TLS Server auth
},
false));
certificateRequest.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(
key: certificateRequest.PublicKey,
critical: false
)
);
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
var cert = certificateRequest.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(5));
// windows only
return new X509Certificate2(cert.Export(X509ContentType.Pfx), (string?)null, X509KeyStorageFlags.Exportable);
}
}
Спасибо!
Подробнее здесь: https://stackoverflow.com/questions/790 ... inactivity