尝试这样一个思路,用UDP组播的方式,由一个发送端在组内发布时间计数,所有接收端以此计数作为相对时间,来定位当前应该显示哪张图片;
发送端代码如下(此段代码为站在巨人的肩膀上,引用自小白的Blog):
发送端
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace CommandLine {
class Program {
static void Main( string [] args) {
IPAddress ip = IPAddress.Parse( " 224.1.2.3 " );
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 1 );
IPEndPoint ipep = new IPEndPoint(ip, 5000 );
Console.WriteLine( " Begin Connect... " );
int interval = 20 ;
int sum = 86400000 / 20 ;
int num = 0 ;
timer = new System.Timers.Timer(interval);
timer.Elapsed += (o1, e1) => {
string str = num.ToString();
byte [] buff = Encoding.ASCII.GetBytes(str);
s.SendTo(buff, buff.Length, SocketFlags.None, ipep);
Console.WriteLine( string .Format( " Send=>{0} " , str));
num ++ ;
if (num >= sum) num = 0 ;
};
timer.Start();
Console.WriteLine( " == End == " );
Console.ReadKey();
timer.Stop();
s.Close();
}
private static System.Timers.Timer timer;
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace CommandLine {
class Program {
static void Main( string [] args) {
IPAddress ip = IPAddress.Parse( " 224.1.2.3 " );
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 1 );
IPEndPoint ipep = new IPEndPoint(ip, 5000 );
Console.WriteLine( " Begin Connect... " );
int interval = 20 ;
int sum = 86400000 / 20 ;
int num = 0 ;
timer = new System.Timers.Timer(interval);
timer.Elapsed += (o1, e1) => {
string str = num.ToString();
byte [] buff = Encoding.ASCII.GetBytes(str);
s.SendTo(buff, buff.Length, SocketFlags.None, ipep);
Console.WriteLine( string .Format( " Send=>{0} " , str));
num ++ ;
if (num >= sum) num = 0 ;
};
timer.Start();
Console.WriteLine( " == End == " );
Console.ReadKey();
timer.Stop();
s.Close();
}
private static System.Timers.Timer timer;
}
}
接收端代码如下:
接收端
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace WpfApplication {
public class TimeManager {
public static TimeManager Instance { get { return instance; } set { instance = value; } }
public int Count { get { return count; } set { count = value; } }
private TimeManager() { }
public void Start() {
s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000 );
s.Bind(ipep);
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse( " 224.1.2.3 " ), IPAddress.Any));
new Thread( new ThreadStart(() => {
while ( ! needExit) {
try {
byte [] b = new byte [ 4 ];
s.Receive(b, 4 , SocketFlags.None);
string str = Encoding.ASCII.GetString(b, 0 , b.Length);
count = Convert.ToInt32(str);
} catch { }
}
})).Start();
}
public void Stop() {
needExit = true ;
s.Close();
}
private static TimeManager instance = new TimeManager();
private int count;
private Socket s;
private bool needExit = false ;
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace WpfApplication {
public class TimeManager {
public static TimeManager Instance { get { return instance; } set { instance = value; } }
public int Count { get { return count; } set { count = value; } }
private TimeManager() { }
public void Start() {
s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000 );
s.Bind(ipep);
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse( " 224.1.2.3 " ), IPAddress.Any));
new Thread( new ThreadStart(() => {
while ( ! needExit) {
try {
byte [] b = new byte [ 4 ];
s.Receive(b, 4 , SocketFlags.None);
string str = Encoding.ASCII.GetString(b, 0 , b.Length);
count = Convert.ToInt32(str);
} catch { }
}
})).Start();
}
public void Stop() {
needExit = true ;
s.Close();
}
private static TimeManager instance = new TimeManager();
private int count;
private Socket s;
private bool needExit = false ;
}
}
然后在前端,将这个时间计数作为标准来定位和显示图片,代码如下:
显示端
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
namespace WpfApplication {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded( object sender, RoutedEventArgs e) {
FullScreen();
TimeManager.Instance.Start();
foreach ( string each in Directory.GetFiles( " ./ " )) {
if (System.IO.Path.GetExtension(each).ToLower() == " .jpg " ) {
files.Add(System.IO.Path.GetFullPath(each));
}
}
new Thread( new ThreadStart(ShowImage)).Start();
}
private void ShowImage() {
if (files.Count > 0 ) {
while ( ! needExit) {
try {
int now = TimeManager.Instance.Count;
int count = files.Count;
int offset = now % (count * 100 );
int index = offset / 100 ;
string filename = files[index];
this .Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) => {
image1.Source = new BitmapImage( new Uri(filename));
}), null , null );
// int interval = 500 - offset % 500;
Thread.Sleep( 20 );
} catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
}
}
private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e) {
needExit = true ;
TimeManager.Instance.Stop();
}
private void Window_KeyDown( object sender, KeyEventArgs e) {
if (e.Key == Key.F5) {
if (fullscreen) {
PartScreen();
} else {
FullScreen();
}
}
}
private void Window_SizeChanged( object sender, SizeChangedEventArgs e) {
//
}
private void FullScreen() {
this .WindowStyle = WindowStyle.None;
this .WindowState = WindowState.Maximized;
this .WindowStartupLocation = WindowStartupLocation.CenterScreen;
this .Topmost = true ;
ShowCursor( 0 );
fullscreen = true ;
}
private void PartScreen() {
// this.WindowState = lastBorder;
// this.Location = lastLocation;
// this.RenderSize = lastSize;
// this.Topmost = false;
// ShowCursor(1);
// fullscreen = false;
}
[DllImport( " user32.dll " , EntryPoint = " ShowCursor " , CharSet = CharSet.Auto)]
public extern static void ShowCursor( int status);
private List < string > files = new List < string > ();
private bool needExit = false ;
private bool fullscreen;
private Point lastLocation;
private Size lastSize;
private WindowState lastBorder;
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
namespace WpfApplication {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded( object sender, RoutedEventArgs e) {
FullScreen();
TimeManager.Instance.Start();
foreach ( string each in Directory.GetFiles( " ./ " )) {
if (System.IO.Path.GetExtension(each).ToLower() == " .jpg " ) {
files.Add(System.IO.Path.GetFullPath(each));
}
}
new Thread( new ThreadStart(ShowImage)).Start();
}
private void ShowImage() {
if (files.Count > 0 ) {
while ( ! needExit) {
try {
int now = TimeManager.Instance.Count;
int count = files.Count;
int offset = now % (count * 100 );
int index = offset / 100 ;
string filename = files[index];
this .Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) => {
image1.Source = new BitmapImage( new Uri(filename));
}), null , null );
// int interval = 500 - offset % 500;
Thread.Sleep( 20 );
} catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
}
}
private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e) {
needExit = true ;
TimeManager.Instance.Stop();
}
private void Window_KeyDown( object sender, KeyEventArgs e) {
if (e.Key == Key.F5) {
if (fullscreen) {
PartScreen();
} else {
FullScreen();
}
}
}
private void Window_SizeChanged( object sender, SizeChangedEventArgs e) {
//
}
private void FullScreen() {
this .WindowStyle = WindowStyle.None;
this .WindowState = WindowState.Maximized;
this .WindowStartupLocation = WindowStartupLocation.CenterScreen;
this .Topmost = true ;
ShowCursor( 0 );
fullscreen = true ;
}
private void PartScreen() {
// this.WindowState = lastBorder;
// this.Location = lastLocation;
// this.RenderSize = lastSize;
// this.Topmost = false;
// ShowCursor(1);
// fullscreen = false;
}
[DllImport( " user32.dll " , EntryPoint = " ShowCursor " , CharSet = CharSet.Auto)]
public extern static void ShowCursor( int status);
private List < string > files = new List < string > ();
private bool needExit = false ;
private bool fullscreen;
private Point lastLocation;
private Size lastSize;
private WindowState lastBorder;
}
}
然而在两个设备上测试后发现实际的效果却不尽如人意,会出现同步->A超前->同步->B超前的现象,如此摇摆;复看了一遍小白的那篇文章,发现有这样一句说明:“UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的。也就是数据能不能到达接受端和数据到达的顺序都是不能保证的。”这是否说明完全依靠UDP组播的精确同步也是做不到的呢?
To be continued...