由於種種原因刷了兩個晚上寫了這個東西。必須吐槽一下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的話延遲太高導致客戶端各種無響應。考慮過室友提出的緩衝池。但只是能解決無響應的問題。畫面還是會卡頓。求不特別依賴網速的其他實現方法。
適應屏幕是手動調大小的。。心好累。
沒寫註釋。感覺看官老爺們看代碼會很憂桑。。。