• 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("启动服务器成功......
    ");
                }
                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();
            //    }
            //}
        }
    }
  • 相关阅读:
    HDU--1212大数取模
    欧拉函数
    自制体重转换器
    常用快捷键
    Markdown基本语法
    python全栈开发day50-jquery之ajax、XmlHttpRquest
    python全栈开发day49-jquery的位置信息、事件流、事件对象,事件委托,事件绑定和解绑
    阳历转阴历算法
    python全栈开发day48-jqurey自定义动画,jQuery属性操作,jQuery的文档操作,jQuery中的ajax
    python全栈开发day47-jqurey
  • 原文地址:https://www.cnblogs.com/hebeiDGL/p/3155255.html
Copyright © 2020-2023  润新知