与众不同 windows phone (33) - Communication(通信)之源特定组播 SSM(Source Specific Multicast)
作者:webabcd
介绍
与众不同 windows phone 7.5 (sdk 7.1) 之通信
- 实现“源特定多播” - SSM(Source Specific Multicast)
示例
1、服务端
Main.cs
/* * 此服务会定时向指定的多播组发送消息,用于演示 SSM */ 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; using System.Net.Sockets; namespace SocketServerSSM { public partial class Main : Form { System.Threading.SynchronizationContext _syncContext; public Main() { InitializeComponent(); LaunchSocketUdp(); } private void LaunchSocketUdp() { _syncContext = System.Threading.SynchronizationContext.Current; // 定义 Source Specific Multicast 中的 Source,即 SSM 客户端仅接收此 Source 发送到多播组的数据 IPEndPoint sourcePoint = new IPEndPoint(IPAddress.Any, 3370); // 定义多播组 IPEndPoint multicastPoint = new IPEndPoint(IPAddress.Parse("224.0.1.2"), 3369); UdpClient sourceUdp = new UdpClient(sourcePoint); ShowMessage("用于演示 SSM 的 Socket 服务已启动,每 3 秒向多播组发送一次信息"); // 每 3 秒向多播组发送一次信息 var timer = new System.Timers.Timer(); timer.Interval = 3000d; timer.Elapsed += delegate { string msg = string.Format("{0} - {1}", Dns.GetHostName(), DateTime.Now.ToString("HH:mm:ss")); byte[] data = Encoding.UTF8.GetBytes(msg); sourceUdp.Send(data, data.Length, multicastPoint); }; timer.Start(); } public void ShowMessage(string msg) { txtMsg.Text += msg + "\r\n"; } } }
2、客户端
实现 SSM 信道
UdpSingleSourceMulticastChannel.cs
/* * 实现一个 SSM 信道(即 SSM 帮助类),供外部调用 * * * 通过 UdpSingleSourceMulticastClient 实现 SSM(Source Specific Multicast),即“源特定多播” * 多播组基于 IGMP(Internet Group Management Protocol),即“Internet组管理协议” * * UdpSingleSourceMulticastClient - 一个从单一源接收多播信息的客户端,即 SSM 客户端 * BeginJoinGroup(), EndJoinGroup() - 加入“源”的异步方法 * BeginReceiveFromSource(), EndReceiveFromSource() - 从“源”接收信息的异步方法 * BeginSendToSource(), EndSendToSource() - 发送信息到“源”的异步方法 * ReceiveBufferSize - 接收信息的缓冲区大小 * SendBufferSize - 发送信息的缓冲区大小 */ 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; using System.Net.Sockets; using System.Text; namespace Demo.Communication.SocketClient { public class UdpSingleSourceMulticastChannel : IDisposable { // SSM 客户端 private UdpSingleSourceMulticastClient _client; // “源”的地址 private IPAddress _sourceAddress; // 接收信息的缓冲区 private byte[] _buffer; // 此客户端是否加入了多播组 private bool _isJoined; /// <summary> /// 构造函数 /// </summary> /// <param name="sourceAddress">SSM 的“源”的地址</param> /// <param name="groupAddress">多播组的地址</param> /// <param name="port">多播组的端口</param> /// <param name="maxMessageSize">接收信息的缓冲区大小</param> public UdpSingleSourceMulticastChannel(IPAddress sourceAddress, IPAddress groupAddress, int port, int maxMessageSize) { _sourceAddress = sourceAddress; _buffer = new byte[maxMessageSize]; // 实例化 SSM 客户端,需要指定的参数为:“源”的地址;多播组的地址;多播组的端口 _client = new UdpSingleSourceMulticastClient(sourceAddress, groupAddress, port); } // 收到多播信息后触发的事件 public event EventHandler<UdpPacketEventArgs> Received; private void OnReceived(IPEndPoint source, byte[] data) { var handler = Received; if (handler != null) handler(this, new UdpPacketEventArgs(data, source)); } // 加入多播组后触发的事件 public event EventHandler Opening; private void OnOpening() { var handler = Opening; if (handler != null) handler(this, EventArgs.Empty); } // 断开多播组后触发的事件 public event EventHandler Closing; private void OnClosing() { var handler = Closing; if (handler != null) handler(this, EventArgs.Empty); } /// <summary> /// 加入多播组 /// </summary> public void Open() { if (!_isJoined) { _client.BeginJoinGroup( result => { _client.EndJoinGroup(result); _isJoined = true; Deployment.Current.Dispatcher.BeginInvoke( () => { OnOpening(); Receive(); }); }, null); } } /// <summary> /// 发送信息到“源” /// </summary> public void Send(string msg) { if (_isJoined) { byte[] data = Encoding.UTF8.GetBytes(msg); // 需要指定“源”的端口 int sourcePort = 100; _client.BeginSendToSource(data, 0, data.Length, sourcePort, result => { _client.EndSendToSource(result); }, null); } } /// <summary> /// 接收从多播组发过来的信息,即“源”发送给多播组的信息 /// </summary> private void Receive() { if (_isJoined) { Array.Clear(_buffer, 0, _buffer.Length); _client.BeginReceiveFromSource(_buffer, 0, _buffer.Length, result => { int sourcePort; // 接收到多播信息后,可获取到“源”的端口 _client.EndReceiveFromSource(result, out sourcePort); Deployment.Current.Dispatcher.BeginInvoke( () => { OnReceived(new IPEndPoint(_sourceAddress, sourcePort), _buffer); Receive(); }); }, null); } } // 关闭 SSM 信道 public void Close() { _isJoined = false; OnClosing(); Dispose(); } public void Dispose() { if (_client != null) _client.Dispose(); } } }
演示 SSM
SourceSpecificMulticast.xaml
<phone:PhoneApplicationPage x:Class="Demo.Communication.SocketClient.SourceSpecificMulticast" 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"> <ListBox Name="lstAllMsg" MaxHeight="400" /> </StackPanel> </Grid> </phone:PhoneApplicationPage>
SourceSpecificMulticast.xaml.cs
/* * 用于演示 SSM */ 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.Windows.Navigation; namespace Demo.Communication.SocketClient { public partial class SourceSpecificMulticast : PhoneApplicationPage { // 实例化自定义的 SSM 信道 private UdpSingleSourceMulticastChannel _channel; public SourceSpecificMulticast() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { // 多播组地址是必须介于 224.0.0.0 到 239.255.255.255 之间的 IP 地址,其中范围介于 224.0.0.0 到 224.0.0.255 之间的多播地址是保留多播地址 // 比如:224.0.0.0 是基址,224.0.0.1 是代表同一个物理网络中所有系统的多播组地址,而 224.0.0.2 代表同一个物理网络中的所有路由器 _channel = new UdpSingleSourceMulticastChannel(IPAddress.Parse("192.168.8.217"), IPAddress.Parse("224.0.1.2"), 3369, 2048); _channel.Opening += new EventHandler(_channel_Opening); _channel.Received += new EventHandler<UdpPacketEventArgs>(_channel_Received); _channel.Closing += new EventHandler(_channel_Closing); _channel.Open(); // 需要的使用,应该调用 Close() // _channel.Close(); } void _channel_Opening(object sender, EventArgs e) { lstAllMsg.Items.Insert(0, "已经连上多播组,等待来自多播组的消息"); } void _channel_Received(object sender, UdpPacketEventArgs e) { // 因为已经指定了接收信息的缓冲区大小是 2048 ,所以如果信息不够 2048 个字节的的话,空白处均为空字节“\0” string message = string.Format("{0} - 来自:{1}", e.Message.TrimEnd('\0'), e.Source.ToString()); lstAllMsg.Items.Insert(0, message); } void _channel_Closing(object sender, EventArgs e) { lstAllMsg.Items.Insert(0, "已经断开多播组"); } } }
OK
[源码下载]