重新想象 Windows 8 Store Apps (62) - 通信: Socket TCP, Socket UDP
重新想象 Windows 8 Store Apps (62) - 通信: Socket TCP, Socket UDP
作者:webabcd
介绍
重新想象 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
[源码下载]