MS Quic: QuicException: время ожидания соединения истекло из-за неактивностиC#

Место общения программистов C#
Anonymous
MS Quic: QuicException: время ожидания соединения истекло из-за неактивности

Сообщение Anonymous »

Я обнаружил msquic, который хотел бы использовать для потоковой передачи данных (например, изображения с камеры) с серверного компьютера на клиентский компьютер внутри локальной сети Wi-Fi. Причина, по которой я хотел бы использовать msquick, заключается в том, что UDP должен обеспечивать меньшую задержку, чем технологии на основе TCP, гарантия доставки менее важна.
На основе предоставленной ссылки, которая на данный момент кажется единственной применение быстрой технологии на данный момент я начал реализовывать серверные и клиентские приложения как консольные приложения в .net 8. Это должно быть лишь простым доказательством концепции регулярной отправки строк с сервера на клиент.
Проблема:

Клиент запускается и создает соединение, я верю серверу, я не смог это проверить .
Но сервер на линии:

Код: Выделить всё

var connection = await listener.AcceptConnectionAsync();
выдает исключение System.Exception {System.Net.Quic.QuicException} {"Время ожидания соединения истекло из-за бездействия." через некоторое время. Я не понимаю, в чем моя ошибка и как ее устранить, чтобы начать потоковую передачу с сервера и получать на клиенте.
Сервер:
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);
}
}
Я ценю любую помощь. Я молодой разработчик, я изо всех сил старался найти подходящее решение, прежде чем публиковать здесь, но msqucik в .net в Интернете не так уж и много. Я также буду рад предложениям о различных технологиях, которые могут стать альтернативой msquick.
Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/790 ... inactivity

Вернуться в «C#»