Основы работы в сети

Действия, которые необходимо сделать для любого приложения с поддержкой сети.

Возможности

Чтобы использовать сеть, необходимо добавить соответствующие элементы возможностей в манифест приложения. Если в манифесте приложения нет сетевых возможностей, приложение не сможет подключиться к сети, и любая попытка подключиться к сети завершится ошибкой.

Ниже приведены наиболее используемые сетевые возможности.

Возможность Description
internetClient Предоставляет исходящий доступ к Интернету и сетям в общественных местах, таких как аэропорты и кафе. Эта характеристика должна использоваться для большинства приложений, которым необходим доступ к Интернету.
internetClientServer Предоставляет приложению входящий и исходящий сетевой доступ из Интернета и сетей в общественных местах, таких как аэропорты и кафе.
privateNetworkClientServer Предоставляет приложению входящий и исходящий сетевой доступ в надежных местах пользователя, например дома и работы.

Существуют и другие возможности, которые могут потребоваться для вашего приложения в определенных обстоятельствах.

Возможность Description
enterpriseAuthentication Позволяет приложению подключаться к сетевым ресурсам, которым требуются учетные данные домена. В качестве примера можно привести приложение, которое получает данные с серверов SharePoint в частной интрасети. С помощью этой возможности учетные данные можно использовать для доступа к сетевым ресурсам в сети, требующей учетных данных. Приложение с этой возможностью может олицетворить вас в сети. Эта возможность не требуется, если нужно разрешить приложению доступ к Интернету через прокси-сервер, выполняющий проверку подлинности.

Дополнительные сведения см. в документации по сценарию возможностей Enterprise в разделе об ограниченных возможностях.
proximity Требуется для связи близкого взаимодействия с устройствами в близком расположении к компьютеру. Близкое к полю можно использовать для отправки или подключения к приложению на соседнем устройстве.

Эта возможность позволяет приложению получить доступ к сети для подключения к устройству в близком расположении с согласием пользователя на отправку приглашения или принятие приглашения.
sharedUserCertificates Эта возможность позволяет приложению получать доступ к сертификатам программного обеспечения и оборудования, таким как смарт-карта сертификаты. При вызове этой возможности во время выполнения пользователь должен принять меры, такие как вставка карта или выбор сертификата.

Благодаря этой возможности сертификаты программного обеспечения и оборудования или смарт-карта используются для идентификации в приложении. Эта возможность может использоваться вашим работодателем, банком или государственными службами для идентификации.

Обмен данными, когда ваше приложение не находится на переднем плане

Поддержка приложения с фоновыми задачами содержит общие сведения об использовании фоновых задач для выполнения работы, если приложение не находится на переднем плане. В частности, код должен выполнять специальные действия, чтобы получать уведомления, если оно не является текущим приложением переднего плана и данными, поступающими через сеть для него. Для этой цели вы использовали триггеры канала управления в Windows 8, и они по-прежнему поддерживаются в Windows 10. Полные сведения об использовании триггеров канала управления доступны здесь. Новая технология в Windows 10 имеет лучшую функциональность с меньшей нагрузкой для некоторых сценариев, как в случае с сокетами потока с поддержкой push-уведомлений: брокер сокета и триггеры активности сокетов.

Если приложение использует DatagramSocket, StreamSocket или StreamSocketListener, ваше приложение может передать владение открытым сокетом брокеру сокета, предоставленному системой, а затем оставить передний план или даже завершить работу. Когда подключение выполняется на перенесенном сокете или трафик поступает на этот сокет, то активируется ваше приложение или назначенная фоновая задача. Если приложение не запущено, он запускается. Затем брокер сокетов уведомляет приложение с помощью SocketActivityTrigger о том, что новый трафик прибыл. Приложение освобождает сокет от брокера сокетов и обрабатывает трафик в сокете. Это означает, что приложение потребляет гораздо меньше системных ресурсов, если он не активно обрабатывает сетевой трафик.

Брокер сокетов предназначен для замены триггеров канала управления, где это применимо, так как он предоставляет те же функции, но с меньшими ограничениями и меньшим объемом памяти. Брокер сокетов можно использовать приложениями, которые не являются приложениями экрана блокировки, и он используется так же, как на телефонах, так и на других устройствах. Приложения не должны работать при поступлении трафика для активации брокером сокетов. И брокер сокетов поддерживает прослушивание сокетов TCP, которые триггеры канала управления не поддерживаются.

Выбор сетевого триггера

Существуют некоторые сценарии, в которых любой тип триггера подходит. При выборе типа триггера, используемого в приложении, рассмотрите следующие советы.

  • Если вы используете IXMLHTTPRequest2, System.Net.HttpClient или System.Net.Http.HttpClientHandler, необходимо использовать ControlChannelTrigger.
  • Если вы используете StreamSockets с поддержкой push-уведомлений, можно использовать триггеры канала управления, но следует предпочесть SocketActivityTrigger. Последний выбор позволяет системе освободить память и уменьшить требования к мощности, если подключение не используется.
  • Если вы хотите свести к минимуму объем памяти приложения, если оно не активно обслуживает сетевые запросы, по возможности предпочтете SocketActivityTrigger .
  • Если вы хотите, чтобы приложение могло получать данные, пока система находится в Подключение режиме ожидания, используйте SocketActivityTrigger.

Дополнительные сведения и примеры использования брокера сокетов см. в разделе "Сетевые коммуникации" в фоновом режиме.

Защищенные подключения

Безопасный уровень сокетов (SSL) и более поздний протокол TLS — это криптографические протоколы, предназначенные для проверки подлинности и шифрования для сетевого взаимодействия. Эти протоколы предназначены для предотвращения перехвата и изменения при отправке и получении сетевых данных. Эти протоколы используют модель клиентского сервера для обмена протоколами. Эти протоколы также используют цифровые сертификаты и центры сертификации, чтобы убедиться, что сервер является тем, кто он утверждает.

Создание безопасных подключений сокета

Объект StreamSocket можно настроить для обмена данными между клиентом и сервером с помощью SSL/TLS. Эта поддержка SSL/TLS ограничена использованием объекта StreamSocket в качестве клиента в согласовании SSL/TLS. Вы не можете использовать SSL/TLS с StreamSocket, созданным StreamSocketListener при получении входящих сообщений, так как согласование SSL/TLS в качестве сервера не реализуется классом StreamSocket.

Существует два способа защиты подключения StreamSocket с помощью SSL/TLS:

  • ConnectAsync устанавливает начальное подключение к сетевой службе и сразу согласовывает использование протокола SSL/TLS для всех подключений.
  • UpgradeToSslAsync сначала подключается к сетевой службе без использования шифрования. Приложение может отправлять или получать данные. Затем обновите подключение, чтобы использовать SSL/TLS для всех дальнейших подключений.

SocketProtectionLevel указывает необходимый уровень защиты сокета, который требуется приложению, чтобы установить или обновить подключение. Однако конечный уровень защиты установленного соединения определяется в процессе согласования между обеими конечными точками подключения. Результатом может быть менее безопасный уровень защиты, чем указанный, если другая конечная точка запрашивает более низкий уровень.

После успешного выполнения асинхронной операции можно получить запрошенный уровень защиты, используемый в вызове ConnectAsync или UpgradeToSslAsync с помощью свойства StreamSocketinformation.ProtectionLevel. Но это не отражает фактический уровень защиты соединения.

Примечание.

Код не должен зависеть от использования определенного уровня защиты или предположения, что заданный уровень безопасности используется по умолчанию. Среда безопасности постоянно изменяется. Протоколы и уровни защиты по умолчанию будут также изменяться со временем, чтобы не допустить использование протоколов с известными слабыми местами. Значения по умолчанию могут отличаться в зависимости от конфигурации отдельного компьютера или установленного программного обеспечения и примененных исправлений. Если поведение приложения зависит от использования определенного уровня безопасности, необходимо явно указать этот уровень и убедиться, что этот уровень действительно используется в установленном подключении.

Использование Подключение Async

ConnectAsync можно использовать для установки начального подключения к сетевой службе и немедленного согласования использования протокола SSL/TLS для всех подключений. Существует два метода Подключение Async, поддерживающие передачу параметра protectionLevel:

Если параметр protectionLevel имеет значение Windows.Networking.Sockets.SocketProtectionLevel.Ssl при вызове любого из указанных выше методов Подключение Async, для шифрования необходимо установить StreamSocket для шифрования SSL/TLS. Это значение требует шифрования и никогда не позволяет использовать шифр NULL.

Обычная последовательность, используемая с одним из этих методов Подключение Async, совпадает.

В следующем примере создается StreamSocket и пытается установить подключение к сетевой службе и немедленно согласовать использование SSL/TLS. Если согласование выполнено успешно, все сетевое взаимодействие с помощью StreamSocket между клиентом сетевого сервера будет зашифровано.

using Windows.Networking;
using Windows.Networking.Sockets;

    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
     
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "https";
    
    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 
    
    // Try to connect to contoso using HTTPS (port 443)
    try {

        // Call ConnectAsync method with SSL
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.Ssl);

        NotifyUser("Connected");
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }
        
        NotifyUser("Connect failed with error: " + exception.Message);
        // Could retry the connection, but for this simple example
        // just close the socket.
        
        clientSocket.Dispose();
        clientSocket = null; 
    }
           
    // Add code to send and receive data using the clientSocket
    // and then close the clientSocket
#include <winrt/Windows.Networking.Sockets.h>

using namespace winrt;
...
    // Define some variables, and set values.
    Windows::Networking::Sockets::StreamSocket clientSocket;

    Windows::Networking::HostName serverHost{ L"www.contoso.com" };
    winrt::hstring serverServiceName{ L"https" };

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages.

    // Try to connect to the server using HTTPS and SSL (port 443).
    try
    {
        co_await clientSocket.ConnectAsync(serverHost, serverServiceName, Windows::Networking::Sockets::SocketProtectionLevel::Tls12);
        NotifyUser(L"Connected");
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Connect failed with error: " + exception.message());
        clientSocket = nullptr;
    }
    // Add code to send and receive data using the clientSocket,
    // then set the clientSocket to nullptr when done to close it.
using Windows::Networking;
using Windows::Networking::Sockets;

    // Define some variables and set values
    StreamSocket^ clientSocket = new ref StreamSocket();
 
    HostName^ serverHost = new ref HostName("www.contoso.com");
    String serverServiceName = "https";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to the server using HTTPS and SSL (port 443)
    task<void>(clientSocket->ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel::SSL)).then([this] (task<void> previousTask) {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();
            NotifyUser("Connected");
        }
        catch (Exception^ exception)
        {
            NotifyUser("Connect failed with error: " + exception->Message);
            
            clientSocket.Close();
            clientSocket = null;
        }
    });
    // Add code to send and receive data using the clientSocket
    // Then close the clientSocket when done

Использование UpgradeToSslAsync

Когда код использует UpgradeToSslAsync, сначала устанавливает подключение к сетевой службе без шифрования. Приложение может отправлять или получать некоторые данные, а затем обновить подключение, чтобы использовать SSL/TLS для всех дальнейших коммуникаций.

Метод UpgradeToSslAsync принимает два параметра. Параметр protectionLevel указывает требуемый уровень защиты. Параметр validationHostName — это имя узла удаленного назначения сети, которое используется для проверки при обновлении до SSL. Как правило, имя узла проверкиHostName совпадает с именем узла, которое приложение использовало для первоначального установления подключения. Если параметр protectionLevel имеет значение Windows.System.Socket.SocketProtectionLevel.Ssl при вызове UpgradeToSslAsync, StreamSocket должен использовать SSL/TLS для шифрования для дальнейшего обмена данными по сокету. Это значение требует шифрования и никогда не позволяет использовать шифр NULL.

Обычная последовательность, используемая с методом UpgradeToSslAsync , выглядит следующим образом:

В следующем примере создается StreamSocket, пытается установить подключение к сетевой службе, отправляет некоторые исходные данные, а затем согласовывает использование SSL/TLS. Если согласование выполнено успешно, все сетевое взаимодействие с помощью StreamSocket между клиентом и сетевым сервером будет зашифровано.

using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
 
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "http";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTP (port 80)
    try {
        // Call ConnectAsync method with a plain socket
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.PlainSocket);

        NotifyUser("Connected");

    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Connect failed with error: " + exception.Message, NotifyType.ErrorMessage);
        // Could retry the connection, but for this simple example
        // just close the socket.

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }

    // Now try to send some data
    DataWriter writer = new DataWriter(clientSocket.OutputStream);
    string hello = "Hello, World! ☺ ";
    Int32 len = (int) writer.MeasureString(hello); // Gets the UTF-8 string length.
    writer.WriteInt32(len);
    writer.WriteString(hello);
    NotifyUser("Client: sending hello");

    try {
        // Call StoreAsync method to store the hello message
        await writer.StoreAsync();

        NotifyUser("Client: sent data");

        writer.DetachStream(); // Detach stream, if not, DataWriter destructor will close it.
    }
    catch (Exception exception) {
        NotifyUser("Store failed with error: " + exception.Message);
        // Could retry the store, but for this simple example
            // just close the socket.

            clientSocket.Dispose();
            clientSocket = null; 
            return;
    }

    // Now upgrade the client to use SSL
    try {
        // Try to upgrade to SSL
        await clientSocket.UpgradeToSslAsync(SocketProtectionLevel.Ssl, serverHost);

        NotifyUser("Client: upgrade to SSL completed");
           
        // Add code to send and receive data 
        // The close clientSocket when done
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Upgrade to SSL failed with error: " + exception.Message);

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>

using namespace winrt;
using namespace Windows::Storage::Streams;
...
    // Define some variables, and set values.
    Windows::Networking::Sockets::StreamSocket clientSocket;

    Windows::Networking::HostName serverHost{ L"www.contoso.com" };
    winrt::hstring serverServiceName{ L"https" };

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages. 

    // Try to connect to the server using HTTP (port 80).
    try
    {
        co_await clientSocket.ConnectAsync(serverHost, serverServiceName, Windows::Networking::Sockets::SocketProtectionLevel::PlainSocket);
        NotifyUser(L"Connected");
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Connect failed with error: " + exception.message());
        clientSocket = nullptr;
    }

    // Now, try to send some data.
    DataWriter writer{ clientSocket.OutputStream() };
    winrt::hstring hello{ L"Hello, World! ☺ " };
    uint32_t len{ writer.MeasureString(hello) }; // Gets the size of the string, in bytes.
    writer.WriteInt32(len);
    writer.WriteString(hello);
    NotifyUser(L"Client: sending hello");

    try
    {
        co_await writer.StoreAsync();
        NotifyUser(L"Client: sent hello");

        writer.DetachStream(); // Detach the stream when you want to continue using it; otherwise, the DataWriter destructor closes it.
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Store failed with error: " + exception.message());
        // We could retry the store operation. But, for this simple example, just close the socket by setting it to nullptr.
        clientSocket = nullptr;
        co_return;
    }

    // Now, upgrade the client to use SSL.
    try
    {
        co_await clientSocket.UpgradeToSslAsync(Windows::Networking::Sockets::SocketProtectionLevel::Tls12, serverHost);
        NotifyUser(L"Client: upgrade to SSL completed");

        // Add code to send and receive data using the clientSocket,
        // then set the clientSocket to nullptr when done to close it.
    }
    catch (winrt::hresult_error const& exception)
    {
        // If this is an unknown status, then the error is fatal and retry will likely fail.
        Windows::Networking::Sockets::SocketErrorStatus socketErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(exception.to_abi()) };
        if (socketErrorStatus == Windows::Networking::Sockets::SocketErrorStatus::Unknown)
        {
            throw;
        }

        NotifyUser(L"Upgrade to SSL failed with error: " + exception.message());
        // We could retry the store operation. But for this simple example, just close the socket by setting it to nullptr.
        clientSocket = nullptr;
        co_return;
    }
using Windows::Networking;
using Windows::Networking::Sockets;
using Windows::Storage::Streams;

    // Define some variables and set values
    StreamSocket^ clientSocket = new ref StreamSocket();
 
    Hostname^ serverHost = new ref HostName("www.contoso.com");
    String serverServiceName = "http";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTP (port 80)
    task<void>(clientSocket->ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel::PlainSocket)).then([this] (task<void> previousTask) {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();
            NotifyUser("Connected");
        }
        catch (Exception^ exception)
        {
            NotifyUser("Connect failed with error: " + exception->Message);
 
            clientSocket->Close();
            clientSocket = null;
        }
    });
       
    // Now try to send some data
    DataWriter^ writer = new ref DataWriter(clientSocket.OutputStream);
    String hello = "Hello, World! ☺ ";
    Int32 len = (int) writer->MeasureString(hello); // Gets the UTF-8 string length.
    writer->writeInt32(len);
    writer->writeString(hello);
    NotifyUser("Client: sending hello");

    task<void>(writer->StoreAsync()).then([this] (task<void> previousTask) {
        try {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();

            NotifyUser("Client: sent hello");

            writer->DetachStream(); // Detach stream, if not, DataWriter destructor will close it.
       }
       catch (Exception^ exception) {
               NotifyUser("Store failed with error: " + exception->Message);
               // Could retry the store, but for this simple example
               // just close the socket.
 
               clientSocket->Close();
               clientSocket = null;
               return
       }
    });

    // Now upgrade the client to use SSL
    task<void>(clientSocket->UpgradeToSslAsync(clientSocket.SocketProtectionLevel.Ssl, serverHost)).then([this] (task<void> previousTask) {
        try {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();

           NotifyUser("Client: upgrade to SSL completed");
           
           // Add code to send and receive data 
           // Then close clientSocket when done
        }
        catch (Exception^ exception) {
            // If this is an unknown status it means that the error is fatal and retry will likely fail.
            if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
                throw;
            }

            NotifyUser("Upgrade to SSL failed with error: " + exception.Message);

            clientSocket->Close();
            clientSocket = null; 
            return;
        }
    });

Создание безопасных подключений WebSocket

Подобно традиционным соединениям сокетов, для приложений UWP соединения WebSocket можно шифровать с помощью протоколов TLS/SSL при использовании компонентов StreamWebSocket и MessageWebSocket. В большинстве случаев вы хотите использовать безопасное подключение WebSocket. Это приведет к увеличению вероятности успешного подключения, так как многие прокси-серверы отклонят незашифрованные подключения WebSocket.

Примеры создания или обновления для безопасного подключения сокета к сетевой службе см. в статье "Как защитить подключения WebSocket с помощью TLS/SSL".

Помимо шифрования TLS/SSL, серверу может потребоваться значение заголовка Sec-WebSocket-Protocol для завершения первоначального подтверждения. Это значение, представленное свойствами StreamWebSocketInformation.Protocol и MessageWebSocketInformation.Protocol, указывает версию протокола подключения и позволяет серверу правильно интерпретировать открывающее подтверждение и обмен данных после этого. Используя эти сведения о протоколе, если в любой момент сервер не может интерпретировать входящие данные безопасным образом, подключение можно закрыть.

Если исходный запрос от клиента либо не содержит этого значения, либо предоставляет значение, которое не соответствует ожидаемому серверу, ожидаемое значение отправляется от сервера клиенту в webSocket.

Проверка подлинности

Как предоставить учетные данные проверки подлинности при подключении по сети.

Предоставление сертификата клиента с классом StreamSocket

Класс Windows.Networking.Sockets.StreamSocket поддерживает возможность использования протокола SSL или TLS для проверки подлинности сервера, к которому обращается приложение. В некоторых случаях приложение также должно пройти проверку подлинности на сервере с помощью сертификата клиента TLS. В Windows 10 можно предоставить сертификат клиента на объект StreamSocket.Control (необходимо задать перед началом подтверждения TLS). Если сервер запрашивает сертификат клиента, Windows будет отвечать на предоставленный сертификат.

Ниже приведен фрагмент кода, показывающий, как реализовать это:

var socket = new StreamSocket();
Windows.Security.Cryptography.Certificates.Certificate certificate = await GetClientCert();
socket.Control.ClientCertificate = certificate;
await socket.ConnectAsync(destination, SocketProtectionLevel.Tls12);

Предоставление учетных данных проверки подлинности веб-службе

Сетевые API, позволяющие приложениям взаимодействовать с безопасными веб-службами, каждый предоставляет собственные методы для инициализации клиента или задания заголовка запроса с учетными данными проверки подлинности сервера и прокси-сервера. Каждый метод задается с объектом PasswordCredential , указывающим имя пользователя, пароль и ресурс, для которого используются эти учетные данные. В следующей таблице представлено сопоставление этих API:

WebSockets MessageWebSocketControl.ServerCredential
MessageWebSocketControl.ProxyCredential
StreamWebSocketControl.ServerCredential
StreamWebSocketControl.ProxyCredential
Background Transfer BackgroundDownloader.ServerCredential
BackgroundDownloader.ProxyCredential
BackgroundUploader.ServerCredential
BackgroundUploader.ProxyCredential
Syndication SyndicationClient(PasswordCredential)
SyndicationClient.ServerCredential
SyndicationClient.ProxyCredential
AtomPub AtomPubClient(PasswordCredential)
AtomPubClient.ServerCredential
AtomPubClient.ProxyCredential

Обработка сетевых исключений

В большинстве областей программирования исключение указывает на значительную проблему или сбой, вызванный некоторыми недостатками в программе. В сетевом программировании существует дополнительный источник исключений: сама сеть и характер сетевого взаимодействия. Сетевые коммуникации по сути ненадежны и подвержены неожиданному сбою. Для каждого из способов использования сети приложение должно поддерживать некоторые сведения о состоянии; и код приложения должен обрабатывать сетевые исключения, обновляя эти сведения о состоянии и инициируя соответствующую логику для повторного установления или повторения сбоев связи.

Когда универсальные приложения Windows вызывают исключение, обработчик исключений может получить более подробную информацию о причине исключения, чтобы лучше понять ошибку и принять соответствующие решения.

Каждая проекция языка поддерживает метод для получения более подробных сведений. Проекты исключений в качестве значения HRESULT в универсальных приложениях Windows. Файл Winerror.h содержит очень большой список возможных значений HRESULT , включая сетевые ошибки.

Сетевые API поддерживают различные методы получения подробных сведений о причине исключения.

  • Некоторые API предоставляют вспомогательный метод, который преобразует значение HRESULT из исключения в значение перечисления.
  • Другие API предоставляют метод для получения фактического значения HRESULT .