尝试这样一个思路,用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...