2012年9月17日星期一

与众不同 windows phone (31)

与众不同 windows phone (31)

[源码下载]


与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室



作者:webabcd


介绍
与众不同 windows phone 7.5 (sdk 7.1) 之通信

  • 实例 - 基于 Socket UDP 开发一个多人聊天室



示例
1、服务端
Main.cs

/* * Socket UDP 聊天室的服务端 *  * 注:udp 报文(Datagram)的最大长度为 65535(包括报文头) */using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Net.Sockets;using System.Net;using System.Threading;using System.IO;namespace SocketServerUdp{    public partial class Main : Form    {        SynchronizationContext _syncContext;        System.Timers.Timer _timer;        // 客户端终结点集合        private List<IPEndPoint> _clientList = new List<IPEndPoint>();        public Main()        {            InitializeComponent();            // UI 线程            _syncContext = SynchronizationContext.Current;            // 启动后台线程接收数据            Thread thread = new Thread(new ThreadStart(ReceiveData));            thread.IsBackground = true;            thread.Start();            // 每 10 秒运行一次计时器所指定的方法,群发信息            _timer = new System.Timers.Timer();            _timer.Interval = 10000d;            _timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);            _timer.Start();        }        // 接收数据        private void ReceiveData()        {            // 实例化一个 UdpClient,监听指定端口,用于接收信息            UdpClient listener = new UdpClient(3367);            // 客户端终结点            IPEndPoint clientEndPoint = null;            try            {                while (true)                {                    // 一直等待,直至接收到数据为止(可以获得接收到的数据和客户端终结点)                    byte[] bytes = listener.Receive(ref clientEndPoint);                    string strResult = Encoding.UTF8.GetString(bytes);                    OutputMessage(strResult);                    // 将发送此信息的客户端加入客户端终结点集合                    if (!_clientList.Any(p => p.Equals(clientEndPoint)))                        _clientList.Add(clientEndPoint);                }            }            catch (Exception ex)            {                OutputMessage(ex.ToString());            }        }        private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)        {            // 每 10 秒群发一次信息            SendData(string.Format("webabcd 对所有人说:大家好! 【信息来自服务端 {0}】", DateTime.Now.ToString("hh:mm:ss")));        }        // 发送数据        private void SendData(string data)        {            // 向每一个曾经向服务端发送过信息的客户端发送信息            foreach (IPEndPoint ep in _clientList)            {                // 实例化一个 UdpClient,用于发送信息                UdpClient udpClient = new UdpClient();                try                {                    byte[] byteData = UTF8Encoding.UTF8.GetBytes(data);                    // 发送信息到指定的客户端终结点,并返回发送的字节数                    int count = udpClient.Send(byteData, byteData.Length, ep);                }                catch (Exception ex)                {                    OutputMessage(ex.ToString());                }                // 关闭 UdpClient                // udpClient.Close();            }        }        // 在 UI 上输出指定信息        private void OutputMessage(string data)        {            _syncContext.Post((p) => { txtMsg.Text += p.ToString() + "\r\n"; }, data);        }    }}


2、客户端
UdpPacketEventArgs.cs

/* * 演示 ASM 和 SSM 用 */using System;using System.Net;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Ink;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;namespace Demo.Communication.SocketClient{    public class UdpPacketEventArgs : EventArgs    {        // UDP 包的内容        public string Message { get; set; }        // UDP 包的来源的 IPEndPoint         public IPEndPoint Source { get; set; }        public UdpPacketEventArgs(byte[] data, IPEndPoint source)        {            this.Message = System.Text.Encoding.UTF8.GetString(data, 0, data.Length);            this.Source = source;        }    }}

UdpDemo.xaml

<phone:PhoneApplicationPage     x:    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    ="http://schemas.microsoft.com/winfx/2006/xaml"    ="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"    ="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"    ="http://schemas.microsoft.com/expression/blend/2008"    ="http://schemas.open    FontFamily="{StaticResource PhoneFontFamilyNormal}"    FontSize="{StaticResource PhoneFontSizeNormal}"    Foreground="{StaticResource PhoneForegroundBrush}"    SupportedOrientations="Portrait" Orientation="Portrait"    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"    shell:SystemTray.IsVisible="True">    <Grid x:Name="LayoutRoot" Background="Transparent">        <StackPanel HorizontalAlignment="Left">            <ScrollViewer x:Name="svChat" Height="400">                <TextBlock x:Name="txtChat" TextWrapping="Wrap" />            </ScrollViewer>            <TextBox x:Name="txtName" />            <TextBox x:Name="txtInput" KeyDown="txtInput_KeyDown" />            <Button x:Name="btnSend" Content="发送" Click="btnSend_Click" />        </StackPanel>    </Grid></phone:PhoneApplicationPage>

UdpDemo.xaml.cs

/* * Socket UDP 聊天室的客户端 */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.Text;using System.Threading;namespace Demo.Communication.SocketClient{    public partial class UdpDemo : PhoneApplicationPage    {        // 客户端 Socket        private Socket _socket;        // 用于发送数据的 Socket 异步操作对象        private SocketAsyncEventArgs _socketAsyncSend;        // 用于接收数据的 Socket 异步操作对象        private SocketAsyncEventArgs _socketAsyncReceive;        // 是否已发送过数据        private bool _sent = false;        private ManualResetEvent _signalSend = new ManualResetEvent(false);        public UdpDemo()        {            InitializeComponent();            this.Loaded += new RoutedEventHandler(UdpDemo_Loaded);        }        void UdpDemo_Loaded(object sender, RoutedEventArgs e)        {            // 初始化姓名和需要发送的默认文字            txtName.Text = "匿名用户" + new Random().Next(0, 9999).ToString().PadLeft(4, '0');            txtInput.Text = "hi";            // 实例化 Socket            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);            // 实例化 SocketAsyncEventArgs ,用于发送数据            _socketAsyncSend = new SocketAsyncEventArgs();            // 服务端的 EndPoint            _socketAsyncSend.RemoteEndPoint = new DnsEndPoint("192.168.8.217", 3367);            // 异步操作完成后执行的事件            _socketAsyncSend.Completed += new EventHandler<SocketAsyncEventArgs>(_socketAsyncSend_Completed);        }        public void SendData()        {            byte[] payload = Encoding.UTF8.GetBytes(txtName.Text + ":" + txtInput.Text);            // 设置需要发送的数据的缓冲区            _socketAsyncSend.SetBuffer(payload, 0, payload.Length);            _signalSend.Reset(); // 无信号            // 异步地向服务端发送信息(SendToAsync - UDP;SendAsync - TCP)            _socket.SendToAsync(_socketAsyncSend);                       _signalSend.WaitOne(3000); // 阻塞        }        void _socketAsyncSend_Completed(object sender, SocketAsyncEventArgs e)        {            if (e.SocketError != SocketError.Success)            {                OutputMessage(e.SocketError.ToString());            }            _signalSend.Set(); // 有信号            if (!_sent)            {                _sent = true;                // 注:只有发送过数据,才能接收数据,否则 ReceiveFromAsync() 时会出现异常                ReceiveData();            }        }        // 接收信息        public void ReceiveData()        {            // 实例化 SocketAsyncEventArgs,并指定端口,以接收数据            _socketAsyncReceive = new SocketAsyncEventArgs();            _socketAsyncReceive.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 3367);            // 设置接收数据的缓冲区,udp 报文(Datagram)的最大长度为 65535(包括报文头)            _socketAsyncReceive.SetBuffer(new Byte[65535], 0, 65535);            _socketAsyncReceive.Completed += new EventHandler<SocketAsyncEventArgs>(_socketAsyncReceive_Completed);            // 异步地接收数据(ReceiveFromAsync - UDP;ReceiveAsync - TCP)            _socket.ReceiveFromAsync(_socketAsyncReceive);        }        void _socketAsyncReceive_Completed(object sender, SocketAsyncEventArgs e)        {            if (e.SocketError == SocketError.Success)            {                // 接收数据成功,将接收到的数据转换成字符串,并去掉两头的空字节                var response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);                response = response.Trim('\0');                OutputMessage(response);            }            else            {                OutputMessage(e.SocketError.ToString());            }            // 继续异步地接收数据            _socket.ReceiveFromAsync(e);        }        private void OutputMessage(string data)        {            // 在聊天文本框中输出指定的信息,并将滚动条滚到底部            this.Dispatcher.BeginInvoke(                delegate                {                    txtChat.Text += data + "\r\n";                    svChat.ScrollToVerticalOffset(txtChat.ActualHeight - svChat.Height);                }            );        }        private void btnSend_Click(object sender, RoutedEventArgs e)        {            SendData();        }        private void txtInput_KeyDown(object sender, KeyEventArgs e)        {            if (e.Key == Key.Enter)            {                SendData();                this.Focus();            }        }    }}



OK
[源码下载]


TAG: