我需要一个类似于.NET 5.0中支持的System.Runtime.Remoting的替代方案



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

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

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

提前感谢您的帮助!

@Fildor,这并不是一个真正的答案,伙计们,只是发布了我最终所做的简单方法,而不是利用远程处理框架。我的远程处理实现总是在私人网络或公司VPN上运行,所以安全性不是问题。因此,我可以采取一种简单化的方法。

我最终实现的是一个简单的TCPSocket实现,它使用TcpClient类和TcpListener传递Json字符串来表示命令和定义为类的响应对象。此类包括服务器API方法,每个方法映射到定义的API。

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

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

RemoteClientSocket类包括方法SendCommand((,该方法在实例化RemoteClientCommand对象后由每个客户端API方法调用,此方法采用Remote ClientCommand参数。

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

然后,我创建了RemoteClientCommand类,该类被序列化为Json字符串,并从客户端发送,并由映射到服务器命令的RemoteListener对象反序列化。然后,我创建了一个RemoteClientResponse类,该类被序列化为Json字符串,并返回给客户端。每个类都包括一个Serialize((DeSerialize(。

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

RemoteListener执行命令后,它会实例化一个新的RemoteCommandResponse对象,并填充CommandReturnobject和command OutParamsobject数组,在RemoteCommandResponse上调用Serialize((,并通过TCP/IP连接发送Json字符串作为响应。

RemoteClientSocket接收到响应时,它会对Json响应字符串调用DeSerialize((,并在从客户端方法返回之前,对CommandReturn对象(如果有(和Command OutParams(如果有的话(进行解包。

以下是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;
}
}
}

相关内容

最新更新