新時尚Windows8開發(38):聊天程式

來源:互聯網
上載者:User

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 == 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);            }        }    }}

同時啟動兩個應用就可以測試了,注意遠程連接埠要填對,對方在哪個連接埠上偵聽你就填那個連接埠號碼就行了。

代碼隨後上傳到資源中。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.