02、Windows Phone 套接字(Socket)实战之服务器端设计

 

    这里主要写 PC 服务器端的逻辑,UI 使用的是 WPF,因为 WPF 比普通的 WinForm 的流式布局

更容易控制,而且比 WinForm 美观一些,显示截图:

 

 

一、页面 UI

      MainWindow.xaml 文件中布局的 XAML:

   <Grid ShowGridLines="True">
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="Width" Value="110"/>
                <Setter Property="Height" Value="30"/>
                <Setter Property="Margin" Value="10,0,10,0"/>
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80*"/>
            <ColumnDefinition Width="20*"/>
        </Grid.ColumnDefinitions>
        <Grid ShowGridLines="True">
            <Grid.RowDefinitions>
                <RowDefinition Height="1*"/>
                <RowDefinition Height="7*"/>
                <RowDefinition Height="2*"/>
            </Grid.RowDefinitions>
            <!--本地 IP-->
            <StackPanel Orientation="Horizontal">
                <StackPanel.Resources>
                    <Style TargetType="TextBlock">
                        <Setter Property="Width" Value="120"/>
                        <Setter Property="VerticalAlignment" Value="Center"/>
                        <Setter Property="HorizontalAlignment" Value="Center"/>
                    </Style>
                </StackPanel.Resources>
                <!--服务器端的 IP 和端口号-->
                <TextBlock x:Name="txtIP" Margin="4,14,0,14" Width="144"/>
                <TextBlock x:Name="txtPort" Text="5000" Margin="10, 0, 0, 0"/>
                <Button x:Name="btnBeginListening" Content="开始侦听" HorizontalAlignment="Right" Click="btnBeginListening_Click"/>
                <!--<Button x:Name="btnStop" Content="停止" HorizontalAlignment="Right" Click="btnStop_Click"/>-->
            </StackPanel>

            <!--消息窗口和消息发送窗口-->
            <Grid Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="8*"/>
                    <RowDefinition Height="2*"/>
                </Grid.RowDefinitions>
                <ScrollViewer x:Name="scroll">
                    <!--显示连接状态、聊天消息等-->
                    <TextBlock x:Name="txtResult"  TextWrapping="Wrap"/>
                </ScrollViewer>
                <!--聊天文字输入框-->
                <TextBox x:Name="txtInput" Grid.Row="1" KeyDown="txtInput_KeyDown" TextWrapping="Wrap"/>
            </Grid>

            <!--操作按钮-->
            <Grid Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="5*"/>
                    <ColumnDefinition Width="5*"/>
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal">
                    <!--用户选择的文件的路径-->
                    <TextBox x:Name="txtFilePath" Width="150" Height="40"/>
                    <Button Content="选择文件" x:Name="btnChooseFile" Click="btnChooseFile_Click"/>
                </StackPanel>

                <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Grid.Column="1">
                    <!--发送文件或者发送文字消息-->
                    <Button Content="发送文件" x:Name="btnSendFile" Click="btnSendFile_Click"/>
                    <Button Content="发送消息" x:Name="btnSendMsg" Click="btnSendMsg_Click"/>
                </StackPanel>
            </Grid>
        </Grid>

        <!--显示请求连接到服务器端的 WP 端的 IP 信息-->
        <ListBox Grid.Column="1" x:Name="listbox">
        </ListBox>
    </Grid>


二、定义一个服务器端的 SocketClient 类,用来封装与 WP 端通讯所使用的 Socket

      虽然 WP 和 PC 端都是使用 C# 语言开发的,但是 WP 端和 PC 端的 Socket 类在使用方法上有一些差异,

并且 WP 端明显是经过精简过的,这里就不贴出来了。直接贴出 SocketClient 类:

namespace DesktopSocketServerDemo
{
    /// <summary>
    /// 与客户端的 连接通信类(包含了一个 与客户端 通信的 套接字,和线程)
    /// </summary>
    public class SocketClient
    {
        Socket sokMsg;
        Thread threadMsg;

        // 通信操作完成时调用,用来触发在操作完成时,在宿主页面显示消息提示
        public event EventHandler<string> Completed;

        public SocketClient(Socket sokMsg)
        {
            this.sokMsg = sokMsg;

            this.threadMsg = new Thread(ReceiveMsg);
            this.threadMsg.IsBackground = true;
            this.threadMsg.Start();
        }

        bool isRec = true;
        // 负责监听客户端发送来的消息
        void ReceiveMsg()
        {
            while (isRec)
            {
                try
                {
                    byte[] byteSrc = new byte[CommonHelper.FileLength];

                    // 从绑定的 Socket 套接字接收数据,将数据存入接收缓冲区。
                    int length = sokMsg.Receive(byteSrc);

                    DataType dataType;
                    byte[] bytesFile;
                    string strMsg;

                    // 转换源 byte[] 数据内容,获取其中的 head 和 body 的实际内容
                    CommonHelper.ConvertByteToData(byteSrc, out dataType, out bytesFile, out strMsg);

                    if (dataType.IsFile == true)
                    {
                        // 如果 body 的类型文件,则直接将文件存储到 PC 端的 D: 盘根路径下
                        using (FileStream file = new FileStream("d:\\" + dataType.FileName + dataType.Exten, FileMode.OpenOrCreate))
                        {
                            file.Write(bytesFile, 0, bytesFile.Length);
                        }

                        // 显示回调消息
                        OnCompleted("客户端发送的文件:" + dataType.FileName + dataType.Exten);
                        OnCompleted("该文件保存在 D: 盘的根目录下");
                    }
                    else
                    {
                        OnCompleted(">>客户端:" + strMsg);
                    }

                }
                catch (Exception ex)
                {
                    // 如果抛异常,则释放该 Socket 对象所占用的资源
                    CloseConnection();

                    OnCompleted("SocketClient.ReceiveMsg() :" + ex.Message);
                }
            }
        }

        /// <summary>   
        ///  向客户端发送消息   
        /// </summary>
        /// <param name="strMsg"></param>
        public void Send(string strMsg)
        {
            byte[] arrMsgFinal = CommonHelper.ConvertDataToByte(new DataType { IsFile = false }, null, strMsg);

            sokMsg.Send(arrMsgFinal);
        }

        // 向客户端发送文件数据
        public void SendFile(string strPath)
        {
            try
            {
                byte[] arrFileFina = CommonHelper.ConvertDataToByte(new DataType { IsFile = true }, strPath, null);

                //发送文件数据
                sokMsg.Send(arrFileFina);//, 0, length + 1, SocketFlags.None);
                OnCompleted("发送文件完成");
            }
            catch (Exception ex)
            {
                OnCompleted(ex.Message);
            }
        }

        // 关闭与客户端连接
        public void CloseConnection()
        {
            isRec = false;
            sokMsg.Close();
            sokMsg.Dispose();
        }


        void OnCompleted(string strMsg)
        {
            if (Completed != null)
            {
                Completed(null, strMsg);
            }
        }

    }
}


三、在 MainWindow.xaml 文件中,定义侦听客户端连接的 Socket

      MainWindow.xaml 文件相应的 Codebehind 代码:

namespace DesktopSocketServerDemo
{
    /// <summary>
    /// PC 端服务器,用来接收连接 PC 的 Socket 连接,并创建 SocketClient 类与客户端通信
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //MultiCast cast = new MultiCast();
            //cast.Startup();

            txtIP.Text += "本地 IP:" + strIP;
            txtPort.Text = " 端口:" + Port;
        }

        int Port = 5000;
        // 服务器端的 IP 地址
        string strIP = CommonHelper.GetIPAddress();

        //负责监听 客户端 的连接请求
        Socket socketWatch = null;

        // 执行 socketWatch 对象监听请求的线程
        Thread threadWatch = null; 

        // 开启监听
        private void btnBeginListening_Click(object sender, RoutedEventArgs e)
        {
            if (socketWatch == null)
            {
                //实例化 套接字 (ip4寻址协议,流式传输,TCP协议)
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(strIP), Port);

                //将 监听套接字  绑定到 对应的IP和端口
                socketWatch.Bind(endpoint);

                //设置 监听队列 长度为10(同时能够处理 10个连接请求)
                socketWatch.Listen(10);
                threadWatch = new Thread(StartWatch);
                threadWatch.IsBackground = true;
                threadWatch.Start();
                ShowMsg("启动服务器成功......\r\n");
            }
            else
            {
                ShowMsg("服务器已经启动");
            }
        }

        // 循环侦听客户端的连接请求
        bool isWatch = true;

        // 负责与客户端通信的对象
        SocketClient socketClient;

        /// <summary>
        /// 被线程调用 监听连接端口
        /// </summary>
        void StartWatch()
        {
            while (isWatch)
            {
                try
                {
                    //监听 客户端 连接请求,但是,Accept会阻断当前线程
                    //监听到请求,立即创建负责与该客户端套接字通信的套接字
                    Socket socket = socketWatch.Accept();

                    if (socketClient != null)
                        socketClient.CloseConnection();

                    socketClient = new SocketClient(socket);
                    socketClient.Completed += connection_Completed;

                    this.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        if (socket != null && socket.RemoteEndPoint != null)
                            //将 通信套接字的 客户端IP端口保存在下拉框里
                            listbox.Items.Add(new TextBlock { Text = socket.RemoteEndPoint.ToString() });

                        ShowMsg("接收一个客户端连接......");
                    }), null);
                }
                catch (Exception ex)
                {
                    ShowMsg(ex.Message);
                    socketWatch.Close();
                    socketWatch.Dispose();
                    isWatch = false;
                }

            }
        }
                
        //发送消息到已经连接到 PC 的客户端
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtInput.Text))
            {
                ShowMsg("输入内容不能为空");
                return;
            }

            if (socketClient != null)
            {
                ShowMsg(txtInput.Text.Trim());

                // 发送文字内容
                socketClient.Send(txtInput.Text.Trim());

                txtInput.Text = "";
            }
            else
            {
                MessageBox.Show("还没有建立连接");
            }
        }

        //选择要发送的文件
        private void btnChooseFile_Click(object sender, EventArgs e)
        {
            Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
            if (openFileDialog.ShowDialog() == true)
            {
                txtFilePath.Text = openFileDialog.FileName;
            }
        }

        //发送文件
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtFilePath.Text))
            {
                MessageBox.Show("请先选择一个文件");
                return;
            }

            //connection = new SocketClient();

            if (socketClient != null)
            {
                // 发送文件内容
                socketClient.SendFile(txtFilePath.Text.Trim());
            }
            else
            {
                MessageBox.Show("还没有建立连接");
            }
        }

        // 操作完成
        void connection_Completed(object sender, string e)
        {
            ShowMsg(e);
        }
        
        // 向窗口追加消息
        void ShowMsg(string strMsg)
        {
            this.Dispatcher.BeginInvoke(new Action(delegate
               {
                   txtResult.Text += Environment.NewLine + strMsg + Environment.NewLine;
                   scroll.ScrollToBottom();
               }), null);
        }

        // 当服务器关闭时触发
        protected override void OnClosed(EventArgs e)
        {
            if (socketWatch != null)
            {
                socketWatch.Close();
                socketWatch.Dispose();
            }
            base.OnClosed(e);
        }

        // 按“回车键”发送消息
        private void txtInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                btnSendMsg_Click(null, null);
            }
        }

        // 释放连接资源
        //private void btnStop_Click(object sender, RoutedEventArgs e)
        //{
        //    if (socketClient != null)
        //    {
        //        socketClient.CloseConnection();
        //    }

        //    if (sokWatch != null)
        //    {
        //        sokWatch.Close();
        //        sokWatch.Dispose();
        //    }
        //}
    }
}

 

posted @ 2013-06-25 18:45  博琼  阅读(414)  评论(0编辑  收藏  举报