自己动手实现网页版的远程桌面

自己动手实现网页版的远程桌面

  • 背景
  • 实现原理
  • 服务端
  • 客户端
  • 展示图片
  • 最后

背景

因为一些原因,小编需要使用远程桌面软件,但小编实在穷,所以使用的是免费版的向日葵。就在前几天,免费版的向日葵莫名其妙崩了(各种重启都没用),虽然之后通过升级这种高级手段又重新可以用了,但是这在我幼小的心灵上留下了创伤,所以,我决定自己手写一个远程桌面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>

展示图片

自己动手实现网页版的远程桌面_第1张图片

服务端界面

自己动手实现网页版的远程桌面_第2张图片

Web端界面

最后

界面随便写写的,代码我放GitHub上了,有兴趣的自己去看
代码:远程桌面Web版

你可能感兴趣的:(.NET,JS,websocket,c#,网络协议)