RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / user-672699

Серый Биф's questions

Martin Hope
Серый Биф
Asked: 2025-01-08 19:41:28 +0000 UTC

使用 Windows 11 从服务器下载应用程序版本时,HttpClient 无法正常工作

  • 6

该任务的本质是使用带有动态更新的 ProgressBar 和 Label 的表单从 Internet 服务器下载应用程序安装程序文件,该 Label 指示已下载的 MB 数以及HttpClient类,以获得对 Internet 服务器的直接访问。另外,如果互联网连接中断,请等待 60 秒才能重新连接。如果互联网丢失 >= 60 秒 - 关闭表单并中断下载。

在 Windows 10 上,该功能工作正常,但在 Windows 11 上,当 Internet 连接中断时,即断开连接时,就会出现问题 - 表单停止处理交互事件(单击按钮等)(当 GUI 正常工作时)。在某些版本的 Windows 上(Windows 11 版本 23h2) - 整个应用程序关闭(从应用程序调用下载表单)。

.NET框架4.8

StartDownload - 下载开始时调用的方法。

private async void StartDownload(string path = "")
{
    if (_cts != null) return;

    _lastSelectedPath = string.IsNullOrEmpty(path) ? SelectFolder() : path;

    if (_lastSelectedPath == null || !CanWriteToFolder(_lastSelectedPath))
    {
        MessageBox.Show(Properties.Resources.AccessViolation);
        Close();
        return;
    }

    FilePath = Path.Combine(_lastSelectedPath, _fileName);
    string url = _plugin.IsTestMode
        ? $"Сервер №1"
        : $"Сервер №2";

    bool isDownloadSuccessful = false;
    using (_cts = new CancellationTokenSource())
    {
        try
        {
            var progress = new Progress<DownloadProgress>(UpdateProgress);
            do
            {
                try
                {
                    await DownloadAndSaveFileAsync(url, FilePath, progress, _cts.Token); // Метод будет описан ниже
                    isDownloadSuccessful = true;
                    break; // Успешная загрузка, выходим из цикла
                }
                catch (IOException ex) // потеря доступа к интернету
                {
                    lbProgress.Text = Properties.Resources.ConnectWithServerLost; // lbProgress - Label на форме отображающий состояние скачивания
                    await WaitForReconnectAsync(_cts.Token); // Запуск 60 секунд для восстановления интернета
                }
                catch (HttpRequestException ex)
                {
                    if (ex.Message.Contains("416")) // 416 (Запрошенный диапазон невыполним = Файл уже полностью загружен)
                    {
                        MessageBox.Show(Properties.Resources.FileAlreadyUploaded);
                        DialogResult = DialogResult.OK;
                        break;
                    }
                    else break; // выходим из цикла, если были другие ошибки типа HttpRequestException
                }
                catch (OperationCanceledException) // Отмена пользователем с помощью кнопки "Cancel" или закрытия формы
                {
                    DialogResult = DialogResult.Cancel;
                    break;
                }
                catch (Exception msg)
                {
                    break;
                }
            }
            while (!_connectionEstablishmentFailed); // Повторяем, пока не исчерпан лимит времени

            
            if (!_connectionEstablishmentFailed && DialogResult != DialogResult.Cancel) isDownloadSuccessful = true;
            else // Соединение не восстановилось
            {
                DialogResult = DialogResult.Cancel;
            }
        }

        finally
        {
            if (isDownloadSuccessful) DialogResult = DialogResult.OK;
            progress.Value = 0;
            Close();
        }
    }
    _cts = null; // Обязательно сбрасываем, чтобы избежать ObjectDisposedException
}

DownloadAndSaveFileAsync - 在 StartDownload 执行期间调用以开始下载文件

private async Task DownloadAndSaveFileAsync(string url, string filePath, IProgress<DownloadProgress> progress, CancellationToken token)
{
    const int bufferLength = 8192;
    long downloadedBytes = File.Exists(filePath) ? new FileInfo(filePath).Length : 0;

    using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url))
    {
        if (downloadedBytes > 0) request.Headers.Range = new RangeHeaderValue(downloadedBytes, null);

        using (HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false))
        {
            response.EnsureSuccessStatusCode();

            using (Stream contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
            using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None))
            {
                long totalBytes = downloadedBytes + response.Content.Headers.ContentLength ?? 0;
                byte[] buffer = new byte[bufferLength];
                int bytesReceived;

                while ((bytesReceived = await contentStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0)
                {
                    await fs.WriteAsync(buffer, 0, bytesReceived, token).ConfigureAwait(false);
                    downloadedBytes += bytesReceived;
                    progress?.Report(new DownloadProgress
                    {
                        BytesDownloaded = downloadedBytes,
                        TotalBytes = totalBytes
                    });
                }
            }
        }
    }
}

WaitForReconnectAsync - 启动等待 60 秒以恢复 Internet 连接的过程的方法

private async Task WaitForReconnectAsync(CancellationToken token)
{
    int elapsedTime = 0;
    while (elapsedTime < maxWaitTime)
    {
        token.ThrowIfCancellationRequested();
        if (PingServer("4.2.2.4"))
        {
            _connectionEstablishmentFailed = false; // Сбрасываем флаг при успешном восстановлении сети Интернет
            return;
        }
        await Task.Delay(checkInterval, token);
        lbProgress.Text = Properties.Resources.ConnectWithServerLost + $" {elapsedTime / 1000} sec.";
        elapsedTime += checkInterval;
    }
    _connectionEstablishmentFailed = true; // Соединение не восстановлено в течении maxWaitTime
}

PingServer - 向指定地址发送请求的方法

private bool PingServer(string host)
{
    try
    {
        using (var ping = new Ping())
        {
            var reply = ping.Send(host, 1000);
            return reply.Status == IPStatus.Success;
        }
    }
    catch { return false; }
}
c#
  • 1 个回答
  • 30 Views

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5