@Fildor,这并不是一个真正的答案,本质上,只是发布了我最终为一种简单的方法所做的事情,而不是利用远程处理框架。我的远程实现始终在专用网络或公司 VPN 上运行,因此安全性不是问题。因此,我可以采取简单的方法。
我最终实现的是一个简单的 TCP/IP Socket 实现,它使用 TcpClient 类和通过 JsonTcpListener /strong> 字符串来表示一个命令和定义为类的响应对象。此类包括服务器 API 方法,每个方法都映射到定义的 API。
我为客户端创建了一个继承自 TcpClient (RemoteClientSocket) 的类和另一个继承自 TcpListener (RemoteListener) 在服务器上。此类包括客户端 API 方法,每个方法都映射到定义的 API。
我定义了一个 enum,CommandType,其中包括每个远程命令的类型。
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;
}
}
}