與眾不同 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
[源碼下載]