WebSockets

Protokoły WebSocket zapewniają mechanizm szybkiej, bezpiecznej dwukierunkowej komunikacji między klientem a serwerem za pośrednictwem sieci Web przy użyciu protokołu HTTP(S) i obsługują zarówno komunikaty UTF-8, jak i binarne.

W ramach protokołu WebSocket dane są przesyłane natychmiast za pośrednictwem połączenia z jednym gniazdem pełnodupleksowym, co umożliwia wysyłanie i odbieranie komunikatów z obu punktów końcowych w czasie rzeczywistym. WebSockety doskonale nadają się do zastosowań w grach wieloosobowych (zarówno czasu rzeczywistego, jak i turowych), natychmiastowych powiadomieniach w sieciach społecznościowych, bieżącym wyświetlaniu informacji o kursach akcji lub pogodzie oraz innych aplikacjach wymagających bezpiecznego i szybkiego przesyłu danych.

Aby ustanowić połączenie WebSocket, między klientem a serwerem dochodzi do określonego uzgadniania połączenia opartego na protokole HTTP. W przypadku powodzenia protokół warstwy aplikacji jest "uaktualniony" z protokołu HTTP do protokołu WebSockets przy użyciu wcześniej ustanowionego połączenia TCP. Gdy tak się dzieje, protokół HTTP jest całkowicie poza obrazem; dane mogą być wysyłane lub odbierane przy użyciu protokołu WebSocket przez oba punkty końcowe do momentu zamknięcia połączenia protokołu WebSocket.

Uwaga Klient nie może używać protokołu WebSocket do przesyłania danych, chyba że serwer również korzysta z protokołu WebSocket. Jeśli serwer nie obsługuje obiektów WebSocket, należy użyć innej metody transferu danych.

Windows zapewnia obsługę WebSocket zarówno po stronie klienta, jak i serwera. Przestrzeń nazw Windows.Networking.Sockets definiuje dwie klasy WebSocket do użytku przez klientów — MessageWebSocket i StreamWebSocket. Oto porównanie tych dwóch klas protokołu WebSocket.

MessageWebSocket StreamWebSocket
Cały komunikat protokołu WebSocket jest odczytywany/zapisywany w jednej operacji. Sekcje komunikatu można odczytywać przy użyciu każdej operacji odczytu.
Odpowiednie, gdy komunikaty nie są zbyt duże. Nadaje się do przesyłania bardzo dużych plików (takich jak zdjęcia lub wideo).
Obsługuje zarówno komunikaty UTF-8, jak i binarne. Obsługuje tylko komunikaty binarne.
Podobnie jak gniazdo UDP lub gniazdo datagramowe (w tym sensie, że jest przeznaczone do częstych, małych wiadomości), ale z niezawodnością protokołu TCP, gwarancją kolejności pakietów i kontrolą przeciążenia. Podobnie jak gniazdo TCP lub gniazdo strumieniowe.

Zabezpieczanie połączenia przy użyciu protokołu TLS/SSL

W większości przypadków należy użyć bezpiecznego połączenia protokołu WebSocket, aby wysyłane i odbierane dane są szyfrowane. Zwiększy to również prawdopodobieństwo pomyślnego nawiązania połączenia, ponieważ wielu pośredników, takich jak zapory i serwery proxy, odrzuca niezaszyfrowane połączenia protokołu WebSocket. Protokół WebSocket definiuje te dwa schematy identyfikatorów URI.

Schemat identyfikatora URI Purpose
Wss: Służy do bezpiecznych połączeń, które powinny być szyfrowane.
Ws: Służy do nieszyfrowanych połączeń.

Aby zaszyfrować połączenie protokołu WebSocket, użyj schematu identyfikatora wss: URI. Oto przykład.

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    var webSocket = new Windows.Networking.Sockets.MessageWebSocket();
    await webSocket.ConnectAsync(new Uri("wss://www.contoso.com/mywebservice"));
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <sstream>

using namespace winrt;
using namespace Windows::Foundation;
...
IAsyncAction ConnectWebSocketAsync()
{
    Windows::Networking::Sockets::MessageWebSocket webSocket;
    co_await webSocket.ConnectAsync(Uri{ L"wss://www.contoso.com/mywebservice" });
}

Nawiązywanie połączenia przy użyciu protokołu MessageWebSocket

Funkcja MessageWebSocket umożliwia odczytywanie/zapisywanie całego komunikatu protokołu WebSocket w jednej operacji. W związku z tym jest to odpowiednie, gdy komunikaty nie są zbyt duże. Klasa obsługuje zarówno komunikaty UTF-8, jak i binarne.

Poniższy przykładowy kod łączy się z serwerem Protokołu WebSocket i wysyła komunikat. Publiczna usługa echo.websocket.org została wycofana, dlatego w tych przykładach zastąp zastępczy identyfikator URI własnym identyfikatorem URI serwera WebSocket.

private Windows.Networking.Sockets.MessageWebSocket messageWebSocket;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.messageWebSocket = new Windows.Networking.Sockets.MessageWebSocket();

    // In this example, we send/receive a string, so we need to set the MessageType to Utf8.
    this.messageWebSocket.Control.MessageType = Windows.Networking.Sockets.SocketMessageType.Utf8;

    this.messageWebSocket.MessageReceived += WebSocket_MessageReceived;
    this.messageWebSocket.Closed += WebSocket_Closed;

    try
    {
        // Replace with your WebSocket server URI.
        Task connectTask = this.messageWebSocket.ConnectAsync(new Uri("wss://example.com/ws")).AsTask();
        connectTask.ContinueWith(_ => this.SendMessageUsingMessageWebSocketAsync("Hello, World!"));
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add additional code here to handle exceptions.
    }
}

private async Task SendMessageUsingMessageWebSocketAsync(string message)
{
    using (var dataWriter = new DataWriter(this.messageWebSocket.OutputStream))
    {
        dataWriter.WriteString(message);
        await dataWriter.StoreAsync();
        dataWriter.DetachStream();
    }
    Debug.WriteLine("Sending message using MessageWebSocket: " + message);
}

private void WebSocket_MessageReceived(Windows.Networking.Sockets.MessageWebSocket sender, Windows.Networking.Sockets.MessageWebSocketMessageReceivedEventArgs args)
{
    try
    {
        using (DataReader dataReader = args.GetDataReader())
        {
            dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
            string message = dataReader.ReadString(dataReader.UnconsumedBufferLength);
            Debug.WriteLine("Message received from MessageWebSocket: " + message);
            this.messageWebSocket.Dispose();
        }
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add additional code here to handle exceptions.
    }
}

private void WebSocket_Closed(Windows.Networking.Sockets.IWebSocket sender, Windows.Networking.Sockets.WebSocketClosedEventArgs args)
{
    Debug.WriteLine("WebSocket_Closed; Code: " + args.Code + ", Reason: \"" + args.Reason + "\"");
    // Add additional code here to handle the WebSocket being closed.
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::MessageWebSocket m_messageWebSocket;
    winrt::event_token m_messageReceivedEventToken;
    winrt::event_token m_closedEventToken;

public:
    IAsyncAction OnNavigatedTo(NavigationEventArgs /* e */)
    {
        // In this example, we send/receive a string, so we need to set the MessageType to Utf8.
        m_messageWebSocket.Control().MessageType(Windows::Networking::Sockets::SocketMessageType::Utf8);

        m_messageReceivedEventToken = m_messageWebSocket.MessageReceived({ this, &MessageWebSocketPage::OnWebSocketMessageReceived });
        m_closedEventToken = m_messageWebSocket.Closed({ this, &MessageWebSocketPage::OnWebSocketClosed });

        try
        {
            co_await m_messageWebSocket.ConnectAsync(Uri{ L"wss://example.com/ws" });
            SendMessageUsingMessageWebSocketAsync(L"Hello, World!");
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }

private:
    IAsyncAction SendMessageUsingMessageWebSocketAsync(std::wstring message)
    {
        DataWriter dataWriter{ m_messageWebSocket.OutputStream() };
        dataWriter.WriteString(message);

        co_await dataWriter.StoreAsync();
        dataWriter.DetachStream();
        std::wstringstream wstringstream;
        wstringstream << L"Sending message using MessageWebSocket: " << message.c_str() << std::endl;
        ::OutputDebugString(wstringstream.str().c_str());
    }

    void OnWebSocketMessageReceived(Windows::Networking::Sockets::MessageWebSocket const& /* sender */, Windows::Networking::Sockets::MessageWebSocketMessageReceivedEventArgs const& args)
    {
        try
        {
            DataReader dataReader{ args.GetDataReader() };

            dataReader.UnicodeEncoding(Windows::Storage::Streams::UnicodeEncoding::Utf8);
            auto message = dataReader.ReadString(dataReader.UnconsumedBufferLength());
            std::wstringstream wstringstream;
            wstringstream << L"Message received from MessageWebSocket: " << message.c_str() << std::endl;
            ::OutputDebugString(wstringstream.str().c_str());
            m_messageWebSocket.Close(1000, L"");
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }

    void OnWebSocketClosed(Windows::Networking::Sockets::IWebSocket const& /* sender */, Windows::Networking::Sockets::WebSocketClosedEventArgs const& args)
    {
        std::wstringstream wstringstream;
        wstringstream << L"WebSocket_Closed; Code: " << args.Code() << ", Reason: \"" << args.Reason().c_str() << "\"" << std::endl;
        ::OutputDebugString(wstringstream.str().c_str());
        // Add additional code here to handle the WebSocket being closed.
    }
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::MessageWebSocket^ messageWebSocket;

protected:
    virtual void OnNavigatedTo(NavigationEventArgs^ e) override
    {
        this->messageWebSocket = ref new Windows::Networking::Sockets::MessageWebSocket();

        // In this example, we send/receive a string, so we need to set the MessageType to Utf8.
        this->messageWebSocket->Control->MessageType = Windows::Networking::Sockets::SocketMessageType::Utf8;

        this->messageWebSocket->MessageReceived += ref new TypedEventHandler<Windows::Networking::Sockets::MessageWebSocket^, Windows::Networking::Sockets::MessageWebSocketMessageReceivedEventArgs^>(this, &MessageWebSocketPage::WebSocket_MessageReceived);
        this->messageWebSocket->Closed += ref new TypedEventHandler<Windows::Networking::Sockets::IWebSocket^, Windows::Networking::Sockets::WebSocketClosedEventArgs^>(this, &MessageWebSocketPage::WebSocket_Closed);

        try
        {
            auto connectTask = Concurrency::create_task(this->messageWebSocket->ConnectAsync(ref new Uri(L"wss://example.com/ws")));
            connectTask.then([this] { this->SendMessageUsingMessageWebSocketAsync(L"Hello, World!"); });
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

private:
    void SendMessageUsingMessageWebSocketAsync(Platform::String^ message)
    {
        auto dataWriter = ref new DataWriter(this->messageWebSocket->OutputStream);
        dataWriter->WriteString(message);

        Concurrency::create_task(dataWriter->StoreAsync()).then(
            [=](unsigned int)
        {
            dataWriter->DetachStream();
            std::wstringstream wstringstream;
            wstringstream << L"Sending message using MessageWebSocket: " << message->Data() << std::endl;
            ::OutputDebugString(wstringstream.str().c_str());
        });
    }

    void WebSocket_MessageReceived(Windows::Networking::Sockets::MessageWebSocket^ sender, Windows::Networking::Sockets::MessageWebSocketMessageReceivedEventArgs^ args)
    {
        try
        {
            DataReader^ dataReader = args->GetDataReader();

            dataReader->UnicodeEncoding = Windows::Storage::Streams::UnicodeEncoding::Utf8;
            Platform::String^ message = dataReader->ReadString(dataReader->UnconsumedBufferLength);
            std::wstringstream wstringstream;
            wstringstream << L"Message received from MessageWebSocket: " << message->Data() << std::endl;
            ::OutputDebugString(wstringstream.str().c_str());
            this->messageWebSocket->Close(1000, L"");
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

    void WebSocket_Closed(Windows::Networking::Sockets::IWebSocket^ sender, Windows::Networking::Sockets::WebSocketClosedEventArgs^ args)
    {
        std::wstringstream wstringstream;
        wstringstream << L"WebSocket_Closed; Code: " << args->Code << ", Reason: \"" << args->Reason->Data() << "\"" << std::endl;
        ::OutputDebugString(wstringstream.str().c_str());
        // Add additional code here to handle the WebSocket being closed.
    }

Obsługa zdarzeń MessageWebSocket.MessageReceived i MessageWebSocket.Closed

Jak pokazano w powyższym przykładzie, przed nawiązaniem połączenia i wysłaniem danych za pomocą protokołu MessageWebSocket należy zasubskrybować zdarzenia MessageWebSocket.MessageReceived i MessageWebSocket.Closed .

MessageReceived jest wywoływane po odebraniu danych. Dostęp do danych można uzyskać za pośrednictwem elementu MessageWebSocketMessageReceivedEventArgs. Zamknięcie jest wywoływane, gdy klient lub serwer zamyka gniazdo.

Wysyłanie danych w usłudze MessageWebSocket

Po nawiązaniu połączenia można wysyłać dane do serwera. W tym celu należy użyć właściwości MessageWebSocket.OutputStream i elementu DataWriter w celu zapisania danych.

UwagaAutor danych przejmuje własność strumienia wyjściowego. Gdy element DataWriter wykracza poza zakres, jeśli strumień wyjściowy jest do niego dołączony, funkcja DataWriter cofa przydział strumienia wyjściowego. Następnie kolejne próby użycia strumienia wyjściowego kończą się niepowodzeniem z wartością HRESULT 0x80000013. Możesz jednak wywołać metodę DataWriter.DetachStream , aby odłączyć strumień wyjściowy od elementu DataWriter i zwrócić własność strumienia do obiektu MessageWebSocket.

Nawiązywanie połączenia przy użyciu usługi StreamWebSocket

Usługa StreamWebSocket umożliwia odczytywanie sekcji komunikatu przy użyciu każdej operacji odczytu. W związku z tym jest to odpowiednie, gdy są przesyłane bardzo duże pliki (takie jak zdjęcia lub wideo). Klasa obsługuje tylko komunikaty binarne.

Poniższy przykładowy kod łączy się z serwerem Protokołu WebSocket i wysyła komunikat. Publiczna usługa echo.websocket.org została wycofana, dlatego w tych przykładach również użyto zastępczego identyfikatora URI, który należy zastąpić własnym identyfikatorem URI serwera WebSocket.

private Windows.Networking.Sockets.StreamWebSocket streamWebSocket;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.streamWebSocket = new Windows.Networking.Sockets.StreamWebSocket();

    this.streamWebSocket.Closed += WebSocket_Closed;

    try
    {
        Task connectTask = this.streamWebSocket.ConnectAsync(new Uri("wss://example.com/ws")).AsTask();

        connectTask.ContinueWith(_ =>
        {
            Task.Run(() => this.ReceiveMessageUsingStreamWebSocket());
            Task.Run(() => this.SendMessageUsingStreamWebSocket(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }));
        });
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add code here to handle exceptions.
    }
}

private async void ReceiveMessageUsingStreamWebSocket()
{
    try
    {
        using (var dataReader = new DataReader(this.streamWebSocket.InputStream))
        {
            dataReader.InputStreamOptions = InputStreamOptions.Partial;
            await dataReader.LoadAsync(256);
            byte[] message = new byte[dataReader.UnconsumedBufferLength];
            dataReader.ReadBytes(message);
            Debug.WriteLine("Data received from StreamWebSocket: " + message.Length + " bytes");
        }
        this.streamWebSocket.Dispose();
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add code here to handle exceptions.
    }
}

private async void SendMessageUsingStreamWebSocket(byte[] message)
{
    try
    {
        using (var dataWriter = new DataWriter(this.streamWebSocket.OutputStream))
        {
            dataWriter.WriteBytes(message);
            await dataWriter.StoreAsync();
            dataWriter.DetachStream();
        }
        Debug.WriteLine("Sending data using StreamWebSocket: " + message.Length.ToString() + " bytes");
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add code here to handle exceptions.
    }
}

private void WebSocket_Closed(Windows.Networking.Sockets.IWebSocket sender, Windows.Networking.Sockets.WebSocketClosedEventArgs args)
{
    Debug.WriteLine("WebSocket_Closed; Code: " + args.Code + ", Reason: \"" + args.Reason + "\"");
    // Add additional code here to handle the WebSocket being closed.
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::StreamWebSocket m_streamWebSocket;
    winrt::event_token m_closedEventToken;

public:
    IAsyncAction OnNavigatedTo(NavigationEventArgs /* e */)
    {
        m_closedEventToken = m_streamWebSocket.Closed({ this, &StreamWebSocketPage::OnWebSocketClosed });

        try
        {
            co_await m_streamWebSocket.ConnectAsync(Uri{ L"wss://example.com/ws" });
            ReceiveMessageUsingStreamWebSocket();
            SendMessageUsingStreamWebSocket({ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 });
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }

private:
    IAsyncAction SendMessageUsingStreamWebSocket(std::vector< byte > message)
    {
        try
        {
            DataWriter dataWriter{ m_streamWebSocket.OutputStream() };
            dataWriter.WriteBytes(message);

            co_await dataWriter.StoreAsync();
            dataWriter.DetachStream();
            std::wstringstream wstringstream;
            wstringstream << L"Sending data using StreamWebSocket: " << message.size() << L" bytes" << std::endl;
            ::OutputDebugString(wstringstream.str().c_str());
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }

    IAsyncAction ReceiveMessageUsingStreamWebSocket()
    {
        try
        {
            DataReader dataReader{ m_streamWebSocket.InputStream() };
            dataReader.InputStreamOptions(InputStreamOptions::Partial);

            unsigned int bytesLoaded = co_await dataReader.LoadAsync(256);
            std::vector< byte > message(bytesLoaded);
            dataReader.ReadBytes(message);
            std::wstringstream wstringstream;
            wstringstream << L"Data received from StreamWebSocket: " << message.size() << " bytes" << std::endl;
            ::OutputDebugString(wstringstream.str().c_str());
            m_streamWebSocket.Close(1000, L"");
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }

    void OnWebSocketClosed(Windows::Networking::Sockets::IWebSocket const&, Windows::Networking::Sockets::WebSocketClosedEventArgs const& args)
    {
        std::wstringstream wstringstream;
        wstringstream << L"WebSocket_Closed; Code: " << args.Code() << ", Reason: \"" << args.Reason().c_str() << "\"" << std::endl;
        ::OutputDebugString(wstringstream.str().c_str());
        // Add additional code here to handle the WebSocket being closed.
    }
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::StreamWebSocket^ streamWebSocket;

protected:
    virtual void OnNavigatedTo(NavigationEventArgs^ e) override
    {
        this->streamWebSocket = ref new Windows::Networking::Sockets::StreamWebSocket();

        this->streamWebSocket->Closed += ref new TypedEventHandler<Windows::Networking::Sockets::IWebSocket^, Windows::Networking::Sockets::WebSocketClosedEventArgs^>(this, &StreamWebSocketPage::WebSocket_Closed);

        try
        {
            auto connectTask = Concurrency::create_task(this->streamWebSocket->ConnectAsync(ref new Uri(L"wss://example.com/ws")));

            connectTask.then(
                [=]
            {
                this->ReceiveMessageUsingStreamWebSocket();
                this->SendMessageUsingStreamWebSocket(ref new Platform::Array< byte >{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 });
            });
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

private:
    void SendMessageUsingStreamWebSocket(const Platform::Array< byte >^ message)
    {
        try
        {
            auto dataWriter = ref new DataWriter(this->streamWebSocket->OutputStream);
            dataWriter->WriteBytes(message);

            Concurrency::create_task(dataWriter->StoreAsync()).then(
                [=](Concurrency::task< unsigned int >) // task< unsigned int > instead of unsigned int in order to handle any exceptions thrown in StoreAsync().
            {
                dataWriter->DetachStream();
                std::wstringstream wstringstream;
                wstringstream << L"Sending data using StreamWebSocket: " << message->Length << L" bytes" << std::endl;
                ::OutputDebugString(wstringstream.str().c_str());
            });
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

    void ReceiveMessageUsingStreamWebSocket()
    {
        try
        {
            DataReader^ dataReader = ref new DataReader(this->streamWebSocket->InputStream);
            dataReader->InputStreamOptions = InputStreamOptions::Partial;

            Concurrency::create_task(dataReader->LoadAsync(256)).then(
                [=](unsigned int bytesLoaded)
            {
                auto message = ref new Platform::Array< byte >(bytesLoaded);
                dataReader->ReadBytes(message);
                std::wstringstream wstringstream;
                wstringstream << L"Data received from StreamWebSocket: " << message->Length << " bytes" << std::endl;
                ::OutputDebugString(wstringstream.str().c_str());
                this->streamWebSocket->Close(1000, L"");
            });
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

    void WebSocket_Closed(Windows::Networking::Sockets::IWebSocket^ sender, Windows::Networking::Sockets::WebSocketClosedEventArgs^ args)
    {
        std::wstringstream wstringstream;
        wstringstream << L"WebSocket_Closed; Code: " << args->Code << ", Reason: \"" << args->Reason->Data() << "\"" << std::endl;
        ::OutputDebugString(wstringstream.str().c_str());
        // Add additional code here to handle the WebSocket being closed.
    }

Obsługa zdarzenia StreamWebSocket.Closed

Przed nawiązaniem połączenia i wysłaniem danych za pomocą protokołu StreamWebSocket należy zasubskrybować zdarzenie StreamWebSocket.Closed . Zamknięcie jest wywoływane, gdy klient lub serwer zamyka gniazdo.

Wysyłanie danych w usłudze StreamWebSocket

Po nawiązaniu połączenia można wysyłać dane do serwera. W tym celu należy użyć właściwości StreamWebSocket.OutputStream i DataWriter w celu zapisania danych.

Uwaga Jeśli chcesz zapisać kolejne dane do tego samego gniazda, pamiętaj, aby wywołać metodę DataWriter.DetachStream, aby odłączyć strumień wyjściowy od DataWriter, zanim obiekt DataWriter wyjdzie poza zakres. Spowoduje to zwrócenie własności strumienia do obiektu StreamWebSocket.

Odbieranie danych w usłudze StreamWebSocket

Aby odczytać dane, użyj właściwości StreamWebSocket.InputStream i elementu DataReader.

Zaawansowane opcje dla MessageWebSocket i StreamWebSocket

Przed nawiązaniem połączenia można ustawić opcje zaawansowane dla gniazda, ustawiając właściwości w elemecie MessageWebSocketControl lub StreamWebSocketControl. Uzyskujesz dostęp do wystąpienia tych klas z poziomu samego obiektu gniazda, odpowiednio za pośrednictwem właściwości MessageWebSocket.Control lub StreamWebSocket.Control.

Oto przykład użycia usługi StreamWebSocket. Ten sam wzorzec dotyczy protokołu MessageWebSocket.

var streamWebSocket = new Windows.Networking.Sockets.StreamWebSocket();

// By default, the Nagle algorithm is not used. This overrides that, and causes it to be used.
streamWebSocket.Control.NoDelay = false;

await streamWebSocket.ConnectAsync(new Uri("wss://example.com/ws"));
Windows::Networking::Sockets::StreamWebSocket streamWebSocket;

// By default, the Nagle algorithm is not used. This overrides that, and causes it to be used.
streamWebSocket.Control().NoDelay(false);

auto connectAsyncAction = streamWebSocket.ConnectAsync(Uri{ L"wss://example.com/ws" });
auto streamWebSocket = ref new Windows::Networking::Sockets::StreamWebSocket();

// By default, the Nagle algorithm is not used. This overrides that, and causes it to be used.
streamWebSocket->Control->NoDelay = false;

auto connectTask = Concurrency::create_task(streamWebSocket->ConnectAsync(ref new Uri(L"wss://example.com/ws")));

Uwaga Nie próbuj zmieniać właściwości kontrolki po wywołaniu narzędzia ConnectAsync. Jedynym wyjątkiem od tej reguły jest MessageWebSocketControl.MessageType.

Klasy informacji protokołu WebSocket

Zarówno MessageWebSocket, jak i StreamWebSocket mają odpowiadającą im klasę, która zawiera dodatkowe informacje o obiekcie.

MessageWebSocketInformation udostępnia informacje o obiekcie MessageWebSocket, a jego instancję można pobrać przy użyciu właściwości MessageWebSocket.Information.

StreamWebSocketInformation udostępnia informacje o obiekcie StreamWebSocket, a jego wystąpienie można uzyskać za pomocą właściwości StreamWebSocket.Information.

Należy pamiętać, że właściwości tych klas informacji są tylko do odczytu, ale można ich używać do pobierania informacji w dowolnym momencie w okresie istnienia obiektu gniazda sieci Web.

Obsługa wyjątków

Błąd napotkany w operacji MessageWebSocket lub StreamWebSocket jest zwracany jako wartość HRESULT . Możesz przekazać tę wartość HRESULT do metody WebSocketError.GetStatus , aby przekonwertować ją na wartość wyliczenia WebErrorStatus .

Większość wartości wyliczenia WebErrorStatus odpowiada błędowi zwróconego przez natywną operację klienta HTTP. Aplikacja może włączyć wartości wyliczenia WebErrorStatus , aby zmodyfikować zachowanie aplikacji w zależności od przyczyny wyjątku.

W przypadku błędów walidacji parametrów można użyć hrESULT z wyjątku, aby dowiedzieć się więcej szczegółowych informacji na temat błędu. Możliwe wartości HRESULT są wymienione w pliku Winerror.h, który można znaleźć w instalacji zestawu SDK (na przykład w folderze C:\Program Files (x86)\Windows Kits\10\Include\<VERSION>\shared). W przypadku większości błędów sprawdzania poprawności parametrów zwracanym kodem jest HRESULTE_INVALIDARG.

Ustawianie limitów czasu w operacjach protokołu WebSocket

Usługa MessageWebSocket i StreamWebSocket używają wewnętrznej usługi systemowej do wysyłania żądań klientów protokołu WebSocket i odbierania odpowiedzi z serwera. Domyślna wartość limitu czasu używana dla operacji łączenia protokołu WebSocket wynosi 60 sekund. Jeśli serwer HTTP, który obsługuje protokoły WebSocket, nie odpowiada na żądanie połączenia protokołu WebSocket (jest tymczasowo wyłączony lub zablokowany przez awarię sieci), wewnętrzna usługa systemowa czeka domyślnie 60 sekund, zanim zwróci błąd. Ten błąd powoduje zgłoszenie wyjątku w metodzie WebSocket ConnectAsync . W przypadku operacji wysyłania i odbierania po nawiązaniu połączenia protokołu WebSocket domyślny limit czasu wynosi 30 sekund.

Jeśli zapytanie DNS dotyczące nazwy serwera HTTP w URI zwraca dla tej nazwy wiele adresów IP, wewnętrzna usługa systemowa podejmuje próbę połączenia z maksymalnie 5 adresami IP witryny (każdy z domyślnym limitem czasu 60 sekund), zanim operacja zakończy się niepowodzeniem. W związku z tym aplikacja może poczekać kilka minut na próbę nawiązania połączenia z wieloma adresami IP, zanim obsłuży wyjątek. Takie zachowanie może sprawiać użytkownikowi wrażenie, że aplikacja przestała działać.

Aby zwiększyć szybkość reakcji aplikacji i zminimalizować te problemy, możesz ustawić krótszy limit czasu dla żądań połączeń. Limit czasu można ustawić w podobny sposób dla protokołu MessageWebSocket i StreamWebSocket.

private Windows.Networking.Sockets.MessageWebSocket messageWebSocket;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.messageWebSocket = new Windows.Networking.Sockets.MessageWebSocket();

    try
    {
        var cancellationTokenSource = new CancellationTokenSource();
        var connectTask = this.messageWebSocket.ConnectAsync(new Uri("wss://example.com/ws")).AsTask(cancellationTokenSource.Token);

        // Cancel connectTask after 5 seconds.
        cancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(5000));

        connectTask.ContinueWith((antecedent) =>
        {
            if (antecedent.Status == TaskStatus.RanToCompletion)
            {
                // connectTask ran to completion, so we know that the MessageWebSocket is connected.
                // Add additional code here to use the MessageWebSocket.
            }
            else
            {
                // connectTask timed out, or faulted.
            }
        });
    }
    catch (Exception ex)
    {
        Windows.Web.WebErrorStatus webErrorStatus = Windows.Networking.Sockets.WebSocketError.GetStatus(ex.GetBaseException().HResult);
        // Add additional code here to handle exceptions.
    }
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::MessageWebSocket m_messageWebSocket;

    IAsyncAction TimeoutAsync()
    {
        // Return control to the caller, and resume again to complete the async action after the timeout period.
        // 5 seconds, in this example.
        co_await(std::chrono::seconds{ 5 });
    }

public:
    IAsyncAction OnNavigatedTo(NavigationEventArgs /* e */)
    {
        try
        {
            // Return control to the caller, and then immediately resume on a thread pool thread.
            co_await winrt::resume_background();

            auto connectAsyncAction = m_messageWebSocket.ConnectAsync(Uri{ L"wss://example.com/ws" });

            TimeoutAsync().Completed([connectAsyncAction](IAsyncAction const& sender, AsyncStatus const)
            {
                // TimeoutAsync completes after the timeout period. After that period, it's safe
                // to cancel the ConnectAsync action even if it has already completed.
                connectAsyncAction.Cancel();
            });

            try
            {
                // Block until the ConnectAsync action completes or is canceled.
                connectAsyncAction.get();
            }
            catch (winrt::hresult_error const& ex)
            {
                std::wstringstream wstringstream;
                wstringstream << L"ConnectAsync threw an exception: " << ex.message().c_str() << std::endl;
                ::OutputDebugString(wstringstream.str().c_str());
            }

            if (connectAsyncAction.Status() == AsyncStatus::Completed)
            {
                // connectTask ran to completion, so we know that the MessageWebSocket is connected.
                // Add additional code here to use the MessageWebSocket.
            }
            else
            {
                // connectTask did not run to completion.
            }
        }
        catch (winrt::hresult_error const& ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus{ Windows::Networking::Sockets::WebSocketError::GetStatus(ex.to_abi()) };
            // Add additional code here to handle exceptions.
        }
    }
#include <agents.h>
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Navigation;
...
private:
    Windows::Networking::Sockets::MessageWebSocket^ messageWebSocket;

protected:
    virtual void OnNavigatedTo(NavigationEventArgs^ e) override
    {
        this->messageWebSocket = ref new Windows::Networking::Sockets::MessageWebSocket();

        try
        {
            Concurrency::cancellation_token_source cancellationTokenSource;
            Concurrency::cancellation_token cancellationToken = cancellationTokenSource.get_token();

            auto connectTask = Concurrency::create_task(this->messageWebSocket->ConnectAsync(ref new Uri(L"wss://example.com/ws")), cancellationToken);

            // This continuation task returns true should connectTask run to completion.
            Concurrency::task< bool > taskRanToCompletion = connectTask.then([](void)
            {
                return true;
            });

            // This task returns false after the specified timeout. 5 seconds, in this example.
            Concurrency::task< bool > taskTimedout = Concurrency::create_task([]() -> bool
            {
                Concurrency::task_completion_event< void > taskCompletionEvent;

                // A call object that sets the task completion event.
                auto call = std::make_shared< Concurrency::call< int > >([taskCompletionEvent](int)
                {
                    taskCompletionEvent.set();
                });

                // A non-repeating timer that calls the call object when the timer fires.
                auto nonRepeatingTimer = std::make_shared< Concurrency::timer < int > >(5000, 0, call.get(), false);
                nonRepeatingTimer->start();

                // A task that completes after the completion event is set.
                Concurrency::task< void > taskWaitForCompletionEvent(taskCompletionEvent);

                return taskWaitForCompletionEvent.then([]() {return false; }).get();
            });

            (taskRanToCompletion || taskTimedout).then([this, cancellationTokenSource](bool connectTaskRanToCompletion)
            {
                if (connectTaskRanToCompletion)
                {
                    // connectTask ran to completion, so we know that the MessageWebSocket is connected.
                    // Add additional code here to use the MessageWebSocket.
                }
                else
                {
                    // taskTimedout ran to completion, so we should cancel connectTask via the cancellation_token_source.
                    cancellationTokenSource.cancel();
                }
            });
        }
        catch (Platform::Exception^ ex)
        {
            Windows::Web::WebErrorStatus webErrorStatus = Windows::Networking::Sockets::WebSocketError::GetStatus(ex->HResult);
            // Add additional code here to handle exceptions.
        }
    }

Ważne interfejsy API

Samples