由于种种原因刷了两个晚上写了这个东西。必须吐槽一下WPF和WinForm的库不一样好麻烦。
BitmapImage、BitmapSource和Bitmap这三个东西弄的我好烦躁。
第一次用异步Socket。写的时候还不是很懂好在Socket部分一次成不用Debug。写完这篇再慢慢回味一下代码。
之前写Shuide的时候服务器总是在连接第二个客户端的时候死掉。当时调了好久没调出来现在来看换成异步应该就阔以了。。。写完这篇就去重写Shuide的后台了。
服务器端的代码。。感觉这次的代码没什么核心技术。就干脆全贴上来了。。。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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.Drawing; using System.Windows.Forms; using System.Windows.Interop; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace ScreenServer { public partial class MainWindow : Window { private Socket serverSocket; private IPEndPoint serverIEP; private BitmapImage imgBuffer; private delegate void SetImageCallBack(BitmapImage bitImage); SetImageCallBack setImageCallBack; private delegate void SetTextblockStateCallBack(String Str); SetTextblockStateCallBack setTextblockStateCallBack; private int Time; public MainWindow() { InitializeComponent(); setImageCallBack = new SetImageCallBack(SetImage); setTextblockStateCallBack = new SetTextblockStateCallBack(SetTextblockState); Time = 50; } private void buttonStart_Click(object sender, RoutedEventArgs e) { textblockState.Dispatcher.Invoke(setTextblockStateCallBack, "开始监听........."); buttonStart.IsEnabled = false; int port = 51888; if (textboxTime.Text != "" && textboxTime.Text != null) { Time = int.Parse(textboxTime.Text); } try { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverIEP = new IPEndPoint(IPAddress.Any, port); serverSocket.Bind(serverIEP); serverSocket.Listen(20); Thread threadAccept = new Thread(new ThreadStart(Accept)); threadAccept.IsBackground = true; threadAccept.Start(); } catch { } } private void Accept() { while(true) { Socket client = serverSocket.Accept(); textblockState.Dispatcher.Invoke(setTextblockStateCallBack, "用户 " + ((IPEndPoint)client.RemoteEndPoint).Address.ToString() + " 已登录!"); Thread threadSend = new Thread(new ParameterizedThreadStart(Send)); threadSend.IsBackground = true; threadSend.Start(client); } } private void Send(Object Obj) { try { Socket client = (Socket)Obj; Bitmap bitmap = GetBitmapFromScreen(); Byte[] byteBuffer = new Byte[1048567]; byteBuffer = BitmapToByte(bitmap); if (client.Connected) { client.BeginSend(byteBuffer, 0, byteBuffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), client); } } catch { } } private void SendCallBack(IAsyncResult AR) { try { Thread.Sleep(Time); Socket client = (Socket)AR.AsyncState; Bitmap bitmap = GetBitmapFromScreen(); Byte[] byteBuffer = new Byte[1048567]; byteBuffer = BitmapToByte(bitmap); if (client.Connected) { client.BeginSend(byteBuffer, 0, byteBuffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), client); } } catch { textblockState.Dispatcher.Invoke(setTextblockStateCallBack, "1名用户退出登录!"); } } private Bitmap GetBitmapFromScreen() { System.Drawing.Rectangle rc = SystemInformation.VirtualScreen; var bitmap = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); using (Graphics g = Graphics.FromImage(bitmap)) { g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, System.Drawing.CopyPixelOperation.SourceCopy); } return bitmap; } private Byte[] BitmapToByte(Bitmap bitmap) { MemoryStream stream = new MemoryStream(); bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg); Byte[] buffer = new Byte[1048567]; stream.Seek(0, SeekOrigin.Begin); stream.Read(buffer, 0, Convert.ToInt32(stream.Length)); return buffer; } private void SetTextblockState(String Str) { textblockState.Text = Str; } private void buttonRefresh_Click(object sender, RoutedEventArgs e) { Time = int.Parse(textboxTime.Text); } } }
客户端的代码。。。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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.Windows.Forms; using System.Windows.Interop; using System.Net; using System.Net.Sockets; using System.IO; using System.Drawing; using System.Threading; namespace ScreenClient { public partial class MainWindow : Window { private IPAddress remoteIPA; private IPEndPoint remoteIEP; private int port; private Socket server; private Byte[] byteBuffer; private BitmapImage imgBuffer; private delegate void SetImageCallBack(BitmapImage bitmapImage); SetImageCallBack setImageCallBack; public MainWindow() { InitializeComponent(); setImageCallBack = new SetImageCallBack(SetImage); } private void Go_Click(object sender, RoutedEventArgs e) { remoteIPA = IPAddress.Parse(textboxIP.Text); port = 51888; remoteIEP = new IPEndPoint(remoteIPA, port); byteBuffer = new Byte[1048576]; imgBuffer = new BitmapImage(); server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { server.Connect(remoteIEP); if (server.Connected) { Thread threadReceive = new Thread(new ThreadStart(Receive)); threadReceive.IsBackground = true; threadReceive.Start(); } } catch { } } private void Receive() { while (true) { try { server.Receive(byteBuffer); try { imgBuffer = new BitmapImage(); imgBuffer.BeginInit(); imgBuffer.StreamSource = new MemoryStream(byteBuffer); imgBuffer.EndInit(); imgBuffer.Freeze(); } catch { imgBuffer = null; } } catch { System.Windows.MessageBox.Show("与服务器断开连接!"); this.Close(); } imageScreen.Dispatcher.BeginInvoke(setImageCallBack, imgBuffer); } } private void SetImage(BitmapImage bitmapImage) { imageScreen.Source = bitmapImage; } } }
大概实现方法就是截图存成Bitmap。然后转成Byte。传到Client。再转回BitmapImage。然后贴到客户端的Image控件上。。。
不得不吐槽。BitmapImage和Bitmap完全就是一样的东西还非要在不同的库里面起不同的名字。好烦躁。。。
目前的问题:
网速硬伤。使用Wifi的话延迟太高导致客户端各种无响应。考虑过室友提出的缓冲池。但只是能解决无响应的问题。画面还是会卡顿。求不特别依赖网速的其他实现方法。
适应屏幕是手动调大小的。。心好累。
没写注释。感觉看官老爷们看代码会很忧桑。。。