新时尚Windows8开发(38):聊天程序
2012-12-04 15:19
344 查看
Socket一直是一个痛苦的玩意,不过,还是要把它说一说,其实,我们完全可以用WCF实现网络通信功能。
今天先说说DatagramSocket类,别看这名字好像有些陌生,其实,说白了,这家伙只是换了个“马甲”罢了,本质上说就是UDP传输,最适合做就是传输一些简单的文本信息,所以,弄个聊天程序相当合适。
由于Windows“板砖”应用一般是一个应用窗口占满整个屏幕,有时候可能会挂到屏幕的一边,为了说明DatagramSocket就是UDP协议的socket,我们一端使用Windows Store应用程序,而另一端使用WPF来开发,看看这两者之间的通信就可以说明。
为了在本机测试操作更方便,在Windows Store应用端可以考虑用模拟器来运行,模拟器是根据本地机器当前的系统来模拟的,所以,IP地址可以使用127.0.0.1。
以下是应用运行的效果图。
为了不产生乱码,通信双方均用UTF-8编码,这样它们才有默契。
隐藏代码如下:【C#】
注意以下几点:
1、调用BindServiceNameAsync或BindEndpointAsync方法绑定本地终结点之前,先注册MessageReceived事件处理。
2、在发送消息后,不要把输出流关了,不然下次再发消息时就会发生异常,关闭流就几乎相当于把socket也关了。
WriteString完了之后,数据还没有发送,StoreAsync调用后,数据才被提交到流中。用完之后要调用DetachStream,将DataWriter与流进行分离,但不要关闭流。
而对于后面的代码,就跟以前的.NET开发一样了,用UdpClient类就能完成了。
同时启动两个应用就可以测试了,注意远程端口要填对,对方在哪个端口上侦听你就填那个端口号就行了。
代码随后上传到资源中。
今天先说说DatagramSocket类,别看这名字好像有些陌生,其实,说白了,这家伙只是换了个“马甲”罢了,本质上说就是UDP传输,最适合做就是传输一些简单的文本信息,所以,弄个聊天程序相当合适。
由于Windows“板砖”应用一般是一个应用窗口占满整个屏幕,有时候可能会挂到屏幕的一边,为了说明DatagramSocket就是UDP协议的socket,我们一端使用Windows Store应用程序,而另一端使用WPF来开发,看看这两者之间的通信就可以说明。
为了在本机测试操作更方便,在Windows Store应用端可以考虑用模拟器来运行,模拟器是根据本地机器当前的系统来模拟的,所以,IP地址可以使用127.0.0.1。
以下是应用运行的效果图。
为了不产生乱码,通信双方均用UTF-8编码,这样它们才有默契。
第一项,实现Windows Store端。
主页布局参考下面XAML,我不再解释。<Page x:Class="WStoreSocketApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WStoreSocketApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <Style x:Key="tbfield" TargetType="TextBlock"> <Setter Property="FontSize" Value="20"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Margin" Value="2,0,6,0"/> </Style> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel> <!-- 用于显示接收到的消息 --> <ListBox x:Name="lbRecMessages" Height="300"/> <Grid Margin="0,18,0,15"> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition/> <ColumnDefinition Width="auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Style="{StaticResource tbfield}" Text="远程主机:"/> <TextBox x:Name="txtRemote" Grid.Column="1" Width="260" HorizontalAlignment="Left" Text="127.0.0.1"/> <TextBlock Grid.Column="2" Style="{StaticResource tbfield}" Text="远程端口:"/> <TextBox x:Name="txtPort" Grid.Column="3" Width="90" HorizontalAlignment="Left"/> </Grid> <!-- 用于输入要发送的消息 --> <TextBox x:Name="txtMessageInput" Margin="2,13,2,16" Height="180"/> <StackPanel Margin="3,10,0,15" Orientation="Horizontal"> <Button Content="发送消息" Margin="0,1,12,2" Click="onSend"/> <TextBlock Margin="8,2,0,2" x:Name="tbMessage" FontSize="22"/> </StackPanel> </StackPanel> </Grid> </Page>
隐藏代码如下:【C#】
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; using Windows.Storage.Streams; using Windows.Networking; using Windows.Networking.Sockets; namespace WStoreSocketApp { /// <summary> /// 可用于自身或导航至 Frame 内部的空白页。 /// </summary> public sealed partial class MainPage : Page { DatagramSocket mySocket = null; const string LOCAL_PORT = "9700"; //本地端口 public MainPage() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { if (mySocket == null) { mySocket = new DatagramSocket(); mySocket.MessageReceived += mySocket_MessageReceived; await mySocket.BindServiceNameAsync(LOCAL_PORT); } } async void mySocket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { var reader = args.GetDataReader(); reader.UnicodeEncoding = UnicodeEncoding.Utf8; string msg = reader.ReadString(reader.UnconsumedBufferLength); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { this.lbRecMessages.Items.Add(msg); }); } private async void onSend(object sender, RoutedEventArgs e) { if (mySocket == null) return; if (this.txtRemote.Text == "" || this.txtPort.Text == "") { return; } HostName host = new HostName(this.txtRemote.Text); var outStream = await mySocket.GetOutputStreamAsync(host, this.txtPort.Text); DataWriter writer = new DataWriter(outStream); // 往流里面写数据 writer.WriteString(this.txtMessageInput.Text); await writer.StoreAsync(); writer.DetachStream(); tbMessage.Text = "消息已发送。"; txtMessageInput.Text = ""; } } }
注意以下几点:
1、调用BindServiceNameAsync或BindEndpointAsync方法绑定本地终结点之前,先注册MessageReceived事件处理。
2、在发送消息后,不要把输出流关了,不然下次再发消息时就会发生异常,关闭流就几乎相当于把socket也关了。
writer.WriteString(this.txtMessageInput.Text); await writer.StoreAsync(); writer.DetachStream();
WriteString完了之后,数据还没有发送,StoreAsync调用后,数据才被提交到流中。用完之后要调用DetachStream,将DataWriter与流进行分离,但不要关闭流。
第二项,WPF端。
界面布局很简单,XAML具备极强的可移植性,所以,直接从刚才上面的应用中复制XAML到WPF项目的MainWindow.xaml中,然后稍微改一下就行了。<Window x:Class="wpfUDPSocketApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="客户端" Height="500" Width="800"> <Window.Resources> <Style x:Key="tbfield" TargetType="{x:Type TextBlock}"> <Setter Property="FontSize" Value="20"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Margin" Value="2,0,6,0"/> </Style> </Window.Resources> <Grid> <StackPanel> <!-- 用于显示接收到的消息 --> <ListBox x:Name="lbRecMessages" Height="200"/> <Grid Margin="0,18,0,15"> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Style="{DynamicResource tbfield}" Text="远程主机:"/> <TextBox x:Name="txtRemote" Grid.Column="1" Width="260" HorizontalAlignment="Left" Text="127.0.0.1"/> <TextBlock Grid.Column="2" Style="{StaticResource tbfield}" Text="远程端口:"/> <TextBox x:Name="txtPort" Grid.Column="3" Width="90" HorizontalAlignment="Left"/> </Grid> <!-- 用于输入要发送的消息 --> <TextBox x:Name="txtMessageInput" Margin="2,13,2,16" Height="95"/> <StackPanel Margin="3,10,0,15" Orientation="Horizontal"> <Button Content="发送消息" Margin="0,1,12,2" Click="onSend"/> <TextBlock Margin="8,2,0,2" x:Name="tbMessage" FontSize="22"/> </StackPanel> </StackPanel> </Grid> </Window>
而对于后面的代码,就跟以前的.NET开发一样了,用UdpClient类就能完成了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net; using System.IO; using System.Net.Sockets; namespace wpfUDPSocketApp { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { UdpClient mClient = null; const int LOCAL_PORT = 9800;//本地端口 public MainWindow() { InitializeComponent(); this.mClient = new UdpClient(LOCAL_PORT); this.Loaded += (a, b) => { Task.Run(new Action(this.ReceiveMessage)); }; } private void onSend(object sender, RoutedEventArgs e) { int rmPort=int.MinValue; if (mClient != null && this.txtMessageInput.Text != "" && this.txtRemote.Text != "" && int.TryParse(txtPort.Text,out rmPort)) { byte[] buffer = Encoding.UTF8.GetBytes(this.txtMessageInput.Text); mClient.Send(buffer, buffer.Length, this.txtRemote.Text, rmPort); tbMessage.Text = "消息已发送。"; txtMessageInput.Clear(); } } private void ReceiveMessage() { IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); while (mClient != null) { byte[] buffer = mClient.Receive(ref ep); string msg = Encoding.UTF8.GetString(buffer); Dispatcher.BeginInvoke(new Action(() => { this.lbRecMessages.Items.Add(msg); }), null); } } } }
同时启动两个应用就可以测试了,注意远程端口要填对,对方在哪个端口上侦听你就填那个端口号就行了。
代码随后上传到资源中。
相关文章推荐
- 移动应用(手机应用)开发IM聊天程序解决方案
- 新时尚Windows8开发(32):Json数据处理(A)
- 新时尚Windows8开发(2):应用程序生命周期
- Xamarin开发手机聊天程序
- 聊天程序开发
- 新时尚Windows8开发(21):分组视图
- 新时尚Windows8开发(25):缩放视图
- 新时尚Windows8开发(4):保存/读取本地应用程序设置
- Android蓝牙聊天程序的扩展开发(基于Google Sample,类QQ设计)
- 新时尚Windows8开发(32):Json数据处理(A)
- 概述IE和SQL2k开发一个XML聊天程序
- 新时尚Windows8开发(9):加密和解密数据
- 新时尚Windows8开发(1):如何创建应用程序项目
- 新时尚Windows8开发(10):如何共享文本内容
- 新时尚Windows8开发(33):Json数据处理(B)
- Android 蓝牙开发实例--蓝牙聊天程序的设计和实现
- 新时尚Windows8开发(13):如何选择保存文件
- 新时尚Windows8开发(35):后台传输
- 新时尚Windows8开发(31):去掉文本中的HTML标记
- 聊天程序WMChat开发文档