Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A socketek alacsony szintű adatátviteli technológiák, amelyekre számos hálózati protokoll épül. Windows TCP- és UDP-szoftvercsatornás osztályokat kínál az ügyfélkiszolgálói vagy társközi alkalmazásokhoz, függetlenül attól, hogy a kapcsolatok hosszú élettartamúak-e, vagy nincs szükség meglévő kapcsolatra.
Ez a témakör arra összpontosít, hogyan használhatók a Windows.Networking.Sockets névtérben található Windows socketosztályok. De Windows Sockets 2 (Winsock) is használható egy Windows alkalmazásban.
Note
a hálózatelkülönítés következtében Windows tiltja a szoftvercsatorna-kapcsolat (Sockets vagy WinSock) létrehozását az ugyanazon a gépen futó két Windows alkalmazás között, akár a helyi visszacsatolási címen (127.0.0.1) keresztül, akár a helyi IP-cím explicit megadásával. Azokról a mechanizmusokról, amelyekkel Windows alkalmazások kommunikálhatnak egymással, tekintse meg az alkalmazások közötti kommunikációt.
Alapszintű TCP-szoftvercsatorna-ügyfél és -kiszolgáló létrehozása
A TCP (Transmission Control Protocol) szoftvercsatornák alacsony szintű hálózati adatátvitelt biztosítanak mindkét irányban a hosszú élettartamú kapcsolatokhoz. A TCP-socketek jelentik az Interneten használt hálózati protokollok többsége által használt alapul szolgáló funkciót. Az alapszintű TCP-műveletek bemutatásához az alábbi példakód egy StreamSocketet és egy StreamSocketListenert jelenít meg, amely tcp-en keresztül küld és fogad adatokat visszhangügyfél és -kiszolgáló létrehozásához.
Ahhoz, hogy a lehető legkevesebb mozgó részből lássunk el– és elhárítsuk a hálózatelkülönítési problémákat – hozzon létre egy új projektet, és az ügyfél és a kiszolgáló alábbi kódját is ugyanabba a projektbe helyezze.
Deklarálnia kell egy alkalmazásképességet a projektben. Nyissa meg az alkalmazáscsomag jegyzékfájljának forrásfájlját (a Package.appxmanifest fájlt), és a Képességek lapon ellenőrizze a Privát hálózatok (Ügyfél és kiszolgáló) lehetőséget. Így néz ki ez a Package.appxmanifest jelölőkódban.
<Capability Name="privateNetworkClientServer" />
privateNetworkClientServer helyett megadhatja a(z) internetClientServer értéket, ha az interneten keresztül csatlakozik. A StreamSocketnek és a StreamSocketListenernek is szüksége van egy vagy több ilyen alkalmazás-képesség deklarálására.
TCP foglalatokat használó echo ügyfél és kiszolgáló
Hozzon létre egy StreamSocketListenert , és kezdje el figyelni a bejövő TCP-kapcsolatokat. A StreamSocketListener.ConnectionReceived esemény minden alkalommal létre lesz hozva, amikor egy ügyfél kapcsolatot létesít a StreamSocketListenerrel.
Hozzon létre egy StreamSocketet is, hozzon létre kapcsolatot a kiszolgálóval, küldjön egy kérést, és kapjon választ.
Hozzon létre egy új lapot.StreamSocketAndListenerPage Helyezze az XAML-jelölőnyelvi kódot a StreamSocketAndListenerPage.xaml elembe, az imperatív kódot pedig a StreamSocketAndListenerPage osztályba.
<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);
}
}
A C++ PPL-folytatásokban lévő StreamSocketekre mutató hivatkozások (elsősorban a C++/CX-ekre vonatkozik)
Note
Ha C++/WinRT coroutinest használ, és érték szerint ad át paramétereket, akkor ez a probléma nem érvényes. A paraméterátadási javaslatokért lásd: Egyidejűség és aszinkron műveletek a C++/WinRT használatával.
A StreamSocket mindaddig életben marad, amíg aktív olvasási/írási lehetőség van a bemeneti/kimeneti adatfolyamán (vegyük például azt a StreamSocketListenerConnectionReceivedEventArgs.Socket-t , amelyhez hozzáférése van a StreamSocketListener.ConnectionReceived eseménykezelőben). Amikor meghívja a DataReader.LoadAsync metódust (vagy a ReadAsync/WriteAsync/StoreAsync metódust), az hivatkozást tart fenn a socketre (a socket bemeneti adatfolyamán keresztül) mindaddig, amíg a LoadAsyncCompleted eseményének eseménykezelője (ha van ilyen) le nem fut.
A Parallel Patterns Library (PPL) alapértelmezés szerint nem ütemezi a feladatfolytatásokat közvetlenül. Más szóval, a folytatási tevékenység hozzáadása (task::then() használatával) nem garantálja, hogy a folytatási tevékenység közvetlenül, a befejezési kezelőként fog végrehajtódni.
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.
});
}
A StreamSocket nézőpontjából a befejezési kezelőfüggvény befejezi a futását, mielőtt a folytatás törzse lefutna (és a foglalat ekkor már felszabadítható). Tehát ahhoz, hogy a socket ne szabaduljon fel, ha azon a folytatáson belül szeretné használni, vagy közvetlenül kell hivatkoznia rá (lambdaelfogással), és használnia kell, vagy közvetetten (úgy, hogy a folytatásokon belül továbbra is a args->Socket elemhez fér hozzá), vagy ki kell kényszerítenie, hogy a folytatási feladatok beágyazva fussanak le. A StreamSocket-mintában az első technika (lambda rögzítés) látható működés közben. A fenti Alapszintű TCP-szoftvercsatorna-ügyfél és -kiszolgáló létrehozása szakaszban található C++/CX kód a második technikát használja – válaszként visszhangozza a kérést, és az egyik legbelső args->Socket folytatásból fér hozzá.
A harmadik módszer akkor helyénvaló, ha nem küld vissza választ. A task_continuation_context::use_synchronous_execution() lehetőséggel kényszerítheti a PPL-t a folytatás törzsének helyben történő végrehajtására. Íme egy példa kódra, amely bemutatja, hogyan teheti meg.
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());
}
Ez a viselkedés a Windows.Networking.Sockets névtér összes Sockets- és WebSockets-osztályára vonatkozik. Az ügyféloldali esetekben azonban a socketeket általában tagváltozókban tárolják, ezért a probléma leginkább a StreamSocketListener.ConnectionReceived esetre vonatkozik, a fentiekben bemutatottak szerint.
Alapszintű UDP-szoftvercsatorna-ügyfél és -kiszolgáló létrehozása
Az UDP (User Datagram Protocol) szoftvercsatornák hasonlóak a TCP-szoftvercsatornákhoz, mivel mindkét irányban alacsony szintű hálózati adatátvitelt is biztosítanak. Bár a TCP-szoftvercsatornák hosszú élettartamú kapcsolatokhoz használhatók, az UDP-szoftvercsatornák olyan alkalmazásokhoz használhatók, ahol nincs szükség létesített kapcsolatra. Mivel az UDP socketek nem tartanak fenn kapcsolatot a két végpont között, ezért gyors és egyszerű megoldást jelentenek a távoli gépek közötti hálózati kommunikációhoz. Az UDP-szoftvercsatornák azonban nem biztosítják a hálózati csomagok integritását, és azt sem, hogy a csomagok egyáltalán a távoli célhelyre viszik-e. Ezért az alkalmazást úgy kell megtervezni, hogy ezt elviselje. Az UDP-szoftvercsatornákat használó alkalmazások néhány példája a helyi hálózatfelderítés és a helyi csevegési ügyfelek.
Az alapszintű UDP-műveletek bemutatásához az alábbi példakód azt a DatagramSocket osztályt mutatja be, amellyel az UDP-en keresztül is lehet adatokat küldeni és fogadni egy echo-ügyfelet és -kiszolgálót. Hozzon létre egy új projektet, és helyezze ugyanabba a projektbe az ügyfelet és a kiszolgálókódot is. Csakúgy, mint a TCP-szoftvercsatornák esetében, deklarálnia kell a Private Networks (Client &Server) alkalmazás képességét.
Echo-ügyfél és -kiszolgáló UDP-szoftvercsatornák használatával
DatagramSocket létrehozása az echo-kiszolgáló szerepének lejátszásához, egy adott portszámhoz kötéséhez, egy bejövő UDP-üzenet figyeléséhez és visszahangzásához. A DatagramSocket.MessageReceived esemény akkor aktiválódik, amikor egy üzenet érkezik a foglalaton.
Hozzon létre egy másik DatagramSocketet , amely az echo-ügyfél szerepét tölti be, egy adott portszámhoz köti, UDP-üzenetet küld, és választ kap.
Hozzon létre egy új lapot.DatagramSocketPage Helyezze az XAML-jelölőnyelvi kódot a DatagramSocketPage.xaml elembe, az imperatív kódot pedig a DatagramSocketPage osztályba.
<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"); }));
}
Háttérműveletek és a socketközvetítő
A socketközvetítő és a vezérlőcsatorna-eseményindítók használatával biztosíthatja, hogy az alkalmazás megfelelően fogadja a socketeken érkező kapcsolatokat vagy adatokat, amikor nincs az előtérben. További információ: Hálózati kommunikáció a háttérben.
Kötegelt küldések
Valahányszor a sockethez tartozó adatfolyamba írsz, átmenet történik a felhasználói módból (ahol a kódod fut) a kernelmódba (ahol a hálózati verem található). Ha egyszerre sok puffert ír, ezek az ismétlődő áttűnések jelentős többletterhelést okoznak. A küldések kötegelésével több adatpuffert is küldhet együtt, és elkerülheti ezt a többletterhelést. Különösen hasznos, ha az alkalmazás VoIP-, VPN- vagy egyéb olyan feladatokat végez, amelyek sok adat lehető leghatékonyabb áthelyezését foglalják magukban.
Ez a szakasz bemutatja a StreamSockettel vagy egy csatlakoztatott DatagramSockettel használható kötegelt küldési technikákat.
Az alapkonfiguráció lekéréséhez lássuk, hogyan küldhetünk nagy számú puffert nem hatékony módon. Íme egy minimális bemutató, egy StreamSocket használatával.
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();
}
}
Ez a hatékonyabb technika első példája csak akkor megfelelő, ha C#-ot használ. Módosítsa a(z) OnNavigatedTo elemet úgy, hogy a(z) BatchedSendsCSharpOnly legyen meghívva a(z) SendMultipleBuffersInefficiently vagy a(z) SendMultipleBuffersInefficientlyAsync helyett.
// 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);
}
Ez a következő példa minden UWP-nyelvhez megfelelő, nem csak a C#-hoz. A StreamSocket.OutputStream és a DatagramSocket.OutputStream azon viselkedésére támaszkodik, hogy a küldéseket kötegelve kezeli. A technika meghívja a FlushAsync-et azon a kimeneti adatfolyamon, amely Windows 10 esetén garantáltan csak akkor tér vissza, ha a kimeneti adatfolyam összes művelete befejeződött.
// 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());
}
A kódban kötegelt küldések használatával bizonyos fontos korlátozások vonatkoznak.
- Az írás alatt álló IBuffer-példányok tartalmát csak akkor módosíthatja, ha az aszinkron írás befejeződött.
- A FlushAsync minta csak a StreamSocket.OutputStream és a DatagramSocket.OutputStream szolgáltatásban működik.
- A FlushAsync minta csak a Windows 10-es és újabb verziókon működik.
- Más esetekben a FlushAsync minta helyett használja a Task.WaitAll parancsot.
Portmegosztás a DatagramSockethez
A DatagramSocket konfigurálható úgy, hogy az azonos címhez/porthoz kötött más Win32- vagy UWP-csoportos küldési szoftvercsatornákkal együtt létezzon. Ezt úgy teheti meg, hogy a DatagramSocketControl.MulticastOnly értékét true értékre állítja, mielőtt a socketet kötné vagy csatlakoztatná. A DatagramSocketControl egy példányát maga a DatagramSocket objektumból érheti el a DatagramSocket.Control tulajdonságon keresztül.
Ügyféltanúsítvány biztosítása a StreamSocket-osztálysal
A StreamSocket támogatja az SSL/TLS használatát annak a kiszolgálónak a hitelesítéséhez, amellyel az ügyfélalkalmazás beszél. Bizonyos esetekben az ügyfélalkalmazásnak SSL-/TLS-ügyféltanúsítvány használatával kell hitelesítenie magát a kiszolgálón. A StreamSocketControl.ClientCertificate tulajdonsággal megadhatja az ügyféltanúsítványt a szoftvercsatorna kötése vagy csatlakoztatása előtt (az SSL/TLS kézfogás megkezdése előtt be kell állítani). A StreamSocketControl egy példányát maga a StreamSocket objektumból érheti el a StreamSocket.Control tulajdonságon keresztül. Ha a kiszolgáló az ügyféltanúsítványt kéri, Windows a megadott ügyféltanúsítvánnyal válaszol.
Használja a StreamSocket.ConnectAsync felülbírálását, amely egy SocketProtectionLevelet használ, ahogyan az ebben a minimális kód példában látható.
Important
Ahogy az alábbi kód példákban szereplő megjegyzés is jelzi, a projektnek deklarálnia kell a megosztottUserCertificates alkalmazás képességét a kód működéséhez.
// 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(
[=]
{
...
});
}
});
Kivételek kezelése
A DatagramSocket, a StreamSocket vagy a StreamSocketListener művelet során észlelt hiba HRESULT-értékként jelenik meg. Ezt a HRESULT értéket átadhatja a SocketError.GetStatus metódusnak, hogy socketErrorStatus enumerálási értékké konvertálja.
A SocketErrorStatus felsorolásértékeinek többsége a natív Windows-foglalatművelet által visszaadott hibának felel meg. Az alkalmazás bekapcsolhatja a SocketErrorStatus enumerálási értékeit az alkalmazás viselkedésének módosításához a kivétel okától függően.
Paraméterérvényesítési hibák esetén a kivételből származó HRESULT használatával részletesebb információkat tudhat meg a hibáról. A lehetséges HRESULT értékek az Winerror.hSDK-telepítésben (például a mappában C:\Program Files (x86)\Windows Kits\10\Include\<VERSION>\shared) találhatók. A legtöbb paraméterérvényesítési hiba esetén a visszaadott HRESULT értéke E_INVALIDARG.
A HostName konstruktor kivételt válthat ki, ha az átadott karakterlánc nem érvényes gazdagépnév. Például olyan karaktereket tartalmaz, amelyek nem engedélyezettek, ami valószínűleg akkor fordulhat elő, ha a felhasználó beírja az állomásnevet az alkalmazásba. Hozzon létre egy HostName objektumot egy try/catch blokkon belül. Így kivétel esetén az alkalmazás értesítheti a felhasználót, és új állomásnevet kérhet.
Fontos API-k
- 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
Kapcsolódó témakörök
- Alkalmazások közötti kommunikáció
- Egyidejűség és aszinkron műveletek a C++/WinRT használatával
- Hálózati képességek beállítása
- Windows Sockets 2 (Winsock)
Samples
Windows developer