Windows Phone开发(46):与Socket有个约会

移动开发
在很多情况下,关于Socket的例子,都会做一个聊天程序的,不过,聊天程序要求服务器端和客户都具有发送和接收数据的功能,这样会增加实例的难度和代码长度,不方便入门者阅读。所以,想了一下,今天咱们不玩聊天的,今天咱们玩遥控飞机,如何?

不知道大家有没有“谈Socket色变”的经历?就像我一位朋友所说的,Socket这家伙啊,不得已而用之。哈,Socket真的那么恐怖吗?

其实这话一点也不假,Socket有时候真的不太好操控,也不好维护,但不管怎么样,我们还是要面对它的,没准Socket是一位大美女哦。

关于Socket的前世今生就不用我详述了,关于她的历史,已经不少人仁志士为她立传 写著了,像我们国内的百度百科、互动百科等;全球著名的如维基百科之属。而且,能加入WP开发的学习行列的,我想各位对.NET的其它技术肯定是有一定基 础的。我也相信,各位同仁过去一定也写过与Socket打交道的程序。那么,WP中的Socket又将如何呢?

前提公布答案吧,在WP中使用Socket跟你在其它桌面应用项目如WinForm,WPF等中是一样的,而且说白了,WP中的Socket只不过是从Silverlight框架中继承过来的。

.NET的一大优势就是集成性和统一性都好,这不,你看,无论你是编写桌面应用程序,还是WP上的应用程序,你会发现,你的学习成本不高,很多东西都是一样的,而且是相通的。显然这也是Win8和WP8的应用程序可以整合的原因吧。

在WP中使用Socket要注意以下几点:

1、WP客户端应用程序一般不被视为服务器端,因为不能进行绑定本地终结点和监听连接。但是,收发数据是没问题D。

2、在WP中的Socket操作(连接、接收以及发送)都是异步进行的。如果希望UI线程和后前线程进行同步,不妨使用System.Threading.ManualResetEvent类,这个东西不好讲述,也不好理解。这样吧,我举一个例子。

有一天,NC和脑残因为一件小事闹冲突,闹来闹去还是不能解决,怎么办呢?于是,NC和脑残决定来一场比试。两人约定以跑步方式比试,谁跑得快谁就是胜利者。然而,NC这个人一向比较自负,他相信脑残绝对跑不过他。这样,NC就修改了比赛规则:

NC让脑残先跑5秒,然后他才开始。

假设NC是主线程,脑残是后台线程,现在的情况是:主线程先等待一会儿,让后台线程先 执行;后台线程执行5秒后向主线程发出信号,主线程收到信号后再继续往下执行。按照故事里的情节:NC先让脑残跑5秒钟,他自己就在起跑线上等待,脑残跑 了5秒后向NC发出信号,NC看到信号后就开始跑。

下面介绍一个类——SocketAsyncEventArgs。

这个类作为启动异步操作时传递的参数,它可以包含如接收数据的缓冲区、远程主机、用户自定义对象等内容,这个类并不复杂,打开“对象浏览器”看看就一目了然了。

要设置用于异步接收数据的缓冲区,应调用SetBuffer方法。

好,理论就扯到这儿,其实也没有什么新的知识点,我只是简单提一下罢了。

按照惯例,大家都会猜到,理论过后要干什么了,是的,付诸实践。

在很多情况下,关于Socket的例子,都会做一个聊天程序的,不过,聊天程序要求服务器端和客户都具有发送和接收数据的功能,这样会增加实例的难度和代码长度,不方便入门者阅读。所以,想了一下,今天咱们不玩聊天的,今天咱们玩遥控飞机,如何?

程序代码较长,也不便于逐一来讲解,这样吧,为了保持代码的可读性,我会把完整的代码都贴出来,在代码中我会适当地加上注释。

先说一下原理,利用Socket进行通讯这不用说了,那是肯定的。功能是通过WP手机客户端应用程序来控制PC端播放、暂停和停止动画,而动画嘛,也不弄那么复杂了,就弄个矩形从左边移到右边的动画吧。

***部分  服务器端

既然要播放动画,少不了要用WPF了,而且,也方便贴界面布局的代码。

1、新建WPF应用程序项目。

2、打开MainWindow.xaml文件(默认新建项目后自动打开),输入以下XAML代码。

<Window x:Class="MYServer.MainWindow"   
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
        Title="服务器端" Height="350" Width="525">   
    <Window.Resources>   
        <Storyboard x:Key="std">   
            <DoubleAnimation Duration="0:0:5"   
                                 Storyboard.TargetName="rect"   
                                 Storyboard.TargetProperty="(Rectangle.RenderTransform).(TranslateTransform.X)"   
                                 To="400"/>   
        </Storyboard>   
    </Window.Resources>   
    <Grid>   
        <Grid.RowDefinitions>   
            <RowDefinition />   
            <RowDefinition Height="Auto" />   
        </Grid.RowDefinitions>   
        <Rectangle x:Name="rect" Grid.Row="0" Width="50" Height="50" Fill="Orange" HorizontalAlignment="Left" VerticalAlignment="Center">   
            <Rectangle.RenderTransform>   
                <TranslateTransform X="0" Y="0"/>   
            </Rectangle.RenderTransform>   
        </Rectangle>   
        <TextBlock Name="txtDisplay" Grid.Row="1"/>   
    </Grid>   
</Window>   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

3、打开MainWindow.xaml.cs文件,完成后台代码逻辑。

using System;   
using System.Collections.Generic;   
using System.Linq;   
using System.Text;   
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.Windows.Media.Animation;   
using System.IO;   
using System.Net;   
using System.Net.Sockets;   
namespace MYServer   
{   
    /// <summary>   
    /// MainWindow.xaml 的交互逻辑   
    /// </summary>   
    public partial class MainWindow : Window   
    {   
        Storyboard std = null//演示图板   
        public MainWindow()   
        {   
            InitializeComponent();   
            // 从资源中把Key为std的Storyboard读出来   
            std = this.Resources["std"as Storyboard;   
            // 声明用于监听连接请求的Socket   
            Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);   
            IPEndPoint local = new IPEndPoint(IPAddress.Any, 1377); //监听所有网络接口上的地址   
            Server.Bind(local);// 绑定本地终结点   
            Server.Listen(100);// 侦听连接请求   
            // 开始异步接受传入的连接请求   
            Server.BeginAccept(new AsyncCallback(this.AcceptSocketCallback), Server);   
        }   
        /// <summary>   
        /// 接受传入的Socket的回调   
        /// </summary>   
        private void AcceptSocketCallback(IAsyncResult ia)   
        {   
            Socket _socket = ia.AsyncState as Socket;   
            Socket accptSocket = _socket.EndAccept(ia);   
            try   
            {   
                IPEndPoint remote = (IPEndPoint)accptSocket.RemoteEndPoint;   
                // 显示客户端的IP   
                Dispatcher.BeginInvoke(new Action<string>(this.SetIPForText), remote.Address.ToString());   
                StateObject so = new StateObject();   
                so.theSocket = accptSocket;   
                // 开始异步接收消息   
                accptSocket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, new AsyncCallback(this.ReceiveCallback), so);   
            }   
            catch   
            {   
            }   
            // 继续接受连接请求   
            _socket.BeginAccept(new AsyncCallback(this.AcceptSocketCallback), _socket);   
        }   
        /// <summary>   
        /// 接收消息的回调   
        /// </summary>   
        private void ReceiveCallback(IAsyncResult ia)   
        {   
            StateObject _so = ia.AsyncState as StateObject;   
            Socket _socket = _so.theSocket;   
            try   
            {   
                int n = _socket.EndReceive(ia);//n就是接收到的字节数   
                string msg = Encoding.UTF8.GetString(_so.Buffer, 0, n);   
                // 判断客户端发送了啥命令   
                switch (msg)   
                {   
                    case "play":   
                        Dispatcher.BeginInvoke(new Action(this.Play), null);   
                        break;   
                    case "pause":   
                        Dispatcher.BeginInvoke(new Action(this.Pause), null);   
                        break;   
                    case "stop":   
                        Dispatcher.BeginInvoke(new Action(this.Stop), null);   
                        break;   
                    default:   
                        break;   
                }   
            }   
            catch    
            {   
            }   
            _so = new StateObject();   
            _so.theSocket = _socket;   
            // 继续接收消息   
            _socket.BeginReceive(_so.Buffer,   
                                0,   
                                _so.Buffer.Length,   
                                SocketFlags.None,   
                                new AsyncCallback(this.ReceiveCallback),   
                                _so);   
        }   
        /// <summary>   
        /// 显示客户端的IP   
        /// </summary>   
        private void SetIPForText(string ip)   
        {   
            this.txtDisplay.Text = "客户端IP:" + ip;   
        }   
        #region 控制动画的方法   
        private void Play()   
        {   
            std.Begin();   
        }   
        private void Pause()   
        {   
            std.Pause();   
        }   
        private void Stop()   
        {   
            std.Stop();   
        }   
        #endregion   
    }   
    /// <summary>   
    /// 用于异步Socket操作传递的状态对象   
    /// </summary>   
    public class StateObject   
    {   
        private const int BUFFER_SIZE = 512;   
        public byte[] Buffer { get;  set; }   
        public Socket theSocket { getset; }   
        /// <summary>   
        /// 构造函数   
        /// </summary>   
        public StateObject()   
        {   
            this.Buffer = new byte[BUFFER_SIZE];   
        }   
    }   
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.

别走开,下页为您介绍WP客户端

 #p#

第二部分  WP客户端

1、新建Windows Phone应用程序项目。

2、打开MainPage.xaml文件,参考下面的XAML代码。

<phone:PhoneApplicationPage    
    x:Class="WPClient.MainPage"   
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"   
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"   
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"   
    FontFamily="{StaticResource PhoneFontFamilyNormal}"   
    FontSize="{StaticResource PhoneFontSizeNormal}"   
    Foreground="{StaticResource PhoneForegroundBrush}"   
    SupportedOrientations="Portrait" Orientation="Portrait"   
    shell:SystemTray.IsVisible="True">   
    <!--LayoutRoot 是包含所有页面内容的根网格-->   
    <Grid x:Name="LayoutRoot" Background="Transparent">   
        <Grid.RowDefinitions>   
            <RowDefinition Height="Auto"/>   
            <RowDefinition Height="*"/>   
        </Grid.RowDefinitions>   
        <!--TitlePanel 包含应用程序的名称和页标题-->   
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">   
            <TextBlock x:Name="ApplicationTitle" Text="我的应用程序" Style="{StaticResource PhoneTextNormalStyle}"/>   
            <TextBlock x:Name="PageTitle" Text="页面名称" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>   
        </StackPanel>   
        <!--ContentPanel - 在此处放置其他内容-->   
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">   
            <Grid.RowDefinitions>   
                <RowDefinition Height="auto"/>   
                <RowDefinition Height="*"/>   
            </Grid.RowDefinitions>   
            <Grid Grid.Row="0">   
                <Grid.ColumnDefinitions>   
                    <ColumnDefinition Width="Auto" />   
                    <ColumnDefinition />   
                    <ColumnDefinition Width="Auto" />   
                </Grid.ColumnDefinitions>   
                <TextBlock Grid.Column="0" VerticalAlignment="Center" Text="服务器IP:" />   
                <TextBox Name="txtServerIP" Grid.Column="1"/>   
                <Button Grid.Column="2" Content="连接" Click="onConnect"/>   
            </Grid>   
            <StackPanel Grid.Row="1">   
                <Button Content="放播动画" Click="onPlay"/>   
                <Button Content="暂停动画" Click="onPause"/>   
                <Button Content="停止动画" Click="onStop"/>   
                <TextBlock Name="txtbInfo" Margin="3,18,3,0"/>   
            </StackPanel>   
        </Grid>   
    </Grid>   
    <!--演示 ApplicationBar 用法的示例代码-->   
    <!--<phone:PhoneApplicationPage.ApplicationBar>   
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">   
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="按钮 1"/>   
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="按钮 2"/>   
            <shell:ApplicationBar.MenuItems>   
                <shell:ApplicationBarMenuItem Text="菜单项 1"/>   
                <shell:ApplicationBarMenuItem Text="菜单项 2"/>   
            </shell:ApplicationBar.MenuItems>   
        </shell:ApplicationBar>   
    </phone:PhoneApplicationPage.ApplicationBar>-->   
</phone:PhoneApplicationPage>   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.

3、打开MainPage.xaml.cs,输入以下代码。

using System;   
using System.Collections.Generic;   
using System.Linq;   
using System.Net;   
using System.Windows;   
using System.Windows.Controls;   
using System.Windows.Documents;   
using System.Windows.Input;   
using System.Windows.Media;   
using System.Windows.Media.Animation;   
using System.Windows.Shapes;   
using Microsoft.Phone.Controls;   
using System.Net.Sockets;   
using System.IO;   
using System.Threading;   
namespace WPClient   
{   
    public partial class MainPage : PhoneApplicationPage   
    {   
        Socket mySocket = null;   
        ManualResetEvent MyEvent = null;   
        // 构造函数   
        public MainPage()   
        {   
            InitializeComponent();   
        }   
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)   
        {   
            base.OnNavigatedTo(e);   
            if (mySocket == null)   
            {   
                mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);   
            }   
            if (MyEvent == null)   
            {   
                MyEvent = new ManualResetEvent(false);   
            }   
        }   
        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)   
        {   
            if (mySocket != null)   
            {   
                mySocket.Shutdown(SocketShutdown.Both);   
                mySocket.Close();   
            }   
            base.OnNavigatedFrom(e);   
        }   
        private void onConnect(object sender, RoutedEventArgs e)   
        {   
            if (mySocket != null)   
            {   
                SocketAsyncEventArgs connArg = new SocketAsyncEventArgs();   
                // 要连接的远程服务器   
                connArg.RemoteEndPoint = new DnsEndPoint(this.txtServerIP.Text, 1377);   
                // 操作完成后的回调   
                connArg.Completed += (sendObj, arg) =>   
                {   
                    if (arg.SocketError == SocketError.Success) //连接成功   
                    {   
                        Dispatcher.BeginInvoke(() => txtbInfo.Text = "连接成功。");   
                    }   
                    else   
                    {   
                        Dispatcher.BeginInvoke(() =>   
                        {   
                            txtbInfo.Text = "连接失败,错误:" + arg.SocketError.ToString();   
                        });   
                    }   
                    // 向调用线程报告操作结束   
                    MyEvent.Set();   
                };   
                // 重置线程等待事件   
                MyEvent.Reset();   
                txtbInfo.Text = "正在连接,请等候……";   
                // 开始异连接   
                mySocket.ConnectAsync(connArg);   
                // 等待连接完成   
                MyEvent.WaitOne(6000);   
            }   
        }   
        private void onPause(object sender, RoutedEventArgs e)   
        {   
            SendCommand("pause");   
        }   
        private void onStop(object sender, RoutedEventArgs e)   
        {   
            SendCommand("stop");   
        }   
        private void onPlay(object sender, RoutedEventArgs e)   
        {   
            SendCommand("play");   
        }   
        private void SendCommand(string txt)   
        {   
            if (mySocket != null && mySocket.Connected)   
            {   
                SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();   
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(txt);   
                sendArg.SetBuffer(buffer, 0, buffer.Length);   
                // 发送完成后的回调   
                sendArg.Completed += (objSender, mArg) =>   
                    {   
                        // 如果操作成功   
                        if (mArg.SocketError == SocketError.Success)   
                        {   
                            Dispatcher.BeginInvoke(() => txtbInfo.Text = "发送成功。");   
                        }   
                        else   
                        {   
                            Dispatcher.BeginInvoke(() =>   
                                {   
                                    this.txtbInfo.Text = "发送失败,错误:" + mArg.SocketError.ToString();   
                                });   
                        }   
                        // 报告异步操作结束   
                        MyEvent.Set();   
                    };   
                // 重置信号   
                MyEvent.Reset();   
                txtbInfo.Text = "正在发送,请等候……";   
                // 异步发送   
                mySocket.SendAsync(sendArg);   
                // 等待操作完成   
                MyEvent.WaitOne(6000);   
            }   
        }   
    }   
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.

先运行服务器端,再在WP模拟器或真实手机上运行客户端。

在手机客户端中,输入IP地址,点“连接”,连接成功后,就可以发送指令了。

 好的,就到这儿吧,示例的源码我会上专到“资源”中,有需要的话,大家可以按标题下载。

责任编辑:闫佳明 来源: oschina
相关推荐

2015-12-11 17:24:50

Androidgradle开发

2013-07-30 12:37:56

Windows PhoWindows Pho

2010-04-21 17:07:54

Windows Pho

2013-04-17 14:00:06

Windows PhoWindows Pho

2011-06-07 12:42:15

Windows Pho

2013-04-25 16:42:44

Windows Pho反思、建议与忠告

2013-07-30 11:18:37

Windows PhoWindows Pho

2013-04-19 16:34:56

Windows PhoWindows Pho

2013-04-16 17:02:50

Windows Pho概论

2010-04-12 17:32:59

Windows Pho

2010-04-08 17:40:23

Windows Pho

2013-04-17 14:47:19

Windows PhoWindows Pho

2013-07-31 13:03:51

Windows PhoWindows Pho

2012-08-16 10:35:50

Windows Pho

2011-06-07 11:35:38

Windows Pho

2013-04-17 13:27:04

Windows PhoWindows Pho

2010-07-16 15:29:02

Windows Pho

2010-12-14 18:48:49

微软

2013-07-31 12:50:39

搭建Windows PWindows Pho

2013-07-31 13:13:50

Windows PhoMVVM模式
点赞
收藏

51CTO技术栈公众号