我需要一个类似于 System.Runtime.Remoting 的替代方法,它在 .NET 5.0 中受支持

Posted

技术标签:

【中文标题】我需要一个类似于 System.Runtime.Remoting 的替代方法,它在 .NET 5.0 中受支持【英文标题】:I need an alternative similar to System.Runtime.Remoting that is supported in .NET 5.0 【发布时间】:2021-08-30 13:00:47 【问题描述】:

我有一个用 C# WinForms 编写的当前软件产品,我正在迁移到 WPF .NET 5.0。我几乎已经全部迁移了,但最后迁移的项目之一是对应用程序的远程支持。这适用于从 Intranet 中的客户端进行远程控制,而不是 Internet。

该产品的当前版本使用 .NET Remoting,但是我很遗憾地发现 .NET 5.0 不支持此功能。

我看到了一些关于 gRPC 的东西,但找不到任何东西来告诉我如何实现,或者是否有更类似于 .NET Remoting 的东西可以使用,以便只需要进行最少的更改。

提前感谢您的帮助!

【问题讨论】:

“我看到了一些关于 gRPC 的东西,但找不到任何东西可以告诉我如何实现” - 真的?怎么样...docs.microsoft.com/en-us/aspnet/core/grpc/…,docs.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-5.0,docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/…,docs.microsoft.com/en-us/aspnet/core/grpc/…,duckduckgo.com/…,... 这适用于 ASP.NET,而不是 .NET 5 的 WPF。 唯一的区别应该是它的托管方式。关键是,互联网上到处都是 gRPC 教程…… 我明白这一点。我只是找不到任何其他托管(如 TCP/IP)的示例。我已经进行了很多搜索,但我们都知道归根结底是输入神奇的查询来获得你想要的东西。我看到的一切都是关于 ASP.NET Core。 这对你来说也可能很有趣:github.com/cactuaroid/GrpcWpfSample 【参考方案1】:

@Fildor,这并不是一个真正的答案,本质上,只是发布了我最终为一种简单的方法所做的事情,而不是利用远程处理框架。我的远程实现始终在专用网络或公司 *** 上运行,因此安全性不是问题。因此,我可以采取简单的方法。

我最终实现的是一个简单的 TCP/IP Socket 实现,它使用 TcpClient 类和通过 JsonTcpListener /strong> 字符串来表示一个命令和定义为类的响应对象。此类包括服务器 API 方法,每个方法都映射到定义的 API。

我为客户端创建了一个继承自 TcpClient (RemoteClientSocket) 的类和另一个继承自 TcpListener (RemoteListener) 在服务器上。此类包括客户端 API 方法,每个方法都映射到定义的 API。

我定义了一个 enumCommandType,其中包括每个远程命令的类型。

RemoteClientSocket 类包括方法 SendCommand(),在实例化 RemoteClientCommand 对象后,每个客户端 API 方法都会调用该方法,这方法采用 RemoteClientCommand 参数。

RemoteListener 类包括在 CommandType 枚举中定义的每个远程命令的方法。

然后我创建了 RemoteClientCommand 类,该类被序列化为 Json 字符串并从客户端发送并由 RemoteListener 对象反序列化,该对象映射到服务器命令。然后我创建了一个 RemoteClientResponse 类,该类被序列化为一个 Json 字符串,该字符串返回给客户端。每个类都包含一个 Serialize()DeSerialize() 方法,用于对实际通过 TCP 发送的 Json 字符串进行序列化/反序列化/IP 连接。

最后,为 CommandArgs 对象数组中的每个命令添加方法到 RemoteClientSocket 类以发送到服务器,并将方法添加到 RemoteListener 类执行与 RemoteClientCommand 对象中包含的 CommandType 对应的服务器方法。

RemoteListener 执行命令后,实例化一个新的RemoteCommandResponse 对象并填充CommandReturn 对象和CommandOutParams em> 对象数组,在 RemoteCommandResponse 对象上调用 Serialize() 并通过 TCP/IP 连接发送该 Json 字符串作为响应。 p>

RemoteClientSocket收到响应后,在Json响应字符串上调用DeSerialize(),解包CommandReturn em> 对象(如果有)和 CommandOutParams(如果有),然后从客户端方法返回。

这里是 RemoteListener 的代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using RemoteClient;

namespace RemoteServer

    public class RemoteListener : TcpListener, IWinCalRemotingService
    
        private static readonly IPAddress _ipAddress = IPAddress.Parse("127.0.0.1"); // Local host

        // Keep track of the threads and create CancellationTokenSource for each
        private Dictionary<Thread, CancellationTokenSource> m_ThreadDictionary = new Dictionary<Thread, CancellationTokenSource>();

        public RemoteListener()
            : base(_ipAddress, Constants.DEFAULT_REMOTING_PORT)
        
            RegisterCommands();
        

        /// <summary>
        /// Start the server
        /// </summary>
        public void StartServer()
        
            Start();

            // Create a thread for the server to listen on
            Thread t = new Thread(new ThreadStart(StartListener));
            t.Start();
        

        /// <summary>
        /// Stop the server
        /// </summary>
        public void StopServer()
        
            foreach (KeyValuePair<Thread, CancellationTokenSource> pair in m_ThreadDictionary)
            
                // Cancel all of the client threads
                pair.Value.Cancel();
            

            Stop();
        

        public void StartListener()
        
            try
            
                while (true)
                
                    Debug.WriteLine("Waiting for a Remoting Connection...");
                    TcpClient client = AcceptTcpClient();
                    Debug.WriteLine($"Remoting Client Connected from IP Address:client.Client.RemoteEndPoint");
                    Thread t = new Thread(new ParameterizedThreadStart(HandleClient));

                    // Add a mapping
                    m_ThreadDictionary.Add(t, new CancellationTokenSource());
                    t.Start(client);
                
            
            catch (SocketException e)
            
                Debug.WriteLine("SocketException: 0", e);
            
        

        public void HandleClient(Object obj)
        
            TcpClient client = (TcpClient)obj;

            CancellationTokenSource cancelToken = m_ThreadDictionary[Thread.CurrentThread];
            var stream = client.GetStream();
            string imei = String.Empty;
            string remoteCommand = null;
            Byte[] bytes = new Byte[512];
            int i;
            try
            
                while (!cancelToken.IsCancellationRequested && (i = stream.Read(bytes, 0, bytes.Length)) != 0)
                
                    string hex = BitConverter.ToString(bytes);
                    remoteCommand = Encoding.ASCII.GetString(bytes, 0, i);
                    Debug.WriteLine("1: Received: 0", remoteCommand, Thread.CurrentThread.ManagedThreadId);


                    string response = ProcessCommand(remoteCommand);
                    Byte[] reply = System.Text.Encoding.ASCII.GetBytes(response);
                    stream.Write(reply, 0, reply.Length);
                    Debug.WriteLine("1: Sent: 0", response, Thread.CurrentThread.ManagedThreadId);
                

                m_ThreadDictionary[Thread.CurrentThread].Dispose();
            
            catch (Exception e)
            
                SystemEventsMgr.AddException("Remoting Server Client Thread Exception", e);
            
            finally
            
                // Remove this thread from the map
                m_ThreadDictionary.Remove(Thread.CurrentThread);
                client.Close();
            
        

        private string ProcessCommand(string jsonCommand)
        
            RemoteClientCommand cmd = RemoteClientCommand.DeSerialize(jsonCommand);
            RemoteCommandResponse response = null;

            switch (cmd.CommandType)
            
                case ClientCommand.GetAutoInfo:
                    GetAutoInfo(cmd);
                    break;
            

            return response.Serialize();
        

    

    private RemoteCommandResponse GetAutoInfo(RemoteClientCommand cmd)
    
        int autoID = cmd.CommandArgs[0];
        string manufacturer = "";
        string model = "";
        int year = 0;
        string retString = GetAutoInfo(autoID, out string manufacturer, out string model, out int year);
        object[] outParams = new object[]manufacturer, model, year;
        
        RemoteCommandResponse resp = new RemoteCommandResponse(CommandType.GetAutoInfo, retString, outParams);
    return resp;
    

    private string GetAutoInfo(int autoID, out string autoManufacturer, out string autoModel, out int autoYear)
    
        autoManufacturer = _autos[autoID].Manufacturer;
        autoModel = _autos[autoID].Model;
        autoYear = _autos[autoID].Year;

        return "Success";
    

这里是 RemoteClientSocket 的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace RemoteClient

    /// <summary>
    /// IMPORTANT: When adding new commands, do the following:
    /// 1. Add to the command types
    /// 2. Add to the RemoteListener ProcessCommands switch statement
    /// </summary>
    public enum ClientCommand
    
        GetAutoInfo,
    

    class RemoteClientSocket : TcpClient, IAutoInfoInterface
    
        /// <summary>
        /// Instantiate and connect the client
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="port"></param>
        public RemoteClientSocket(string server, int port)
            : base(server, port)
        
        

        /// <summary>
        /// Send a command of input type and receive a response as output type
        /// </summary>
        /// <param name="cmdObject">The Command Object</param>
        /// <param name="inputType">The Type for the Command Object</param>
        /// <param name="outputType">the Type for the return object</param>
        /// <returns></returns>
        public RemoteCommandResponse SendCommand(RemoteClientCommand cmd)
        
            RemoteCommandResponse cmdResp = null;
            NetworkStream stream = GetStream();

            Byte[] data = Encoding.ASCII.GetBytes(cmd.Serialize());
            stream.Write(data, 0, data.Length);

            data = new Byte[256];
            string response = string.Empty;
            Int32 bytes = stream.Read(data, 0, data.Length);
            response = System.Text.Encoding.ASCII.GetString(data, 0, bytes);

            // Deserialize the response
            cmdResp = RemoteCommandResponse.DeSerialize(response);

            stream.Close();

            return cmdResp;
        

        virtual public string GetAutoInfo(int autoID, out string autoManufacturer, out string autoModel, out int autoYear)
        
            string err = "None";
            RemoteClientCommand cmd = new RemoteClientCommand(CommandType.GetAutoInfo, new object[]  autoID );

            RemoteCommandResponse resp = SendCommand(cmd);

            // Unpackage the return type and output arguments
            err = resp.CommandReturn as string;
            autoManufacturer = resp.CommandOutParams[0] as string;
            autoModel = resp.CommandOutParams[1] as string;
            autoYear = (int)resp.CommandOutParams[2];

            return err;
        
    

    /// <summary>
    /// The WinCal Remoting Client Command Package Containing Json Strings for Objects
    /// </summary>
    public class RemoteClientCommand
    
        /// <summary>
        /// Command Type - one for each remote command
        /// </summary>
        public WinCalClientCommand CommandType  get; private set; 

        /// <summary>
        /// Command argument object(s)
        /// </summary>
        public object[] CommandArgs  get; private set; 

        public RemoteClientCommand(WinCalClientCommand cmdType, object[] cmdArgs)
        
            CommandType = cmdType;
            CommandArgs = cmdArgs;
        

        /// <summary>
        /// Serialize the class to Json string for the command
        /// </summary>
        /// <returns></returns>
        public string Serialize()
        
            return JsonSerializer.Serialize(this);
        

        /// <summary>
        /// Deserialize the command object
        /// </summary>
        /// <param name="jsonString"></param>
        /// <returns></returns>
        public static RemoteClientCommand DeSerialize(string jsonString)
        
            return JsonSerializer.Deserialize(jsonString, typeof(RemoteCommandResponse)) as RemoteClientCommand;
        
    

    /// <summary>
    /// The object serialized and returned by the RemoteListener
    /// </summary>
    public class RemoteCommandResponse
    
        /// <summary>
        /// The Command Type - one for each remote command
        /// </summary>
        public WinCalClientCommand CommandType  get; private set; 

        /// <summary>
        /// The single command return object
        /// </summary>
        public object CommandReturn  get; private set; 

        /// <summary>
        /// The Json strings for all out params - may be empty
        /// </summary>
        public object[] CommandOutParams  get; private set; 

        public RemoteCommandResponse(WinCalClientCommand cmdType, object retObj, object[] outParamObjs)
        
            CommandType = cmdType;
            CommandReturn = retObj;
            CommandOutParams = outParamObjs;
        

        /// <summary>
        /// Serialize the class to a Json string
        /// </summary>
        /// <returns></returns>
        public string Serialize()
        
            return JsonSerializer.Serialize(this);
        

        /// <summary>
        /// Deserialize the response object
        /// </summary>
        /// <param name="jsonString"></param>
        /// <returns></returns>
        public static RemoteCommandResponse DeSerialize(string jsonString)
        
            return JsonSerializer.Deserialize(jsonString,typeof(RemoteCommandResponse)) as RemoteCommandResponse;
        
    

【讨论】:

所以你的意思是你使用套接字来实现一些类似于.net远程处理的类(至少接口是相似的?) @LeiYang,是的,这正是我所做的。实施它并没有花费很长时间。

以上是关于我需要一个类似于 System.Runtime.Remoting 的替代方法,它在 .NET 5.0 中受支持的主要内容,如果未能解决你的问题,请参考以下文章

我需要一个类似于 System.Runtime.Remoting 的替代方法,它在 .NET 5.0 中受支持

多个类似于 Maps 的 UINavigationControllers

iPhone - 行为类似于 UIViewController 的 UIView

我需要一种类似于for循环重新评估测试条件的行为的算法

类似于 avahi 的开源库 [关闭]

关卡选择视图 - 类似于愤怒的小鸟