Windows Phone学习笔记(7) — — TCP套接字

  TCP套接字的主要步骤:创建连接、发送数据或接受数据、关闭连接,下面开始做一个TCP的示例。

首先是WP界面代码:

View Code
        <!--ContentPanel - 在此处放置其他内容-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,-8,12,8">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <TextBlock Grid.Row="0" Grid.Column="0" Text="Host Name:"  
                       VerticalAlignment="Center" HorizontalAlignment="Center" 
                       FontSize="{StaticResource PhoneFontSizeNormal}" />
            <TextBox x:Name="txtRemoteHost" Grid.Row="0" Grid.Column="1"  Height="70" Width="200" 
                     VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="{StaticResource PhoneFontSizeNormal}"  />

            <TextBlock Grid.Row="1" Grid.Column="0" Text="Text To Echo:" 
                       VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="{StaticResource PhoneFontSizeNormal}" />

            <TextBox x:Name="txtInput" Grid.Row="1" Grid.Column="1" Height="70" Width="200"  
                     VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="{StaticResource PhoneFontSizeNormal}" />

            <Button x:Name="btnEcho" Grid.Row="1" Grid.Column="2" Height="70"  Width="120" 
                    Content="Echo" FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnEcho_Click"/>

            <Button x:Name="btnGetQuote" Grid.Row="2" Grid.ColumnSpan="4" Height="70" 
                    Content="Get Quote of the Day" FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnGetQuote_Click"/>

            <TextBox x:Name="txtOutput" Grid.Row="3" Grid.ColumnSpan="4" Background="Black" BorderBrush="Green" 
                     AcceptsReturn="False" Foreground="LightGray" FontFamily="Courier New" IsHitTestVisible="False" FontSize="{StaticResource PhoneFontSizeSmall}" TextWrapping="Wrap" />
        </Grid>

之后创建一个SocketClient类用以实现套接字的请求流程,首先是要声明四个变量:_socket、_clientDone、TIMEOUT_MILLISECONDS、MAX_BUFFER_SIZE。_socket是一个套接字对象,_clientDone可以通过设置通信的接收状态来协调套接字的异步调用,TIMEOUT_MILLISECONDS规定最大的异步时间,MAX_BUFFER_SIZE是数据缓冲区的最大空间。代码如下:

View Code
        Socket _socket = null;

        static ManualResetEvent _clientDone = new ManualResetEvent(false);

        const int TIMEOUT_MILLISECONDS = 5000;

        const int MAX_BUFFER_SIZE = 2048;

之后就是创建服务器的连接:

View Code
        /// <summary>
        /// 尝试一个TCP套接字连接到指定的主机端口
        /// </summary>
        /// <param name="hostName">主机名称</param>
        /// <param name="portNumber">要连接的端口号</param>
        /// <returns>代表一个连接请求的返回结果字符串</returns>
        public string Connect(string hostName, int portNumber)
        {
            string result = string.Empty;
            //创建一个终结点DnsEndPoint对象。将主机名和端口号传递给它
            DnsEndPoint hostEntry = new DnsEndPoint(hostName, portNumber);

            //创建一个引用InterNetwork地址的基于流的TCP协议
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //此处是创建了一个套接字的异步操作,并且设定它远程的IP或者终结点
            SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
            socketEventArg.RemoteEndPoint = hostEntry;  

            //添加套接字的异步操作完成的事件
            socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
            {
                //检测请求的结果
                result = e.SocketError.ToString();

                //请求信号完成,接触用户UI线程
                _clientDone.Set();
            });


            //将事件状态市值为未收到信号状态,阻止UI进程
            _clientDone.Reset();

            //开始对远程主机的一个请求
            _socket.ConnectAsync(socketEventArg);

            //阻止UI进程的时间
            _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
            return result;
      }

向服务器发送数据:

View Code
        /// <summary>
        /// 为指定的数据发送到服务器建立连接
        /// </summary>
        /// <param name="data">发送到服务器的数据</param>
        /// <returns>发送请求的结果</returns>
        public string Send(string data)
        {
            string response = "Operation Timeout";

            //我们用_socket对象初始化的连接对象
            if (_socket != null)
            {
                //创建SocketAsyncEventArgs对象的上下文
                SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();

                //为上下文对象设置属性
                socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
                socketEventArg.UserToken = null;

                //设置异步套接字操作的完成的事件
                socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    response = e.SocketError.ToString();

                    _clientDone.Set();
                });

                //将数据发送到缓冲区
                byte[] payload = Encoding.UTF8.GetBytes(data);
                socketEventArg.SetBuffer(payload, 0, payload.Length);

                // 将事件状态市值为未收到信号状态,导致线程受阻
                _clientDone.Reset();

                //使用异步发送请求的套接字
                _socket.SendAsync(socketEventArg);

                _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
            }
            else
            {
                response = "Socket is not initialized";
            }

            return response;
        }

接收套接字的数据:

View Code
        /// <summary>
        /// 从套接字所建立连接的服务器接收数据
        /// </summary>
        /// <returns>从服务器接收到的数据</returns>
        public string Receive()
        {
            string response = "Operation Timeout";

            //接收一个套接字连接
            if (_socket != null)
            {
                //创建一个SocketAsyncEventArgs上下文
                SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
                socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;

                // 设置缓冲区接收数据
                socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);

                socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    if (e.SocketError == SocketError.Success)
                    {
                        //检测缓存区中的数据
                        response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                        response = response.Trim('\0');
                    }
                    else
                    {
                        response = e.SocketError.ToString();
                    }

                    _clientDone.Set();
                });

                _clientDone.Reset();

                _socket.ReceiveAsync(socketEventArg);

                _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
            }
            else
            {
                response = "Socket is not initialized";
            }

            return response;
        }

以上三个方法中的ConnectAsync、SendAsync、ReceiveAsync方法都是以委托的形式回调Completed事件。

在页面的.cs中,首先声明两个int类型的常量设置两个端口号,之后就是调用以上的三个方法实现TCP通信。

View Code
        const int ECHO_PORT = 80;  // 在Echo按钮中回调协议使用端口
        const int QOTD_PORT = 80; // Quote of the Day按钮使用端口

        // 构造函数
        public MainPage()
        {
            InitializeComponent();
        }

        private void btnEcho_Click(object sender, RoutedEventArgs e)
        {
            ClearLog();
            //验证主机名和数据文本框不为空
            if (ValidateRemoteHost() && ValidateInput())
            {
                //实例化一个SocketClient
                SocketClient client = new SocketClient();

                //尝试连接服务器的回调
                Log(String.Format("Connecting to server '{0}' over port {1} (echo) ...", txtRemoteHost.Text, ECHO_PORT), true);
                string result = client.Connect(txtRemoteHost.Text, ECHO_PORT);
                Log(result, false);

                //发送数据
                Log(String.Format("Sending '{0}' to server ...", txtInput.Text), true);
                result = client.Send(txtInput.Text);
                Log(result, false);

                //接收服务器数据
                Log("Requesting Receive ...", true);
                result = client.Receive();
                Log(result, false);

                //关闭连接
                client.Close();
            }
        }

        private void btnGetQuote_Click(object sender, RoutedEventArgs e)
        {
            ClearLog();

            if (ValidateRemoteHost())
            {
                SocketClient client = new SocketClient();

                Log(String.Format("Connecting to server '{0}' over port {1} (Quote of the Day) ...", txtRemoteHost.Text, QOTD_PORT), true);
                string result = client.Connect(txtRemoteHost.Text, QOTD_PORT);
                Log(result, false);

                Log("Requesting Receive ...", true);
                result = client.Receive();
                Log(result, false);

                // Close the socket conenction explicitly
                client.Close();
            }
        }

        #region UI Validation
        /// <summary>
        /// txtInput文本框非空验证
        /// </summary>
        /// <returns>True if the txtInput TextBox contains valid data, False otherwise</returns>
        private bool ValidateInput()
        {
            if (String.IsNullOrWhiteSpace(txtInput.Text))
            {
                MessageBox.Show("Please enter some text to echo");
                return false;
            }

            return true;
        }

        /// <summary>
        /// txtRemoteHost文本框非空验证
        /// </summary>
        /// <returns>True if the txtRemoteHost contains valid data, False otherwise</returns>
        private bool ValidateRemoteHost()
        {
            if (String.IsNullOrWhiteSpace(txtRemoteHost.Text))
            {
                MessageBox.Show("Please enter a host name");
                return false;
            }

            return true;
        }
        #endregion

        #region Logging
        /// <summary>
        /// 日志的文本到txtOutput文本框
        /// </summary>
        private void Log(string message, bool isOutgoing)
        {
            string direction = (isOutgoing) ? ">> " : "<< ";
            txtOutput.Text += Environment.NewLine + direction + message;
        }

        /// <summary>
        /// 清除txtOutput文本框
        /// </summary>
        private void ClearLog()
        {
            txtOutput.Text = String.Empty;
        }
        #endregion

这就是TCP的一个简单示例,此示例是请求自己机器上的一个端口。程序截图如下图

 参考:http://msdn.microsoft.com/zh-cn/library/hh202858(v=vs.92).aspx

      http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=VS.95).aspx

 

 

 

posted on 2012-11-17 22:48  松竹柏柳  阅读(418)  评论(0编辑  收藏  举报

导航