Win8 开发:聊天程序

38 篇文章 1 订阅
Socket一直是一个痛苦的玩意,不过,还是要把它说一说,其实,我们完全可以用WCF实现网络通信功能。

  今天先说说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 ==  nullreturn;
             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);
            }
        }
    }
}

  同时启动两个应用就可以测试了,注意远程端口要填对,对方在哪个端口上侦听你就填那个端口号就行了。

  代码随后上传到资源中。

  本文来自tcjiaan的博客,原文地址:http://blog.csdn.net/tcjiaan/article/details/8255955


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值