与众不同 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:Class="Demo.Communication.SocketClient.UdpDemo" 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" 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
[源码下载]