因为一些原因,小编需要使用远程桌面软件,但小编实在穷,所以使用的是免费版的向日葵。就在前几天,免费版的向日葵莫名其妙崩了(各种重启都没用),虽然之后通过升级这种高级手段又重新可以用了,但是这在我幼小的心灵上留下了创伤,所以,我决定自己手写一个远程桌面WEB版。不过最后因为时间有限,我写了个demo出来,以后有时间再完善吧。这次,我把自己的思路放出来供大家参考。
截取当前屏幕,并通过websocket将其发到Web端展示,Web端编写鼠标、键盘事件,通过WebSocket发送到服务端处理。
服务端使用了WPF进行开发,websocket用了第三方库WebSocketSharpFork来实现。
接下来,上代码:
WebSocket处理数据
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketSharp;
using WebSocketSharp.Server;
namespace RemoteApp
{
public class Echo : WebSocketBehavior
{
private bool isClose = false;
protected override void OnClose(CloseEventArgs e)
{
isClose = true;
Trace.WriteLine("Closed...");
}
protected override void OnMessage(MessageEventArgs e)
{
Trace.WriteLine(e.Data);
HandleData(e.Data);
}
protected override void OnOpen()
{
Trace.WriteLine("Open...");
//Sessions.Broadcast("Open...");
Thread thread = new Thread(UpdateDesktop);
thread.Start();
}
private void UpdateDesktop()
{
while (true)
{
if (isClose) return;
Bitmap bitmap = GetBitmapFromScreen();
Sessions.Broadcast(BitmapToByte(bitmap));
}
}
private void HandleData(string data)
{
try
{
JObject item = (JObject)JsonConvert.DeserializeObject(data);
int type = Int32.Parse(item["type"].ToString());
byte keyCode;
int x, y;
switch (type)
{
// Key Down
case 0:
keyCode = byte.Parse(item["key"].ToString());
KeyBoard.keyDown(keyCode);
break;
// Key Up
case 1:
keyCode = byte.Parse(item["key"].ToString());
KeyBoard.keyUp(keyCode);
break;
// Mouse move
case 2:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
MouseFlag.SetCursorPos(x, y);
break;
// Mouse Left Click
case 3:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
MouseFlag.MouseLeftClickEvent(x, y, 0);
break;
// Mouse Right Click
case 4:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
MouseFlag.MouseRightClickEvent(x, y, 0);
break;
// Mouse Left Down
case 5:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
MouseFlag.MouseLeftDownEvent(x, y, 0);
break;
// Mouse Left Up
case 6:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
MouseFlag.MouseLeftUpEvent(x, y, 0);
break;
// Mouse Wheel
case 7:
x = Convert.ToInt32(Convert.ToDouble(item["x"].ToString()));
y = Convert.ToInt32(Convert.ToDouble(item["y"].ToString()));
var d = Convert.ToInt32(Convert.ToDouble(item["deltaY"].ToString())) * Convert.ToInt32(Convert.ToDouble(item["deltaFactor"].ToString()));
MouseFlag.MouseWheelEvent(x, y, d);
break;
}
}
catch(Exception e)
{
}
}
private Bitmap GetBitmapFromScreen()
{
System.Drawing.Rectangle rc = System.Windows.Forms.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[stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(buffer, 0, Convert.ToInt32(stream.Length));
return buffer;
}
}
}
开启WebSocket
private void OpenWebSocketServer()
{
server = new WebSocketServer(int.Parse(PortName.Text));
server.AddWebSocketService<Echo>("/Echo");
server.Start();
}
Web端随便用了HTML写了一下,大家参考一下就行
<html>
<head>
<title>Remotetitle>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js">script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery-mousewheel/3.1.9/jquery.mousewheel.min.js">script>
head>
<script>
var ws;
var isEnter = false;
var remoteWidth = 1920;
var remoteHeight = 1080;
var widthRadio = remoteWidth / 1000;
var heightRadio = remoteHeight / 600;
function OpenWS() {
initWS();
initEvent();
}
function initWS() {
ws = new WebSocket("ws://192.168.1.105:10086/Echo");
ws.onopen = function () {
console.log("Openened connection to websocket");
};
ws.onclose = function () {
console.log("Close connection to websocket");
// 断线重连
initWS();
}
ws.onmessage = function (e) {
// console.log(e.data)
var blob = new Blob([e.data], { type: "image/jpg" });
var url = URL.createObjectURL(blob);
var image = document.getElementById("desktop");
image.src = url;
}
}
function initEvent(){
//禁用文本选择
document.onselectstart = function(e){
e = window.event || e;
if(window.event){
try{e.keyCode = 0;}catch(e){}
e.returnValue = false;
}else{
e.preventDefault();
}
};
//禁用右键菜单
$(document).contextmenu(function(e){
e = window.event || e;
if(isEnter)
sendRightClick(e.offsetX * widthRadio, e.offsetY * heightRadio, 1);
if(window.event){
try{e.keyCode = 0;}catch(e){}
e.returnValue = false;
}else{
e.preventDefault();
}
});
$(document).keydown(function (e) {
e = window.event || e;
var keycode = e.keyCode || e.which
// console.log('Down' + e.keyCode);
if(isEnter)
sendKeyDown(keycode);
if(e.ctrlKey || e.altKey || e.shiftKey || (keycode > 111 && keycode <124)){
if(window.event){
try{e.keyCode = 0;}catch(e){}
e.returnValue = false;
}else{
e.preventDefault();
}
// window.event.returnValue = false;
}
});
$(document).keyup(function(e) {
var keycode = e.keyCode || e.which
// console.log('up' + e.keyCode);
if(isEnter)
sendKeyUp(keycode)
});
$(document).mousemove(function(e) {
if(isEnter)
sendMouseMove(e.offsetX * widthRadio, e.offsetY * heightRadio);
});
$(document).click(function(e) {
console.log('click');
if(isEnter)
sendLeftClick(e.offsetX * widthRadio, e.offsetY * heightRadio);
});
$(document).mousedown(function(e){
// console.log('mouse down');
if(isEnter)
sendMouseLeftDown(e.offsetX * widthRadio, e.offsetY * heightRadio);
});
$(document).mouseup(function(e) {
// console.log('mouse up');
if(isEnter)
sendMouseLeftUp(e.offsetX * widthRadio, e.offsetY * heightRadio);
});
$(window).mousewheel(function(e) {
// console.log(e);
sendMouseWheel(e.offsetX * widthRadio, e.offsetY * heightRadio, e.deltaY, e.deltaFactor);
});
}
function sendKeyDown(keyCode){
ws.send("{'type':0, 'key':" + keyCode + "}")
}
function sendKeyUp(keyCode){
ws.send("{'type':1, 'key':" + keyCode + "}")
}
function sendMouseMove(dx, dy){
ws.send("{'type':2, 'x':" + dx + ", 'y':" + dy +"}");
}
function sendLeftClick(dx, dy){
ws.send("{'type':3, 'x':" + dx + ", 'y':" + dy + "}");
}
function sendRightClick(dx, dy){
ws.send("{'type':4, 'x':" + dx + ", 'y':" + dy + "}");
}
function sendMouseLeftDown(dx, dy){
ws.send("{'type':5, 'x':" + dx + ", 'y':" + dy + "}");
}
function sendMouseLeftUp(dx, dy){
ws.send("{'type':6, 'x':" + dx + ", 'y':" + dy + "}");
}
function sendMouseWheel(dx, dy, deltaY, deltaFactor) {
ws.send("{'type':7, 'x':" + dx + ", 'y':" + dy + ", 'deltaY':" + deltaY + ", 'deltaFactor':" + deltaFactor + "}");
}
function imgMouseEnter(){
isEnter = true;
}
function imgMouseLeave(){
isEnter = false;
}
script>
<body>
<button onclick="OpenWS()">打开WebSocketbutton>
<img id="desktop" width="1000" height="600" draggable="false" onmouseenter="imgMouseEnter()" onmouseleave="imgMouseLeave()"/>
body>
html>
界面随便写写的,代码我放GitHub上了,有兴趣的自己去看
代码:远程桌面Web版