ソケットは、多くのネットワーク プロトコルが実装されている上に、低レベルのデータ転送テクノロジです。 Windowsは、接続が有効期間が長いか確立された接続が不要であるかに関係なく、クライアント/サーバーまたはピアツーピア アプリケーション用の TCP および UDP ソケット クラスを提供します。
このトピックでは、WindowsにあるWindows ソケット クラスの使用方法について説明します。Networking.Sockets 名前空間。 ただし、Windows アプリで Windows Sockets 2 (Winsock) を使用することもできます。
Note
ネットワークの分離の結果として、Windowsは、同じコンピューター上で実行されている 2 つのWindows アプリ間のソケット接続 (Sockets または WinSock) の確立を禁止します。ローカル ループバック アドレス (127.0.0.1) を使用するか、ローカル IP アドレスを明示的に指定します。 Windowsアプリが相互に通信できるメカニズムの詳細については、「アプリ間通信」を参照してください。
基本的な TCP ソケット クライアントとサーバーを構築する
TCP (伝送制御プロトコル) ソケットは、有効期間が長い接続に対して、双方向に低レベルのネットワーク データ転送を提供します。 TCP ソケットは、インターネットで使用されるほとんどのネットワーク プロトコルで使用される基になる機能です。 基本的な TCP 操作を示すために、次のコード例は 、StreamSocket と StreamSocketListener が TCP 経由でデータを送受信してエコー クライアントとサーバーを形成することを示しています。
できるだけ少ない可動部分から始めて、現在のネットワーク分離の問題を回避するために、新しいプロジェクトを作成し、以下のクライアントとサーバー コードの両方を同じプロジェクトに配置します。
プロジェクトで アプリ機能を宣言する 必要があります。 アプリ パッケージ マニフェスト ソース ファイル ( Package.appxmanifest ファイル) を開き、[機能] タブで [ プライベート ネットワーク (クライアントとサーバー)] をオンにします。 これは、 Package.appxmanifest マークアップでの外観です。
<Capability Name="privateNetworkClientServer" />
インターネット経由で接続している場合は、 privateNetworkClientServerではなく、 internetClientServer を宣言できます。
StreamSocket と StreamSocketListener の両方で、これらのアプリ機能のいずれかを宣言する必要があります。
TCP ソケットを使用したエコー クライアントとサーバー
StreamSocketListener を作成し、着信 TCP 接続の待ち受けを開始します。 StreamSocketListener.ConnectionReceived イベントは、クライアントが StreamSocketListener との接続を確立するたびに発生します。
また、 StreamSocket を構築し、サーバーへの接続を確立し、要求を送信して、応答を受信します。
という名前の新しいStreamSocketAndListenerPageを作成します。 XAML マークアップを StreamSocketAndListenerPage.xamlに配置し、命令型コードを StreamSocketAndListenerPage クラス内に配置します。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Margin="9.6,0" Style="{StaticResource TitleTextBlockStyle}" Text="TCP socket example"/>
<TextBlock Margin="7.2,0,0,0" Style="{StaticResource HeaderTextBlockStyle}" Text="StreamSocket & StreamSocketListener"/>
</StackPanel>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="9.6" Style="{StaticResource SubtitleTextBlockStyle}" Text="client"/>
<ListBox x:Name="clientListBox" Grid.Row="1" Margin="9.6"/>
<TextBlock Grid.Column="1" Margin="9.6" Style="{StaticResource SubtitleTextBlockStyle}" Text="server"/>
<ListBox x:Name="serverListBox" Grid.Column="1" Grid.Row="1" Margin="9.6"/>
</Grid>
</Grid>
// Every protocol typically has a standard port number. For example, HTTP is typically 80, FTP is 20 and 21, etc.
// For this example, we'll choose an arbitrary port number.
static string PortNumber = "1337";
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.StartServer();
this.StartClient();
}
private async void StartServer()
{
try
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
// The ConnectionReceived event is raised when connections are received.
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await streamSocketListener.BindServiceNameAsync(StreamSocketAndListenerPage.PortNumber);
this.serverListBox.Items.Add("server is listening...");
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.serverListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add(string.Format("server received the request: \"{0}\"", request)));
// Echo the request back as the response.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add(string.Format("server sent back the response: \"{0}\"", request)));
sender.Dispose();
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add("server closed its socket"));
}
private async void StartClient()
{
try
{
// Create the StreamSocket and establish a connection to the echo server.
using (var streamSocket = new Windows.Networking.Sockets.StreamSocket())
{
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("localhost");
this.clientListBox.Items.Add("client is trying to connect...");
await streamSocket.ConnectAsync(hostName, StreamSocketAndListenerPage.PortNumber);
this.clientListBox.Items.Add("client connected");
// Send a request to the echo server.
string request = "Hello, World!";
using (Stream outputStream = streamSocket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
this.clientListBox.Items.Add(string.Format("client sent the request: \"{0}\"", request));
// Read data from the echo server.
string response;
using (Stream inputStream = streamSocket.InputStream.AsStreamForRead())
{
using (StreamReader streamReader = new StreamReader(inputStream))
{
response = await streamReader.ReadLineAsync();
}
}
this.clientListBox.Items.Add(string.Format("client received the response: \"{0}\" ", response));
}
this.clientListBox.Items.Add("client closed its socket");
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.clientListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Microsoft::UI::Dispatching;
using namespace Microsoft::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::StreamSocketListener m_streamSocketListener;
Windows::Networking::Sockets::StreamSocket m_streamSocket;
public:
void OnNavigatedTo(NavigationEventArgs const& /* e */)
{
StartServer();
StartClient();
}
private:
IAsyncAction StartServer()
{
try
{
// The ConnectionReceived event is raised when connections are received.
m_streamSocketListener.ConnectionReceived({ this, &StreamSocketAndListenerPage::OnConnectionReceived });
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
// Every protocol typically has a standard port number. For example, HTTP is typically 80, FTP is 20 and 21, etc.
// For this example, we'll choose an arbitrary port number.
co_await m_streamSocketListener.BindServiceNameAsync(L"1337");
serverListBox().Items().Append(winrt::box_value(L"server is listening..."));
}
catch (winrt::hresult_error const& ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(ex.to_abi()) };
serverListBox().Items().Append(webErrorStatus != Windows::Networking::Sockets::SocketErrorStatus::Unknown ? winrt::box_value(winrt::to_hstring((int32_t)webErrorStatus)) : winrt::box_value(winrt::to_hstring(ex.to_abi())));
}
}
IAsyncAction OnConnectionReceived(Windows::Networking::Sockets::StreamSocketListener /* sender */, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
auto socket{ args.Socket() }; // Keep the socket referenced, and alive.
DataReader dataReader{ socket.InputStream() };
unsigned int bytesLoaded = co_await dataReader.LoadAsync(sizeof(unsigned int));
unsigned int stringLength = dataReader.ReadUInt32();
bytesLoaded = co_await dataReader.LoadAsync(stringLength);
winrt::hstring request = dataReader.ReadString(bytesLoaded);
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
std::wstringstream wstringstream;
wstringstream << L"server received the request: \"" << request.c_str() << L"\"";
serverListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
});
// Echo the request back as the response.
DataWriter dataWriter{ socket.OutputStream() };
dataWriter.WriteUInt32(request.size());
dataWriter.WriteString(request);
co_await dataWriter.StoreAsync();
dataWriter.DetachStream();
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
std::wstringstream wstringstream;
wstringstream << L"server sent back the response: \"" << request.c_str() << L"\"";
serverListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
});
m_streamSocketListener = nullptr;
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
serverListBox().Items().Append(winrt::box_value(L"server closed its socket"));
});
}
catch (winrt::hresult_error const& ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(ex.to_abi()) };
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
serverListBox().Items().Append(webErrorStatus != Windows::Networking::Sockets::SocketErrorStatus::Unknown ? winrt::box_value(winrt::to_hstring((int32_t)webErrorStatus)) : winrt::box_value(winrt::to_hstring(ex.to_abi())));
});
}
}
IAsyncAction StartClient()
{
try
{
// Establish a connection to the echo server.
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
Windows::Networking::HostName hostName{ L"localhost" };
clientListBox().Items().Append(winrt::box_value(L"client is trying to connect..."));
co_await m_streamSocket.ConnectAsync(hostName, L"1337");
clientListBox().Items().Append(winrt::box_value(L"client connected"));
// Send a request to the echo server.
DataWriter dataWriter{ m_streamSocket.OutputStream() };
winrt::hstring request{ L"Hello, World!" };
dataWriter.WriteUInt32(request.size());
dataWriter.WriteString(request);
co_await dataWriter.StoreAsync();
std::wstringstream wstringstream;
wstringstream << L"client sent the request: \"" << request.c_str() << L"\"";
clientListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
co_await dataWriter.FlushAsync();
dataWriter.DetachStream();
// Read data from the echo server.
DataReader dataReader{ m_streamSocket.InputStream() };
unsigned int bytesLoaded = co_await dataReader.LoadAsync(sizeof(unsigned int));
unsigned int stringLength = dataReader.ReadUInt32();
bytesLoaded = co_await dataReader.LoadAsync(stringLength);
winrt::hstring response{ dataReader.ReadString(bytesLoaded) };
wstringstream.str(L"");
wstringstream << L"client received the response: \"" << response.c_str() << L"\"";
clientListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
m_streamSocket = nullptr;
clientListBox().Items().Append(winrt::box_value(L"client closed its socket"));
}
catch (winrt::hresult_error const& ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(ex.to_abi()) };
serverListBox().Items().Append(webErrorStatus != Windows::Networking::Sockets::SocketErrorStatus::Unknown ? winrt::box_value(winrt::to_hstring((int32_t)webErrorStatus)) : winrt::box_value(winrt::to_hstring(ex.to_abi())));
}
}
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::StreamSocketListener^ streamSocketListener;
Windows::Networking::Sockets::StreamSocket^ streamSocket;
protected:
virtual void OnNavigatedTo(NavigationEventArgs^ e) override
{
this->StartServer();
this->StartClient();
}
private:
void StartServer()
{
try
{
this->streamSocketListener = ref new Windows::Networking::Sockets::StreamSocketListener();
// The ConnectionReceived event is raised when connections are received.
streamSocketListener->ConnectionReceived += ref new TypedEventHandler<Windows::Networking::Sockets::StreamSocketListener^, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^>(this, &StreamSocketAndListenerPage::StreamSocketListener_ConnectionReceived);
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
// Every protocol typically has a standard port number. For example, HTTP is typically 80, FTP is 20 and 21, etc.
// For this example, we'll choose an arbitrary port number.
Concurrency::create_task(streamSocketListener->BindServiceNameAsync(L"1337")).then(
[=]
{
this->serverListBox->Items->Append(L"server is listening...");
});
}
catch (Platform::Exception^ ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus = Windows::Networking::Sockets::SocketError::GetStatus(ex->HResult);
this->serverListBox->Items->Append(webErrorStatus.ToString() != L"Unknown" ? webErrorStatus.ToString() : ex->Message);
}
}
void StreamSocketListener_ConnectionReceived(Windows::Networking::Sockets::StreamSocketListener^ sender, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ args)
{
try
{
auto dataReader = ref new DataReader(args->Socket->InputStream);
Concurrency::create_task(dataReader->LoadAsync(sizeof(unsigned int))).then(
[=](unsigned int bytesLoaded)
{
unsigned int stringLength = dataReader->ReadUInt32();
Concurrency::create_task(dataReader->LoadAsync(stringLength)).then(
[=](unsigned int bytesLoaded)
{
Platform::String^ request = dataReader->ReadString(bytesLoaded);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]
{
std::wstringstream wstringstream;
wstringstream << L"server received the request: \"" << request->Data() << L"\"";
this->serverListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
}));
// Echo the request back as the response.
auto dataWriter = ref new DataWriter(args->Socket->OutputStream);
dataWriter->WriteUInt32(request->Length());
dataWriter->WriteString(request);
Concurrency::create_task(dataWriter->StoreAsync()).then(
[=](unsigned int)
{
dataWriter->DetachStream();
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]()
{
std::wstringstream wstringstream;
wstringstream << L"server sent back the response: \"" << request->Data() << L"\"";
this->serverListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
}));
delete this->streamSocketListener;
this->streamSocketListener = nullptr;
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]() {this->serverListBox->Items->Append(L"server closed its socket"); }));
});
});
});
}
catch (Platform::Exception^ ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus = Windows::Networking::Sockets::SocketError::GetStatus(ex->HResult);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]() {this->serverListBox->Items->Append(webErrorStatus.ToString() != L"Unknown" ? webErrorStatus.ToString() : ex->Message); }));
}
}
void StartClient()
{
try
{
// Create the StreamSocket and establish a connection to the echo server.
this->streamSocket = ref new Windows::Networking::Sockets::StreamSocket();
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
auto hostName = ref new Windows::Networking::HostName(L"localhost");
this->clientListBox->Items->Append(L"client is trying to connect...");
Concurrency::create_task(this->streamSocket->ConnectAsync(hostName, L"1337")).then(
[=](Concurrency::task< void >)
{
this->clientListBox->Items->Append(L"client connected");
// Send a request to the echo server.
auto dataWriter = ref new DataWriter(this->streamSocket->OutputStream);
auto request = ref new Platform::String(L"Hello, World!");
dataWriter->WriteUInt32(request->Length());
dataWriter->WriteString(request);
Concurrency::create_task(dataWriter->StoreAsync()).then(
[=](Concurrency::task< unsigned int >)
{
std::wstringstream wstringstream;
wstringstream << L"client sent the request: \"" << request->Data() << L"\"";
this->clientListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
Concurrency::create_task(dataWriter->FlushAsync()).then(
[=](Concurrency::task< bool >)
{
dataWriter->DetachStream();
// Read data from the echo server.
auto dataReader = ref new DataReader(this->streamSocket->InputStream);
Concurrency::create_task(dataReader->LoadAsync(sizeof(unsigned int))).then(
[=](unsigned int bytesLoaded)
{
unsigned int stringLength = dataReader->ReadUInt32();
Concurrency::create_task(dataReader->LoadAsync(stringLength)).then(
[=](unsigned int bytesLoaded)
{
Platform::String^ response = dataReader->ReadString(bytesLoaded);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]
{
std::wstringstream wstringstream;
wstringstream << L"client received the response: \"" << response->Data() << L"\"";
this->clientListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
delete this->streamSocket;
this->streamSocket = nullptr;
this->clientListBox->Items->Append(L"client closed its socket");
}));
});
});
});
});
});
}
catch (Platform::Exception^ ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus = Windows::Networking::Sockets::SocketError::GetStatus(ex->HResult);
this->serverListBox->Items->Append(webErrorStatus.ToString() != L"Unknown" ? webErrorStatus.ToString() : ex->Message);
}
}
C++ PPL 継続での StreamSocket への参照 (主に C++/CX に適用されます)
Note
C++/WinRT コルーチンを使用し、パラメーターを値渡しする場合、この問題は適用されません。 パラメーター渡しの推奨事項については、「 C++/WinRT を使用したコンカレンシーと非同期操作」を参照してください。
StreamSocket は、その入力/出力ストリームにアクティブな読み取り/書き込みがある限り存続します (たとえば、StreamSocketListener.ConnectionReceived イベント ハンドラーでアクセスできる StreamSocketListenerConnectionReceivedEventArgs.Socket を見てみましょう)。
DataReader.LoadAsync (またはReadAsync/WriteAsync/StoreAsync) を呼び出すと、LoadAsync の Completed イベント ハンドラー (存在する場合) の実行が完了するまで(ソケットの入力ストリームを介して) ソケットへの参照が保持されます。
並列パターン ライブラリ (PPL) では、タスクの継続は既定ではインラインでスケジュールされません。 つまり、継続タスク ( task::then()) を追加しても、継続タスクが完了ハンドラーとしてインラインで実行されるとは限りません。
void StreamSocketListener_ConnectionReceived(Windows::Networking::Sockets::StreamSocketListener^ sender, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ args)
{
auto dataReader = ref new DataReader(args->Socket->InputStream);
Concurrency::create_task(dataReader->LoadAsync(sizeof(unsigned int))).then(
[=](unsigned int bytesLoaded)
{
// Work in here isn't guaranteed to execute inline as the completion handler of the LoadAsync.
});
}
StreamSocket の観点では、継続の本体が実行される前に、完了ハンドラーの実行は完了しており(その時点でソケットは破棄可能な状態になります)。 したがって、その継続内でソケットを使いたい場合にソケットが破棄されないようにするには、(ラムダ キャプチャを介して) ソケットを直接参照して使用するか、継続内で引き続き args->Socket にアクセスすることで間接的に参照するか、または継続タスクがインラインで実行されるように強制する必要があります。
StreamSocket サンプルで動作している最初の手法 (ラムダ キャプチャ) を確認できます。 上記の 「基本的な TCP ソケット クライアントとサーバーの構築 」セクションの C++/CX コードでは、2 番目の手法を使用します。要求は応答としてエコー バックされ、最も内側の継続の 1 つから args->Socket にアクセスします。
3 番目の手法は、応答をエコーバックしない場合に適しています。
task_continuation_context::use_synchronous_execution() オプションを使用して、PPL が継続処理の本体をインラインで実行するよう強制します。 その方法を示すコード例を次に示します。
void StreamSocketListener_ConnectionReceived(Windows::Networking::Sockets::StreamSocketListener^ sender, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ args)
{
auto dataReader = ref new DataReader(args->Socket->InputStream);
Concurrency::create_task(dataReader->LoadAsync(sizeof(unsigned int))).then(
[=](unsigned int bytesLoaded)
{
unsigned int messageLength = dataReader->ReadUInt32();
Concurrency::create_task(dataReader->LoadAsync(messageLength)).then(
[=](unsigned int bytesLoaded)
{
Platform::String^ request = dataReader->ReadString(bytesLoaded);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]
{
std::wstringstream wstringstream;
wstringstream << L"server received the request: \"" << request->Data() << L"\"";
this->serverListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
}));
});
}, Concurrency::task_continuation_context::use_synchronous_execution());
}
この動作は、Windows内のすべてのソケットおよび WebSocket クラスに適用されます。Networking.Sockets 名前空間。 ただし、クライアント側のシナリオでは、通常、ソケットがメンバー変数に格納されるため、上に示すように、この問題は StreamSocketListener.ConnectionReceived シナリオに最も当てはまるものです。
基本的な UDP ソケット クライアントとサーバーを構築する
UDP (ユーザー データグラム プロトコル) ソケットは、どちらの方向でも低レベルのネットワーク データ転送を提供するという点で、TCP ソケットに似ています。 ただし、TCP ソケットは有効期間の長い接続用ですが、UDP ソケットは確立された接続が必要ないアプリケーション用です。 UDP ソケットは両方のエンドポイントで接続を維持しないため、リモート マシン間のネットワークのための高速でシンプルなソリューションです。 ただし、UDP ソケットでは、ネットワーク パケットの整合性も、パケットによってリモート宛先に送信されるかどうかも保証されません。 そのため、アプリはこれを許容するように設計する必要があります。 UDP ソケットを使用するアプリケーションの例としては、ローカル ネットワーク検出とローカル チャット クライアントがあります。
基本的な UDP 操作を示すために、次のコード例は、UDP 経由でデータを送受信してエコー クライアントとサーバーを形成するために使用される DatagramSocket クラスを示しています。 新しいプロジェクトを作成し、クライアントと以下のサーバー コードの両方を同じプロジェクトに配置します。 TCP ソケットの場合と同様に、 プライベート ネットワーク (クライアントとサーバー) アプリの機能を宣言する必要があります。
UDP ソケットを使用したエコー クライアントとサーバー
エコー サーバーの役割を果たす DatagramSocket を構築し、それを特定のポート番号にバインドし、受信 UDP メッセージをリッスンしてエコー バックします。 DatagramSocket.MessageReceived イベントは、ソケットでメッセージを受信したときに発生します。
エコー クライアントの役割を果たし、それを特定のポート番号にバインドし、UDP メッセージを送信して応答を受信する別の DatagramSocket を構築します。
という名前の新しいDatagramSocketPageを作成します。 XAML マークアップを DatagramSocketPage.xamlに配置し、命令型コードを DatagramSocketPage クラス内に配置します。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Margin="9.6,0" Style="{StaticResource TitleTextBlockStyle}" Text="UDP socket example"/>
<TextBlock Margin="7.2,0,0,0" Style="{StaticResource HeaderTextBlockStyle}" Text="DatagramSocket"/>
</StackPanel>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="9.6" Style="{StaticResource SubtitleTextBlockStyle}" Text="client"/>
<ListBox x:Name="clientListBox" Grid.Row="1" Margin="9.6"/>
<TextBlock Grid.Column="1" Margin="9.6" Style="{StaticResource SubtitleTextBlockStyle}" Text="server"/>
<ListBox x:Name="serverListBox" Grid.Column="1" Grid.Row="1" Margin="9.6"/>
</Grid>
</Grid>
// Every protocol typically has a standard port number. For example, HTTP is typically 80, FTP is 20 and 21, etc.
// For this example, we'll choose different arbitrary port numbers for client and server, since both will be running on the same machine.
static string ClientPortNumber = "1336";
static string ServerPortNumber = "1337";
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.StartServer();
this.StartClient();
}
private async void StartServer()
{
try
{
var serverDatagramSocket = new Windows.Networking.Sockets.DatagramSocket();
// The ConnectionReceived event is raised when connections are received.
serverDatagramSocket.MessageReceived += ServerDatagramSocket_MessageReceived;
this.serverListBox.Items.Add("server is about to bind...");
// Start listening for incoming UDP datagrams on the specified port. You can specify any port that's not currently in use.
await serverDatagramSocket.BindServiceNameAsync(DatagramSocketPage.ServerPortNumber);
this.serverListBox.Items.Add(string.Format("server is bound to port number {0}", DatagramSocketPage.ServerPortNumber));
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.serverListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void ServerDatagramSocket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
{
string request;
using (DataReader dataReader = args.GetDataReader())
{
request = dataReader.ReadString(dataReader.UnconsumedBufferLength).Trim();
}
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add(string.Format("server received the request: \"{0}\"", request)));
// Echo the request back as the response.
using (Stream outputStream = (await sender.GetOutputStreamAsync(args.RemoteAddress, DatagramSocketPage.ClientPortNumber)).AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add(string.Format("server sent back the response: \"{0}\"", request)));
sender.Dispose();
DispatcherQueue.TryEnqueue(() => this.serverListBox.Items.Add("server closed its socket"));
}
private async void StartClient()
{
try
{
// Create the DatagramSocket and establish a connection to the echo server.
var clientDatagramSocket = new Windows.Networking.Sockets.DatagramSocket();
clientDatagramSocket.MessageReceived += ClientDatagramSocket_MessageReceived;
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("localhost");
this.clientListBox.Items.Add("client is about to bind...");
await clientDatagramSocket.BindServiceNameAsync(DatagramSocketPage.ClientPortNumber);
this.clientListBox.Items.Add(string.Format("client is bound to port number {0}", DatagramSocketPage.ClientPortNumber));
// Send a request to the echo server.
string request = "Hello, World!";
using (var serverDatagramSocket = new Windows.Networking.Sockets.DatagramSocket())
{
using (Stream outputStream = (await serverDatagramSocket.GetOutputStreamAsync(hostName, DatagramSocketPage.ServerPortNumber)).AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
}
this.clientListBox.Items.Add(string.Format("client sent the request: \"{0}\"", request));
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.clientListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void ClientDatagramSocket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
{
string response;
using (DataReader dataReader = args.GetDataReader())
{
response = dataReader.ReadString(dataReader.UnconsumedBufferLength).Trim();
}
DispatcherQueue.TryEnqueue(() => this.clientListBox.Items.Add(string.Format("client received the response: \"{0}\"", response)));
sender.Dispose();
DispatcherQueue.TryEnqueue(() => this.clientListBox.Items.Add("client closed its socket"));
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Microsoft::UI::Dispatching;
using namespace Microsoft::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::DatagramSocket m_clientDatagramSocket;
Windows::Networking::Sockets::DatagramSocket m_serverDatagramSocket;
public:
void OnNavigatedTo(NavigationEventArgs const& /* e */)
{
StartServer();
StartClient();
}
private:
IAsyncAction StartServer()
{
try
{
// The ConnectionReceived event is raised when connections are received.
m_serverDatagramSocket.MessageReceived({ this, &DatagramSocketPage::ServerDatagramSocket_MessageReceived });
serverListBox().Items().Append(winrt::box_value(L"server is about to bind..."));
// Start listening for incoming UDP datagrams on the specified port. You can specify any port that's not currently in use.
co_await m_serverDatagramSocket.BindServiceNameAsync(L"1337");
serverListBox().Items().Append(winrt::box_value(L"server is bound to port number 1337"));
}
catch (winrt::hresult_error const& ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(ex.to_abi()) };
serverListBox().Items().Append(webErrorStatus != Windows::Networking::Sockets::SocketErrorStatus::Unknown ? winrt::box_value(winrt::to_hstring((int32_t)webErrorStatus)) : winrt::box_value(winrt::to_hstring(ex.to_abi())));
}
}
IAsyncAction ServerDatagramSocket_MessageReceived(Windows::Networking::Sockets::DatagramSocket sender, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs args)
{
DataReader dataReader{ args.GetDataReader() };
winrt::hstring request{ dataReader.ReadString(dataReader.UnconsumedBufferLength()) };
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
std::wstringstream wstringstream;
wstringstream << L"server received the request: \"" << request.c_str() << L"\"";
serverListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
});
// Echo the request back as the response.
IOutputStream outputStream = co_await sender.GetOutputStreamAsync(args.RemoteAddress(), L"1336");
DataWriter dataWriter{ outputStream };
dataWriter.WriteString(request);
co_await dataWriter.StoreAsync();
dataWriter.DetachStream();
serverListBox().DispatcherQueue().TryEnqueue([=]()
{
std::wstringstream wstringstream;
wstringstream << L"server sent back the response: \"" << request.c_str() << L"\"";
serverListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
m_serverDatagramSocket = nullptr;
serverListBox().Items().Append(winrt::box_value(L"server closed its socket"));
});
}
IAsyncAction StartClient()
{
try
{
m_clientDatagramSocket.MessageReceived({ this, &DatagramSocketPage::ClientDatagramSocket_MessageReceived });
// Establish a connection to the echo server.
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
Windows::Networking::HostName hostName{ L"localhost" };
clientListBox().Items().Append(winrt::box_value(L"client is about to bind..."));
co_await m_clientDatagramSocket.BindServiceNameAsync(L"1336");
clientListBox().Items().Append(winrt::box_value(L"client is bound to port number 1336"));
// Send a request to the echo server.
IOutputStream outputStream = co_await m_clientDatagramSocket.GetOutputStreamAsync(hostName, L"1337");
winrt::hstring request{ L"Hello, World!" };
DataWriter dataWriter{ outputStream };
dataWriter.WriteString(request);
co_await dataWriter.StoreAsync();
dataWriter.DetachStream();
std::wstringstream wstringstream;
wstringstream << L"client sent the request: \"" << request.c_str() << L"\"";
clientListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
}
catch (winrt::hresult_error const& ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(ex.to_abi()) };
serverListBox().Items().Append(webErrorStatus != Windows::Networking::Sockets::SocketErrorStatus::Unknown ? winrt::box_value(winrt::to_hstring((int32_t)webErrorStatus)) : winrt::box_value(winrt::to_hstring(ex.to_abi())));
}
}
void ClientDatagramSocket_MessageReceived(Windows::Networking::Sockets::DatagramSocket const& /* sender */, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs const& args)
{
DataReader dataReader{ args.GetDataReader() };
winrt::hstring response{ dataReader.ReadString(dataReader.UnconsumedBufferLength()) };
clientListBox().DispatcherQueue().TryEnqueue([=]()
{
std::wstringstream wstringstream;
wstringstream << L"client received the response: \"" << response.c_str() << L"\"";
clientListBox().Items().Append(winrt::box_value(wstringstream.str().c_str()));
});
m_clientDatagramSocket = nullptr;
clientListBox().DispatcherQueue().TryEnqueue([=]()
{
clientListBox().Items().Append(winrt::box_value(L"client closed its socket"));
});
}
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::DatagramSocket^ clientDatagramSocket;
Windows::Networking::Sockets::DatagramSocket^ serverDatagramSocket;
protected:
virtual void OnNavigatedTo(NavigationEventArgs^ e) override
{
this->StartServer();
this->StartClient();
}
private:
void StartServer()
{
try
{
this->serverDatagramSocket = ref new Windows::Networking::Sockets::DatagramSocket();
// The ConnectionReceived event is raised when connections are received.
this->serverDatagramSocket->MessageReceived += ref new TypedEventHandler<Windows::Networking::Sockets::DatagramSocket^, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs^>(this, &DatagramSocketPage::ServerDatagramSocket_MessageReceived);
this->serverListBox->Items->Append(L"server is about to bind...");
// Start listening for incoming UDP datagrams on the specified port. You can specify any port that's not currently in use.
Concurrency::create_task(this->serverDatagramSocket->BindServiceNameAsync("1337")).then(
[=]
{
this->serverListBox->Items->Append(L"server is bound to port number 1337");
});
}
catch (Platform::Exception^ ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus = Windows::Networking::Sockets::SocketError::GetStatus(ex->HResult);
this->serverListBox->Items->Append(webErrorStatus.ToString() != L"Unknown" ? webErrorStatus.ToString() : ex->Message);
}
}
void ServerDatagramSocket_MessageReceived(Windows::Networking::Sockets::DatagramSocket^ sender, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs^ args)
{
DataReader^ dataReader = args->GetDataReader();
Platform::String^ request = dataReader->ReadString(dataReader->UnconsumedBufferLength);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]
{
std::wstringstream wstringstream;
wstringstream << L"server received the request: \"" << request->Data() << L"\"";
this->serverListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
}));
// Echo the request back as the response.
Concurrency::create_task(sender->GetOutputStreamAsync(args->RemoteAddress, "1336")).then(
[=](IOutputStream^ outputStream)
{
auto dataWriter = ref new DataWriter(outputStream);
dataWriter->WriteString(request);
Concurrency::create_task(dataWriter->StoreAsync()).then(
[=](unsigned int)
{
dataWriter->DetachStream();
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]()
{
std::wstringstream wstringstream;
wstringstream << L"server sent back the response: \"" << request->Data() << L"\"";
this->serverListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
delete this->serverDatagramSocket;
this->serverDatagramSocket = nullptr;
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]() {this->serverListBox->Items->Append(L"server closed its socket"); }));
}));
});
});
}
void StartClient()
{
try
{
// Create the DatagramSocket and establish a connection to the echo server.
this->clientDatagramSocket = ref new Windows::Networking::Sockets::DatagramSocket();
this->clientDatagramSocket->MessageReceived += ref new TypedEventHandler<Windows::Networking::Sockets::DatagramSocket^, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs^>(this, &DatagramSocketPage::ClientDatagramSocket_MessageReceived);
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
auto hostName = ref new Windows::Networking::HostName(L"localhost");
this->clientListBox->Items->Append(L"client is about to bind...");
Concurrency::create_task(this->clientDatagramSocket->BindServiceNameAsync("1336")).then(
[=]
{
this->clientListBox->Items->Append(L"client is bound to port number 1336");
});
// Send a request to the echo server.
auto serverDatagramSocket = ref new Windows::Networking::Sockets::DatagramSocket();
Concurrency::create_task(serverDatagramSocket->GetOutputStreamAsync(hostName, "1337")).then(
[=](IOutputStream^ outputStream)
{
auto request = ref new Platform::String(L"Hello, World!");
auto dataWriter = ref new DataWriter(outputStream);
dataWriter->WriteString(request);
Concurrency::create_task(dataWriter->StoreAsync()).then(
[=](unsigned int)
{
dataWriter->DetachStream();
std::wstringstream wstringstream;
wstringstream << L"client sent the request: \"" << request->Data() << L"\"";
this->clientListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
});
});
}
catch (Platform::Exception^ ex)
{
Windows::Networking::Sockets::SocketErrorStatus webErrorStatus = Windows::Networking::Sockets::SocketError::GetStatus(ex->HResult);
this->serverListBox->Items->Append(webErrorStatus.ToString() != L"Unknown" ? webErrorStatus.ToString() : ex->Message);
}
}
void ClientDatagramSocket_MessageReceived(Windows::Networking::Sockets::DatagramSocket^ sender, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs^ args)
{
DataReader^ dataReader = args->GetDataReader();
Platform::String^ response = dataReader->ReadString(dataReader->UnconsumedBufferLength);
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(
[=]
{
std::wstringstream wstringstream;
wstringstream << L"client received the response: \"" << response->Data() << L"\"";
this->clientListBox->Items->Append(ref new Platform::String(wstringstream.str().c_str()));
}));
delete this->clientDatagramSocket;
this->clientDatagramSocket = nullptr;
this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]() {this->clientListBox->Items->Append(L"client closed its socket"); }));
}
バックグラウンド処理とソケットブローカー
ソケット ブローカーと制御チャネル トリガーを使用して、アプリがフォアグラウンドではない間にソケット上の接続またはデータを適切に受信するようにすることができます。 詳しくは、 バックグラウンドでのネットワーク通信に関するページをご覧ください。
バッチ送信
ソケットに関連付けられているストリームに書き込むたびに、ユーザー モード (コード) からカーネル モード (ネットワーク スタックがある場合) に遷移します。 一度に多数のバッファーに書き込む場合、こうした遷移が繰り返されることで、かなりのオーバーヘッドになります。 送信のバッチ処理は、複数のデータ バッファーを一緒に送信し、そのオーバーヘッドを回避する方法です。 アプリが VoIP、VPN、または可能な限り効率的に大量のデータを移動するその他のタスクを実行している場合に特に便利です。
このセクションでは、 StreamSocket または接続された DatagramSocket で使用できるバッチ送信手法の 2 つについて説明します。
ベースラインを取得するには、大量のバッファーを非効率的な方法で送信する方法を見てみましょう。 StreamSocket を使用した最小限のデモを次に示します。
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
await streamSocketListener.BindServiceNameAsync("1337");
var streamSocket = new Windows.Networking.Sockets.StreamSocket();
await streamSocket.ConnectAsync(new Windows.Networking.HostName("localhost"), "1337");
this.SendMultipleBuffersInefficiently(streamSocket, "Hello, World!");
//this.BatchedSendsCSharpOnly(streamSocket, "Hello, World!");
//this.BatchedSendsAnyUWPLanguage(streamSocket, "Hello, World!");
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
using (var dataReader = new DataReader(args.Socket.InputStream))
{
dataReader.InputStreamOptions = InputStreamOptions.Partial;
while (true)
{
await dataReader.LoadAsync(256);
if (dataReader.UnconsumedBufferLength == 0) break;
IBuffer requestBuffer = dataReader.ReadBuffer(dataReader.UnconsumedBufferLength);
string request = Windows.Security.Cryptography.CryptographicBuffer.ConvertBinaryToString(Windows.Security.Cryptography.BinaryStringEncoding.Utf8, requestBuffer);
Debug.WriteLine(string.Format("server received the request: \"{0}\"", request));
}
}
}
// This implementation incurs kernel transition overhead for each packet written.
private async void SendMultipleBuffersInefficiently(Windows.Networking.Sockets.StreamSocket streamSocket, string message)
{
var packetsToSend = new List<IBuffer>();
for (int count = 0; count < 5; ++count) { packetsToSend.Add(Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(message, Windows.Security.Cryptography.BinaryStringEncoding.Utf8)); }
foreach (IBuffer packet in packetsToSend)
{
await streamSocket.OutputStream.WriteAsync(packet);
}
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Security.Cryptography.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <sstream>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Microsoft::UI::Dispatching;
using namespace Microsoft::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::StreamSocketListener m_streamSocketListener;
Windows::Networking::Sockets::StreamSocket m_streamSocket;
public:
IAsyncAction OnNavigatedTo(NavigationEventArgs /* e */)
{
m_streamSocketListener.ConnectionReceived({ this, &BatchedSendsPage::OnConnectionReceived });
co_await m_streamSocketListener.BindServiceNameAsync(L"1337");
co_await m_streamSocket.ConnectAsync(Windows::Networking::HostName{ L"localhost" }, L"1337");
SendMultipleBuffersInefficientlyAsync(L"Hello, World!");
//BatchedSendsAnyUWPLanguageAsync(L"Hello, World!");
}
private:
IAsyncAction OnConnectionReceived(Windows::Networking::Sockets::StreamSocketListener const& /* sender */, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs const& args)
{
DataReader dataReader{ args.Socket().InputStream() };
dataReader.InputStreamOptions(Windows::Storage::Streams::InputStreamOptions::Partial);
while (true)
{
unsigned int bytesLoaded = co_await dataReader.LoadAsync(256);
if (bytesLoaded == 0) break;
winrt::hstring message{ dataReader.ReadString(bytesLoaded) };
::OutputDebugString(message.c_str());
}
}
// This implementation incurs kernel transition overhead for each packet written.
IAsyncAction SendMultipleBuffersInefficientlyAsync(winrt::hstring message)
{
co_await winrt::resume_background();
std::vector< IBuffer > packetsToSend;
for (unsigned int count = 0; count < 5; ++count)
{
packetsToSend.push_back(Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(message, Windows::Security::Cryptography::BinaryStringEncoding::Utf8));
}
for (auto const& element : packetsToSend)
{
m_streamSocket.OutputStream().WriteAsync(element).get();
}
}
#include <ppltasks.h>
#include <sstream>
...
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Navigation;
...
private:
Windows::Networking::Sockets::StreamSocketListener^ streamSocketListener;
Windows::Networking::Sockets::StreamSocket^ streamSocket;
protected:
virtual void OnNavigatedTo(NavigationEventArgs^ e) override
{
this->streamSocketListener = ref new Windows::Networking::Sockets::StreamSocketListener();
streamSocketListener->ConnectionReceived += ref new TypedEventHandler<Windows::Networking::Sockets::StreamSocketListener^, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^>(this, &BatchedSendsPage::StreamSocketListener_ConnectionReceived);
Concurrency::create_task(this->streamSocketListener->BindServiceNameAsync(L"1337")).then(
[=]
{
this->streamSocket = ref new Windows::Networking::Sockets::StreamSocket();
Concurrency::create_task(this->streamSocket->ConnectAsync(ref new Windows::Networking::HostName(L"localhost"), L"1337")).then(
[=](Concurrency::task< void >)
{
this->SendMultipleBuffersInefficiently(L"Hello, World!");
// this->BatchedSendsAnyUWPLanguage(L"Hello, World!");
}, Concurrency::task_continuation_context::use_synchronous_execution());
});
}
private:
void StreamSocketListener_ConnectionReceived(Windows::Networking::Sockets::StreamSocketListener^ sender, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ args)
{
auto dataReader = ref new DataReader(args->Socket->InputStream);
dataReader->InputStreamOptions = Windows::Storage::Streams::InputStreamOptions::Partial;
this->ReceiveStringRecurse(dataReader, args->Socket);
}
void ReceiveStringRecurse(DataReader^ dataReader, Windows::Networking::Sockets::StreamSocket^ streamSocket)
{
Concurrency::create_task(dataReader->LoadAsync(256)).then(
[this, dataReader, streamSocket](unsigned int bytesLoaded)
{
if (bytesLoaded == 0) return;
Platform::String^ message = dataReader->ReadString(bytesLoaded);
::OutputDebugString(message->Data());
this->ReceiveStringRecurse(dataReader, streamSocket);
});
}
// This implementation incurs kernel transition overhead for each packet written.
void SendMultipleBuffersInefficiently(Platform::String^ message)
{
std::vector< IBuffer^ > packetsToSend{};
for (unsigned int count = 0; count < 5; ++count)
{
packetsToSend.push_back(Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(message, Windows::Security::Cryptography::BinaryStringEncoding::Utf8));
}
for (auto element : packetsToSend)
{
Concurrency::create_task(this->streamSocket->OutputStream->WriteAsync(element)).wait();
}
}
このより効率的な手法の最初の例は、C# を使用している場合にのみ適しています。
OnNavigatedToやBatchedSendsCSharpOnlyではなく、SendMultipleBuffersInefficientlyを呼び出すようにSendMultipleBuffersInefficientlyAsyncを変更します。
// A C#-only technique for batched sends.
private async void BatchedSendsCSharpOnly(Windows.Networking.Sockets.StreamSocket streamSocket, string message)
{
var packetsToSend = new List<IBuffer>();
for (int count = 0; count < 5; ++count) { packetsToSend.Add(Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(message, Windows.Security.Cryptography.BinaryStringEncoding.Utf8)); }
var pendingTasks = new System.Threading.Tasks.Task[packetsToSend.Count];
for (int index = 0; index < packetsToSend.Count; ++index)
{
// track all pending writes as tasks, but don't wait on one before beginning the next.
pendingTasks[index] = streamSocket.OutputStream.WriteAsync(packetsToSend[index]).AsTask();
// Don't modify any buffer's contents until the pending writes are complete.
}
// Wait for all of the pending writes to complete.
System.Threading.Tasks.Task.WaitAll(pendingTasks);
}
次の例は、C# だけでなく、任意の UWP 言語に適しています。 これは、バッチが一緒に送信する StreamSocket.OutputStream と DatagramSocket.OutputStream の 動作に依存します。 この手法は、その出力ストリームに対して FlushAsync を呼び出します。これは、Windows 10時点で、出力ストリームに対するすべての操作が完了した後にのみ返されることを保証します。
// An implementation of batched sends suitable for any UWP language.
private async void BatchedSendsAnyUWPLanguage(Windows.Networking.Sockets.StreamSocket streamSocket, string message)
{
var packetsToSend = new List<IBuffer>();
for (int count = 0; count < 5; ++count) { packetsToSend.Add(Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(message, Windows.Security.Cryptography.BinaryStringEncoding.Utf8)); }
var pendingWrites = new IAsyncOperationWithProgress<uint, uint>[packetsToSend.Count];
for (int index = 0; index < packetsToSend.Count; ++index)
{
// track all pending writes as tasks, but don't wait on one before beginning the next.
pendingWrites[index] = streamSocket.OutputStream.WriteAsync(packetsToSend[index]);
// Don't modify any buffer's contents until the pending writes are complete.
}
// Wait for all of the pending writes to complete. This step enables batched sends on the output stream.
await streamSocket.OutputStream.FlushAsync();
}
// An implementation of batched sends suitable for any UWP language.
IAsyncAction BatchedSendsAnyUWPLanguageAsync(winrt::hstring message)
{
std::vector< IBuffer > packetsToSend{};
std::vector< IAsyncOperationWithProgress< unsigned int, unsigned int > > pendingWrites{};
for (unsigned int count = 0; count < 5; ++count)
{
packetsToSend.push_back(Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(message, Windows::Security::Cryptography::BinaryStringEncoding::Utf8));
}
for (auto const& element : packetsToSend)
{
// track all pending writes as tasks, but don't wait on one before beginning the next.
pendingWrites.push_back(m_streamSocket.OutputStream().WriteAsync(element));
// Don't modify any buffer's contents until the pending writes are complete.
}
// Wait for all of the pending writes to complete. This step enables batched sends on the output stream.
co_await m_streamSocket.OutputStream().FlushAsync();
}
private:
// An implementation of batched sends suitable for any UWP language.
void BatchedSendsAnyUWPLanguage(Platform::String^ message)
{
std::vector< IBuffer^ > packetsToSend{};
std::vector< IAsyncOperationWithProgress< unsigned int, unsigned int >^ >pendingWrites{};
for (unsigned int count = 0; count < 5; ++count)
{
packetsToSend.push_back(Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(message, Windows::Security::Cryptography::BinaryStringEncoding::Utf8));
}
for (auto element : packetsToSend)
{
// track all pending writes as tasks, but don't wait on one before beginning the next.
pendingWrites.push_back(this->streamSocket->OutputStream->WriteAsync(element));
// Don't modify any buffer's contents until the pending writes are complete.
}
// Wait for all of the pending writes to complete. This step enables batched sends on the output stream.
Concurrency::create_task(this->streamSocket->OutputStream->FlushAsync());
}
コードでバッチ送信を使用すると、いくつかの重要な制限事項が課されます。
- 非同期書き込みが完了するまで、書き込まれる IBuffer インスタンスの内容を変更することはできません。
- FlushAsync パターンは、StreamSocket.OutputStream と DatagramSocket.OutputStream でのみ機能します。
- FlushAsync パターンは、Windows 10 以降でのみ機能します。
- それ以外の場合は、FlushAsync パターンの代わりに Task.WaitAll を使用します。
DatagramSocket のポート共有
同じアドレス/ポートにバインドされた他の Win32 または UWP マルチキャスト ソケットと共存するように DatagramSocket を構成できます。 これを行うには、ソケットをバインドまたは接続する前 に、DatagramSocketControl.MulticastOnly を true に設定します。
DatagramSocketControl のインスタンスには、DatagramSocket.Control プロパティを使用して DatagramSocket オブジェクト自体からアクセスします。
StreamSocket クラスを使用してクライアント証明書を提供する
StreamSocket では、SSL/TLS を使用して、クライアント アプリが通信しているサーバーを認証できます。 場合によっては、クライアント アプリが SSL/TLS クライアント証明書を使用してサーバーに対して自身を認証する必要があります。 ソケットをバインドまたは接続する前に、 StreamSocketControl.ClientCertificate プロパティを使用してクライアント証明書を指定できます (SSL/TLS ハンドシェイクが開始される前に設定する必要があります)。 StreamSocket.Control プロパティを使用して、StreamSocket オブジェクト自体から StreamSocketControl のインスタンスにアクセスします。 サーバーがクライアント証明書を要求した場合、Windowsは指定したクライアント証明書で応答します。
この最小コード例に示すように、SocketProtectionLevel を受け取る StreamSocket.ConnectAsync のオーバーライドを使用します。
Important
以下のコード例のコメントで示されているように、プロジェクトでは、このコードを機能させるために sharedUserCertificates アプリ機能を宣言する必要があります。
// For this code to work, you need at least one certificate to be present in the user MY certificate store.
// Plugging a smartcard into a smartcard reader connected to your PC will achieve that.
// Also, your project needs to declare the sharedUserCertificates app capability.
var certificateQuery = new Windows.Security.Cryptography.Certificates.CertificateQuery();
certificateQuery.StoreName = "MY";
IReadOnlyList<Windows.Security.Cryptography.Certificates.Certificate> certificates = await Windows.Security.Cryptography.Certificates.CertificateStores.FindAllAsync(certificateQuery);
if (certificates.Count > 0)
{
streamSocket.Control.ClientCertificate = certificates[0];
await streamSocket.ConnectAsync(hostName, "1337", Windows.Networking.Sockets.SocketProtectionLevel.Tls12);
}
// For this code to work, you need at least one certificate to be present in the user MY certificate store.
// Plugging a smartcard into a smartcard reader connected to your PC will achieve that.
// Also, your project needs to declare the sharedUserCertificates app capability.
Windows::Security::Cryptography::Certificates::CertificateQuery certificateQuery;
certificateQuery.StoreName(L"MY");
IVectorView< Windows::Security::Cryptography::Certificates::Certificate > certificates = co_await Windows::Security::Cryptography::Certificates::CertificateStores::FindAllAsync(certificateQuery);
if (certificates.Size() > 0)
{
m_streamSocket.Control().ClientCertificate(certificates.GetAt(0));
co_await m_streamSocket.ConnectAsync(Windows::Networking::HostName{ L"localhost" }, L"1337", Windows::Networking::Sockets::SocketProtectionLevel::Tls12);
...
}
// For this code to work, you need at least one certificate to be present in the user MY certificate store.
// Plugging a smartcard into a smartcard reader connected to your PC will achieve that.
// Also, your project needs to declare the sharedUserCertificates app capability.
auto certificateQuery = ref new Windows::Security::Cryptography::Certificates::CertificateQuery();
certificateQuery->StoreName = L"MY";
Concurrency::create_task(Windows::Security::Cryptography::Certificates::CertificateStores::FindAllAsync(certificateQuery)).then(
[=](IVectorView< Windows::Security::Cryptography::Certificates::Certificate^ >^ certificates)
{
if (certificates->Size > 0)
{
this->streamSocket->Control->ClientCertificate = certificates->GetAt(0);
Concurrency::create_task(this->streamSocket->ConnectAsync(ref new Windows::Networking::HostName(L"localhost"), L"1337", Windows::Networking::Sockets::SocketProtectionLevel::Tls12)).then(
[=]
{
...
});
}
});
例外の処理
DatagramSocket、StreamSocket、または StreamSocketListener 操作で発生したエラーが HRESULT 値として返されます。 その HRESULT 値を SocketError.GetStatus メソッドに渡して、 SocketErrorStatus 列挙値に変換できます。
ほとんどの SocketErrorStatus 列挙値は、ネイティブ Windows ソケット操作によって返されるエラーに対応します。 アプリは SocketErrorStatus 列挙値をオンに切り替えて、例外の原因に応じてアプリの動作を変更できます。
パラメーター検証エラーの場合は、例外の HRESULT を使用して、エラーに関する詳細情報を確認できます。 使用可能な HRESULT 値は Winerror.h に一覧表示されます。これは SDK のインストール (フォルダー C:\Program Files (x86)\Windows Kits\10\Include\<VERSION>\sharedなど) にあります。 ほとんどのパラメーター検証エラーでは、返される HRESULT は E_INVALIDARG。
渡された文字列が有効なホスト名でない場合、 HostName コンストラクターは例外をスローできます。 たとえば、許可されていない文字が含まれています。これは、ホスト名がユーザーによってアプリに入力された場合に発生する可能性があります。 try/catch ブロック内に HostName を 構築します。 こうすることで、例外が発生した場合、アプリはユーザーに通知し、新しいホスト名の入力を求めることができます。
重要な API
- CertificateQuery
- CertificateStores.FindAllAsync
- DatagramSocket
- DatagramSocket.BindServiceNameAsync
- DatagramSocket.Control
- DatagramSocket.GetOutputStreamAsync
- DatagramSocket.MessageReceived
- DatagramSocketControl.MulticastOnly
- DatagramSocketMessageReceivedEventArgs
- DatagramSocketMessageReceivedEventArgs.GetDataReader
- DataReader.LoadAsync
- IOutputStream.FlushAsync
- SocketError.GetStatus
- SocketErrorStatus
- SocketProtectionLevel
- StreamSocket
- StreamSocketControl.ClientCertificate
- StreamSocket.ConnectAsync
- StreamSocket.InputStream
- StreamSocket.OutputStream
- StreamSocketListener
- StreamSocketListener.BindServiceNameAsync
- StreamSocketListener.ConnectionReceived
- StreamSocketListenerConnectionReceivedEventArgs
- Windows.Networking.Sockets
関連トピック
Samples
Windows developer