介紹
重新想象 Windows 8 Store Apps 之 通信
Socket - Tcp Demo
Socket - 實現一個自定義的 http server
Socket - Udp Demo
示例
1、演示 socket tcp 的應用(本例既做服務端又做客戶端)
Communication/Socket/TcpDemo.xaml
<Page
x:Class="XamlDemo.Communication.Socket.TcpDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Communication.Socket"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<StackPanel Margin="120 0 0 0" Orientation="Horizontal">
<StackPanel>
<Button Name="btnStartListener" Content="start a socket listener" Click="btnStartListener_Click" />
<Button Name="btnConnectListener" Content="connect to the socket listener" Click="btnConnectListener_Click" Margin="0 10 0 0" />
<Button Name="btnSendData" Content="send data" Click="btnSendData_Click" Margin="0 10 0 0" />
<Button Name="btnCloseSocket" Content="close server socket and client socket" Click="btnCloseSocket_Click" Margin="0 10 0 0" />
</StackPanel>
<TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" Margin="20 0 0 0" />
</StackPanel>
</Grid>
</Page>
Communication/Socket/TcpDemo.xaml.cs
/*
* 演示 socket tcp 的應用(本例既做服務端又做客戶端)
*
* 通過 StreamSocketListener 實現 tcp 通信的服務端的 socket 監聽
* 通過 StreamSocket 實現 tcp 通信的客戶端 socket
*
* 注:需要在 Package.appxmanifest 中增加配置 <Capability
Name="privateNetworkClientServer" />
*/
using System;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlDemo.Communication.Socket
{
public sealed partial class TcpDemo : Page
{
/// <summary>
/// 服務端 socket
/// </summary>
private StreamSocketListener _listener;
/// <summary>
/// 客戶端 socket
/// </summary>
private StreamSocket _client;
/// <summary>
/// 客戶端向服務端發送數據時的 DataWriter
/// </summary>
private DataWriter _writer;
public TcpDemo()
{
this.InitializeComponent();
}
// 在服務端啟動一個 socket 監聽
private async void btnStartListener_Click(object sender, RoutedEventArgs e)
{
// 實例化一個 socket 監聽對象
_listener = new StreamSocketListener();
// 監聽在接收到一個連接後所觸發的事件
_listener.ConnectionReceived += _listener_ConnectionReceived;
try
{
// 在指定的端口上啟動 socket 監聽
await _listener.BindServiceNameAsync("2211");
lblMsg.Text += "已經在本機的 2211 端口啟動了 socket(tcp) 監聽";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// socket 監聽接收到一個連接後
async void _listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
// 實例化一個 DataReader,用於讀取數據
DataReader reader = new DataReader(args.Socket.InputStream);
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lblMsg.Text += "服務端收到了來自: " + args.Socket.Information.RemoteHostName.RawName + ":" + args.Socket.Information.RemotePort + " 的 socket 連接";
lblMsg.Text += Environment.NewLine;
});
try
{
while (true)
{
// 自定義協議(header|body):前4個字節代表實際數據的長度,之後的實際數據為一個字符串數據
// 讀取 header
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// 在獲取到合法數據之前,socket 關閉了
return;
}
// 讀取 body
uint stringLength = reader.ReadUInt32();
uint actualStringLength = await reader.LoadAsync(stringLength);
if (stringLength != actualStringLength)
{
// 在獲取到合法數據之前,socket 關閉了
return;
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// 顯示客戶端發送過來的數據
lblMsg.Text += "接收到數據: " + reader.ReadString(actualStringLength);
lblMsg.Text += Environment.NewLine;
});
}
}
catch (Exception ex)
{
var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
});
}
}
// 創建一個客戶端 socket,並連接到服務端 socket
private async void btnConnectListener_Click(object sender, RoutedEventArgs e)
{
HostName hostName;
try
{
hostName = new HostName("127.0.0.1");
}
catch (Exception ex)
{
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
return;
}
// 實例化一個客戶端 socket 對象
_client = new StreamSocket();
try
{
// 連接到指定的服務端 socket
await _client.ConnectAsync(hostName, "2211");
lblMsg.Text += "已經連接上了 127.0.0.1:2211";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 從客戶端 socket 發送一個字符串數據到服務端 socket
private async void btnSendData_Click(object sender, RoutedEventArgs e)
{
// 實例化一個 DataWriter,用於發送數據
if (_writer == null)
_writer = new DataWriter(_client.OutputStream);
// 自定義協議(header|body):前4個字節代表實際數據的長度,之後的實際數據為一個字符串數據
string data = "hello webabcd " + DateTime.Now.ToString("hh:mm:ss");
_writer.WriteUInt32(_writer.MeasureString(data)); // 寫 header 數據
_writer.WriteString(data); // 寫 body 數據
try
{
// 發送數據
await _writer.StoreAsync();
lblMsg.Text += "數據已發送: " + data;
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 關閉客戶端 socket 和服務端 socket
private void btnCloseSocket_Click(object sender, RoutedEventArgs e)
{
try
{
_writer.DetachStream(); // 分離 DataWriter 與 Stream 的關聯
_writer.Dispose();
_client.Dispose();
_listener.Dispose();
lblMsg.Text += "服務端 socket 和客戶端 socket 都關閉了";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
}
}
2、演示 socket 的應用(演示如何實現一個 http server,需要注意的是此 http server 只能在此 app 內部調用,而不能在外部調用)
Communication/Socket/CustomHttpServer.xaml
<Page
x:Class="XamlDemo.Communication.Socket.CustomHttpServer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Communication.Socket"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<StackPanel Margin="120 0 0 0" Orientation="Horizontal">
<StackPanel>
<Button Name="btnLaunchHttpServer" Content="啟動 http server"
Click="btnLaunchHttpServer_Click" />
<Button Name="btnRequestHttpServer" Content="請求 http server"
Click="btnRequestHttpServer_Click" Margin="0 10 0 0" />
<Button Name="btnCloseHttpServer" Content="關閉 http server"
Click="btnCloseHttpServer_Click" Margin="0 10 0 0" />
</StackPanel>
<TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap"
Margin="20 0 0 0" />
</StackPanel>
</Grid>
</Page>
Communication/Socket/CustomHttpServer.xaml.cs
/*
* 演示 socket 的應用(演示如何實現一個 http server,需要注意的是此 http server
只能在此 app 內部調用,而不能在外部調用)
*
* 注:需要在 Package.appxmanifest 中增加配置 <Capability
Name="privateNetworkClientServer" />
*/
using System;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System.Text;
using System.Net.Http;
namespace XamlDemo.Communication.Socket
{
public sealed partial class CustomHttpServer : Page
{
/// <summary>
/// http server 的 socket
/// </summary>
private StreamSocketListener _listener;
public CustomHttpServer()
{
this.InitializeComponent();
}
// 啟動 http server,即在服務端啟動一個 socket 監聽
private async void btnLaunchHttpServer_Click(object sender, RoutedEventArgs e)
{
// 實例化一個 socket 監聽對象
_listener = new StreamSocketListener();
// 監聽在接收到一個連接後所觸發的事件
_listener.ConnectionReceived += _listener_ConnectionReceived;
try
{
// 在指定的端口上啟動 socket 監聽
await _listener.BindServiceNameAsync("2212");
lblMsg.Text += "已經在本機的 2212 端口啟動了 socket 監聽,即 http server 的地址為:http://localhost:2212/";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// socket 監聽接收到一個連接後,即收到一個 http 請求後
async void _listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
// 實例化一個 DataReader,用於讀取數據
DataReader reader = new DataReader(args.Socket.InputStream);
try
{
// 用於保存 http 請求的 header
StringBuilder sb = new StringBuilder();
while (true)
{
// 一個字節一個字節地讀
uint loaded = await reader.LoadAsync(1);
if (loaded != 1)
return;
char c = (char)reader.ReadByte();
sb.Append(c);
// 碰到 \r\n\r\n 則說明已經完整地獲取了 header 信息
if (sb.ToString().IndexOf("\r\n\r\n") > 0)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lblMsg.Text += "接收到的 http request header: ";
lblMsg.Text += Environment.NewLine;
lblMsg.Text += sb.ToString();
lblMsg.Text += Environment.NewLine;
});
// 用於保存 http 響應的數據
string response = "";
// 響應的 http 頭信息
response += "HTTP/1.1 200 OK";
response += "Content-Type:text/html\r\n";
response += "Server:Custom Http Server\r\n";
response += "Content-Length:2\r\n";
response += "Connection: Keep-Alive\r\n\r\n";
// 頭信息以 \r\n\r\n 結尾
// 響應的內容
response += "ok";
// 將已經構造好的數據響應給客戶端
DataWriter writer = new DataWriter(args.Socket.OutputStream);
writer.WriteString(response);
await writer.StoreAsync();
sb.Clear();
}
}
}
catch (Exception ex)
{
var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
});
}
}
private async void btnRequestHttpServer_Click(object sender, RoutedEventArgs e)
{
HttpClient client = new HttpClient();
// 請求 http server
string result = await client.GetStringAsync(new Uri("http://localhost:2212/abc.html"));
// 輸出 http server 的響應結果
lblMsg.Text += "response: " + result;
lblMsg.Text += Environment.NewLine;
}
// 關閉 http server,即關閉服務端 socket
private void btnCloseHttpServer_Click(object sender, RoutedEventArgs e)
{
try
{
_listener.Dispose();
lblMsg.Text += "http server 關閉了";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
}
}
查看本欄目
3、演示 socket udp 的應用(本例既做服務端又做客戶端)
Communication/Socket/UdpDemo.xaml
<Page
x:Class="XamlDemo.Communication.Socket.UdpDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Communication.Socket"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<StackPanel Margin="120 0 0 0" Orientation="Horizontal">
<StackPanel>
<Button Name="btnStartListener" Content="start a socket
listener" Click="btnStartListener_Click" />
<Button Name="btnSendData" Content="send data"
Click="btnSendData_Click" Margin="0 10 0 0" />
<Button Name="btnCloseSocket" Content="close server socket and
client socket" Click="btnCloseSocket_Click" Margin="0 10 0 0" />
</StackPanel>
<TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap"
Margin="20 0 0 0" />
</StackPanel>
</Grid>
</Page>
Communication/Socket/UdpDemo.xaml.cs
/*
* 演示 socket udp 的應用(本例既做服務端又做客戶端)
*
* 通過 DatagramSocket 來實現 udp 通信的服務端和客戶端
*
* 注:需要在 Package.appxmanifest 中增加配置 <Capability
Name="privateNetworkClientServer" />
* 注:udp 報文(Datagram)的最大長度為 65535(包括報文頭)
*/
using System;
using System.Threading;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlDemo.Communication.Socket
{
public sealed partial class UdpDemo : Page
{
/// <summary>
/// 服務端 socket
/// </summary>
private DatagramSocket _listener;
/// <summary>
/// 客戶端 socket
/// </summary>
private DatagramSocket _client;
/// <summary>
/// 客戶端向服務端發送數據時的 DataWriter
/// </summary>
private DataWriter _writer;
public UdpDemo()
{
this.InitializeComponent();
}
// 在服務端啟動一個 socket 監聽
private async void btnStartListener_Click(object sender, RoutedEventArgs e)
{
// 實例化一個 socket 監聽對象
_listener = new DatagramSocket();
// 服務端監聽在接收到信息後所觸發的事件
_listener.MessageReceived += _listener_MessageReceived;
try
{
// 在指定的端口上啟動 socket 監聽
await _listener.BindServiceNameAsync("2213");
lblMsg.Text += "已經在本機的 2213 端口啟動了 socket(udp) 監聽";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 服務端的 socket udp 監聽接收到信息後
private void _listener_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
try
{
// 輸出接收到的數據
DataReader dataReader = args.GetDataReader();
uint stringLength = dataReader.UnconsumedBufferLength;
var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lblMsg.Text += "接收到數據: " + dataReader.ReadString(stringLength) + " - 數據來自: " + args.RemoteAddress.RawName + ":" + args.RemotePort;
lblMsg.Text += Environment.NewLine;
});
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 從客戶端 socket 發送數據到服務端 socket
private async void btnSendData_Click(object sender, RoutedEventArgs e)
{
if (_client == null)
{
HostName hostName;
try
{
hostName = new HostName("127.0.0.1");
}
catch (Exception ex)
{
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
return;
}
try
{
// 實例化客戶端 DatagramSocket,並准備往 127.0.0.1:2213 發送數據
_client = new DatagramSocket();
await _client.ConnectAsync(hostName, "2213"); // ConnectAsync() 並非連接的意思,udp 沒有連接一說,這裡用於設置發送數據的目標
lblMsg.Text += "准備往 127.0.0.1:2213 發送數據";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 實例化一個 DataWriter,用於發送數據
if (_writer == null)
_writer = new DataWriter(_client.OutputStream);
try
{
string data = "hello webabcd " + DateTime.Now.ToString("hh:mm:ss");
_writer.WriteString(data);
// 發送數據
await _writer.StoreAsync();
lblMsg.Text += "數據已發送: " + data;
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);
lblMsg.Text += "errStatus: " + errStatus.ToString();
lblMsg.Text += Environment.NewLine;
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
// 關閉客戶端 socket 和服務端 socket
private void btnCloseSocket_Click(object sender, RoutedEventArgs e)
{
try
{
_writer.DetachStream(); // 分離 DataWriter 與 Stream 的關聯
_writer.Dispose();
_client.Dispose();
_listener.Dispose();
lblMsg.Text += "服務端 socket 和客戶端 socket 都關閉了";
lblMsg.Text += Environment.NewLine;
}
catch (Exception ex)
{
lblMsg.Text += ex.ToString();
lblMsg.Text += Environment.NewLine;
}
}
}
}
OK
[源碼下載]:http://files.cnblogs.com/webabcd/Windows8.rar