介紹
重新想象 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