Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
WebSockets предоставляет механизм для быстрого, безопасного двустороннего взаимодействия между клиентом и сервером через Интернет с помощью HTTP(S) и поддержки как UTF-8, так и двоичных сообщений.
Согласно протоколу WebSocket, данные передаются немедленно по одному полнодуплексному сокетному соединению, что позволяет обеим конечным точкам отправлять и получать сообщения в режиме реального времени. WebSockets идеально подходят для многопользовательских игр (как в реальном времени, так и пошаговых), мгновенных уведомлений в социальных сетях, отображения актуальной информации о курсах акций или погоде, а также других приложений, требующих безопасной и быстрой передачи данных.
Чтобы установить соединение WebSocket, между клиентом и сервером выполняется определённое рукопожатие на основе HTTP. В случае успешного выполнения протокол прикладного уровня "обновляется" с HTTP до WebSocket, используя ранее установленное соединение TCP. После этого HTTP больше не задействован. Данные могут быть отправлены или получены с использованием протокола WebSocket любой конечной точкой в любое время до завершения подключения WebSocket.
Примечание Клиент не может использовать WebSockets для передачи данных, если сервер также не использует протокол WebSocket. Если сервер не поддерживает WebSockets, необходимо использовать другой метод передачи данных.
Windows обеспечивает поддержку использования как клиента, так и сервера WebSockets. Пространство имен Windows.Networking.Sockets определяет два класса WebSocket, используемые клиентами, — MessageWebSocket и StreamWebSocket. Ниже приведено сравнение этих двух классов WebSocket.
| MessageWebSocket | StreamWebSocket |
|---|---|
| Целое сообщение WebSocket считывается и записывается в одну операцию. | Разделы сообщения можно считывать с каждой операцией чтения. |
| Подходит, если сообщения не очень большие. | Подходит, когда передаются очень большие файлы (например, фотографии или видео). |
| Поддерживает как UTF-8, так и двоичные сообщения. | Поддерживает только двоичные сообщения. |
| Аналогично UDP-сокету или сокету дейтаграмм (в том смысле, что он предназначен для частых небольших сообщений), но с надёжностью TCP, гарантией порядка пакетов и контролем перегрузки. | Аналогично сокету TCP или stream. |
Защита подключения с помощью TLS/SSL
В большинстве случаев необходимо использовать безопасное подключение WebSocket, чтобы данные, отправляемые и полученные, шифрулись. Это также увеличит вероятность успешного подключения, так как многие посредники, такие как брандмауэры и прокси-серверы, отклоняют незашифрованные подключения WebSocket. Протокол WebSocket определяет эти две схемы URI.
| Схема URI | Purpose |
|---|---|
| Wss: | Используется для безопасных подключений, которые должны быть зашифрованы. |
| Ws: | Используется для незашифрованных подключений. |
Чтобы зашифровать подключение WebSocket, используйте схему wss: URI. Приведем пример.
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" });
}
Подключение с помощью MessageWebSocket
MessageWebSocket позволяет выполнять чтение и запись всего сообщения WebSocket в одной операции. Следовательно, это подходит, когда сообщения не очень большие. Класс поддерживает как сообщения UTF-8, так и двоичные сообщения.
Приведенный ниже пример кода подключается к серверу WebSocket и отправляет сообщение. Общедоступная служба echo.websocket.org больше не предоставляется, поэтому замените URI-заполнитель в этих примерах на URI своего сервера 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.
}
Обработайте события MessageWebSocket.MessageReceived и MessageWebSocket.Closed
Как показано в приведенном выше примере, перед установкой подключения и отправкой данных с помощью MessageWebSocket следует подписаться на события MessageWebSocket.MessageReceived и MessageWebSocket.Closed .
MessageReceived срабатывает при получении данных. Доступ к данным можно получить через MessageWebSocketMessageReceivedEventArgs. Закрытый вызывается, когда клиент или сервер закрывает сокет.
Отправка данных в MessageWebSocket
После установки подключения можно отправить данные на сервер. Это можно сделать с помощью свойства MessageWebSocket.OutputStream и DataWriter для записи данных.
ПримечаниеDataWriter берет на себя ответственность за выходной поток. Когда DataWriter выходит из области видимости, если с ним связан выходной поток, DataWriter освобождает выходной поток. После этого последующие попытки использовать выходной поток завершаются ошибкой с значением HRESULT 0x80000013. Но вы можете вызвать DataWriter.DetachStream , чтобы отключить выходной поток от DataWriter и вернуть владение потоком в MessageWebSocket.
Подключение с помощью StreamWebSocket
StreamWebSocket позволяет считывать разделы сообщения с каждой операцией чтения. Следовательно, это подходит, когда передаются очень большие файлы (например, фотографии или видео). Класс поддерживает только двоичные сообщения.
Приведенный ниже пример кода подключается к серверу WebSocket и отправляет сообщение. Общедоступная служба echo.websocket.org больше не поддерживается, поэтому в этих примерах также используется URI-заполнитель, который следует заменить URI своего сервера 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.
}
Обработайте событие StreamWebSocket.Closed
Перед установкой подключения и отправкой данных с помощью StreamWebSocket необходимо подписаться на событие StreamWebSocket.Closed . Закрытый вызывается, когда клиент или сервер закрывает сокет.
Отправка данных в StreamWebSocket
После установки подключения можно отправить данные на сервер. Это можно сделать с помощью свойства StreamWebSocket.OutputStream и DataWriter для записи данных.
Примечание Если вы хотите написать дополнительные данные в одном сокете, обязательно вызовите DataWriter.DetachStream , чтобы отключить выходной поток от DataWriter , прежде чем DataWriter выходит из области. Это возвращает владение потоком в StreamWebSocket.
Получать данные через StreamWebSocket
Используйте свойство StreamWebSocket.InputStream и DataReader для чтения данных.
Дополнительные параметры MessageWebSocket и StreamWebSocket
Перед установкой подключения можно задать дополнительные параметры сокета, задав свойства в MessageWebSocketControl или StreamWebSocketControl. Доступ к экземпляру этих классов осуществляется из самого объекта сокета с помощью свойства MessageWebSocket.Control или свойства StreamWebSocket.Control в соответствии с соответствующим образом.
Ниже приведен пример использования StreamWebSocket. Тот же шаблон применяется к 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")));
Примечание Не пытайтесь изменить свойство элемента управления после вызова ConnectAsync. Единственным исключением из этого правила является MessageWebSocketControl.MessageType.
Классы информации WebSocket
MessageWebSocket и StreamWebSocket имеют соответствующий класс, предоставляющий дополнительные сведения об объекте.
MessageWebSocketInformation предоставляет сведения о MessageWebSocket и извлекаете экземпляр из него с помощью свойства MessageWebSocket.Information .
StreamWebSocketInformation предоставляет сведения о StreamWebSocket и извлекаете экземпляр из него с помощью свойства StreamWebSocket.Information .
Обратите внимание, что свойства этих классов информации доступны только для чтения, но их можно использовать для получения информации в любое время в течение времени существования объекта веб-сокета.
Обработка исключений
Ошибка, обнаруженная в операции MessageWebSocket или StreamWebSocket , возвращается в качестве значения HRESULT . Вы можете передать это значение HRESULT методу WebSocketError.GetStatus , чтобы преобразовать его в значение перечисления WebErrorStatus .
Большинство значений перечисления WebErrorStatus соответствуют ошибке, возвращаемой операцией собственного клиента HTTP. Приложение может переключаться на значения перечисления WebErrorStatus , чтобы изменить поведение приложения в зависимости от причины исключения.
При ошибках проверки параметров можно использовать значение HRESULT из исключения, чтобы получить более подробные сведения об ошибке. Возможные значения HRESULT перечислены в Winerror.h, который можно найти в составе установленного SDK (например, в папке C:\Program Files (x86)\Windows Kits\10\Include\<VERSION>\shared). Для большинства ошибок проверки параметров возвращаемое значение HRESULT — E_INVALIDARG.
Настройка времени ожидания для операций WebSocket
MessageWebSocket и StreamWebSocket используют внутреннюю системную службу для отправки клиентских запросов WebSocket и получения ответов с сервера. Значение времени ожидания по умолчанию, используемое для операции подключения WebSocket, составляет 60 секунд. Если HTTP-сервер, поддерживающий WebSockets, не может отвечать на запрос подключения WebSocket (он временно отключен или заблокирован сетевым сбоем), внутренняя системная служба ожидает по умолчанию 60 секунд, прежде чем она возвращает ошибку. Эта ошибка приводит к возникновению исключения в методе WebSocket ConnectAsync . Для операций отправки и получения после установки подключения WebSocket время ожидания по умолчанию составляет 30 секунд.
Если запрос имени HTTP-сервера в URI возвращает несколько IP-адресов для имени, то внутренняя системная служба пытается до 5 IP-адресов для сайта (каждый из которых имеет время ожидания по умолчанию 60 секунд) до сбоя. Следовательно, приложение может ждать несколько минут, пытаясь подключиться к нескольким IP-адресам, прежде чем он обрабатывает исключение. Это поведение может показаться пользователю, как приложение перестало работать.
Чтобы сделать приложение более адаптивным и свести к минимуму эти проблемы, можно задать более короткое время ожидания при запросах на подключение. Вы устанавливаете время ожидания аналогичным образом для MessageWebSocket и 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.
}
}
Важные API
- Datareader
- DataWriter
- DataWriter.DetachStream
- MessageWebSocket
- MessageWebSocket.Closed
- MessageWebSocket.ConnectAsync
- MessageWebSocket.Control
- MessageWebSocket.Information
- MessageWebSocket.MessageReceived
- MessageWebSocket.OutputStream
- MessageWebSocketControl
- MessageWebSocketControl.MessageType
- MessageWebSocketInformation
- MessageWebSocketMessageReceivedEventArgs
- SocketMessageType
- StreamWebSocket
- StreamWebSocket.Closed
- StreamSocket.ConnectAsync
- StreamWebSocket.Control
- StreamWebSocket.Information
- StreamWebSocket.InputStream
- StreamWebSocket.OutputStream
- StreamWebSocketControl
- StreamWebSocketInformation
- WebErrorStatus
- WebSocketError.GetStatus
- Windows.Networking.Sockets
Связанные темы
Samples
Windows developer