【问题标题】:How do I write a counterpart program that can be called remotely to perform computations on a remote machine如何编写可以远程调用以在远程机器上执行计算的对应程序
【发布时间】:2017-08-20 17:22:37
【问题描述】:

我编写了一个程序来生成文件校验和。在本地机器上一切正常,但由于网络速度(显然),当我分支到远程机器时,速度大大减慢并变成 ****。

如何编写一个可以远程调用的对应程序来计算远程机器上的校验和并以哈希值响应?

理想情况下,我会喜欢完整的远程部署,但我可以访问远程计算机进行安装和设置。

目标是使用 Mono 的 Windows 和 Ubuntu/FreeBSD。

编辑

我没有像我想象的那样解释自己。我有一个托管大量大文件的服务器,而不是通过网络将这些文件流式传输到我的个人计算机以计算校验和(就像我一直在寻找的那样)我正在寻找一种在服务器上运行守护程序的方法我可以要求计算它在本地托管的文件的校验和并给我哈希。

【问题讨论】:

  • 您是否考虑过使用 TcpClient 和 TcpListener 发送数据或进行远程调用?
  • 我还没有尝试过任何东西,但我会看看 TcpClient/Listener。我最困惑的是如何启动远程程序并将信息传递给它。它必须作为服务/在后台运行吗?
  • 它不必在后台,但这是个好主意。我使用 backgroundWorkers 建立“客户端”和“服务器”之间的连接。我可以留下一个答案来解释如何做到这一点,听起来不错吗?如果是,流程是这样的:“发送校验和 -> 对方计算 -> 接收校验和”?
  • 是的,请!该过程类似于:要求对方计算文件 X 的校验和 -> 对方计算校验和 -> 接收校验和
  • 现在我想起来了。为什么不在主程序的后台进程中计算校验和? backgroundWorker 不会停止应用程序,您可以让 backgroundWorker 自动运行一些代码,当它完成它的进程并通过“process-done-code”返回校验和。这样会更容易,否则你必须先将文件发送到另一台计算机,然后再使用对应程序计算校验和,然后将校验和发送回主程序

标签: c# mono network-programming remote-access


【解决方案1】:

现在对答案进行了彻底的改革。尽管我仍在尽我所能地发表评论,但我已尽量将其保持为“基本”/“一般”,而不会造成松散的结局。


主程序(普通法)

public async Task<byte[]> ConnectToCounterpart(string dataToSend)
{
    // === Sending empty strings breaks the program ===
    if (String.IsNullOrEmpty(dataToSend))
        return null;


    // === Variables n' Stuff ===
    string counterPartIP = "127.0.0.1"; // The ip of the computer running counterpart program. (127.0.0.1 is the localhost/current computer).
    int counterPartPort = 50_000; // The port which the counterpart program listens to


    // === Getting a connection ===
    TcpClient client = new TcpClient(counterPartIP, counterPartPort); // Connect to the counterpart program

    NetworkStream nwStream = client.GetStream(); // Get the networkStream between the main and counterpart program
    nwStream.ReadTimeout = 5_000; // Give the counterpart program 5 seconds to do its thing


    // === Convert the "dataToSend" to a byte-array ===
    byte[] bytesToSend = Encoding.UTF7.GetBytes(dataToSend); // An array with the bytes of the string (UTF7 lets you keep "cpecial characters like Æ, Ø, Å, Á etc.")


    // === Send the file-bytes ===
    await nwStream.WriteAsync(bytesToSend, 0, bytesToSend.Length); // Send the file-bytes to the counterpart program


    // ==========================================
    // At this point counterpart is doing work
    // ==========================================


    byte[] returnBytes = new byte[client.ReceiveBufferSize]; // The byte-array this method returns
    try
    {
        // === Recieve the reply ===
        int numberOfBytesRecieved = await nwStream.ReadAsync(returnBytes, 0, client.ReceiveBufferSize); // Receve the checksum from the counterpart and tthe ammount of sent bytes


        // === Crop return-array to the recieved bytes ===
        Array.Resize(ref returnBytes, numberOfBytesRecieved);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString(), "Error while retrieving"); // Show error

        returnBytes = null; // Set returnbytes to null
    }


    // === Close and dispose stream ===
    nwStream.Close();
    nwStream.Dispose();


    // === Return the byte-array ===
    return returnBytes;
}

在这段代码中,您可能已经注意到“async”、“Task&lt;byte[]&gt;”和“await”。 “Task&lt;byte[]&gt;”与“byte[]”(有点)相同,因为“async”修饰符需要它,所以使用它。 (关于异步编程:https://msdn.microsoft.com/da-dk/library/hh156513(v=vs.110).aspx

在任何情况下返回的类型仍然是“byte[]”。 “Async”和“Await”将在一个“位”(Byte, bit... Puns)中进一步解释。

对应代码(BackgroundWorker)

private void BackgroundWorkerCalculate_Counterpart_DoWork(object sender, DoWorkEventArgs e)
{
    // === Variables n' Stuff ===
    int listeningPort = 50_000; // The port which the counterpart program listens to


    // === Start listening ===
    TcpListener listener = new TcpListener(IPAddress.Any, listeningPort); // Listen to the port
    listener.Start();


    // Infinite loop for infinite computations
    while (true)
    {
        // === Getting a connection ===
        TcpClient connection = listener.AcceptTcpClient();
        NetworkStream nwStream = connection.GetStream();


        // === Get the recieved bytes ===
        byte[] recievedBytes = new byte[connection.ReceiveBufferSize]; // Create a buffer for the incomming file-bytes
        int numberOfBytesRecieved = nwStream.Read(recievedBytes, 0, connection.ReceiveBufferSize); // Read the bytes to the buffer


        // === Convert recieved bytes to a string ===
        string recievedString = Encoding.UTF7.GetString(recievedBytes, 0, numberOfBytesRecieved);



        // === Understanding the recieved data ===
        /* Lets say recievedString == "[path]" */

        byte[] replyBytes = new byte[] { 1 }; // Bytes to reply to the main program

        try
        {
            using (MD5 md5 = MD5.Create()) // Create the hashing protocol
            {
                using (FileStream fileStream = new FileStream(recievedString, FileMode.Open)) // Open the file to hash
                {
                    replyBytes = md5.ComputeHash(fileStream); // replyBytes now contains the hash
                }
            }
        }
        catch
        { } // If file doesn't exist, then replyBytes stays as { 1 }, which will be returned if things goes wrong


        // === Send the reply to main program ===
        nwStream.Write(replyBytes, 0, replyBytes.Length);
    }
}

此代码仍然执行相同的操作:接收一些数据并返回一些数据。您现在可以根据需要修改“了解收到的数据”部分。例如检查接收到的路径是文件还是目录,从而区分“GetChecksum”(指文件)和“GetSubelements”(指目录)。

主程序(用法)

private async void ButtonCalculate_Counterpart_Click(object sender, EventArgs e)
{
    // This is how you use the method
    byte[] replyBytes = await ConnectToCounterpart(@"folder\file.txt");

    // "async" makes the given method "asyncronous", so that it runs parallell to other tings 
    // "await" will prevent a time-consumeing method-call from "halting" or "blocking" the main process.


    // Check if things went wrong
    if (replyBytes == new byte { 1 }) // As mentioned ealier in the backgroundWorker code
    {
        MessageBox.Show("Something went wrong with the hashing. Is the path correct?", "Could not hash")
        return;
    }


    // If you want the reply as a normal string
    string replyString = Encoding.UTF7.GetString(replyBytes, 0, replyBytes.Length);

    // If you want the reply as a checksum
    string checkSum = BitConverter.ToString(replyBytes);
 }

这里的cmets应该解释一下。


注意:您可以轻松使用除 UTF7 之外的其他加密方式,只需在整个代码中保持一致即可。

注意 2:如果发送的数据大于 50 kB,则存在并非所有数据都被发送的风险,因为我对此进行了编程。如果这成为一个问题,我可以做一些改变。这些变化更加复杂。

注意 3:如果您想从单独的网络(通过互联网而不是本地)访问对方,那么您需要在对方所连接的路由器上进行一些端口转发。

注意 4:请记住,如果您将“Folder\File.txt”这样的路径传递给对方,那么对方将查看目录“C:\Users\...\...\FolderWithProgramExe\Folder\File.txt”。如果您改为将“C:\Folder\File.txt”(注意开头为“C:\”)传递给对方,那么对方将准确查看计算机上的位置“C:\Folder\File.txt”。

我希望这对你现在有用。如前所述,我已尝试将代码保持为“可塑性”/“易于修改”,以便您可以根据需要对其进行调整。

【讨论】:

  • 不完全是,但 TCP 代码非常适合我(以前从未做过网络编程)。我没有像我想象的那样解释自己。我的想法是我有一个服务器,它托管大量大文件,而不是通过网络将这些文件流式传输到我的计算机来计算校验和(就像我一直在寻找的那样)我正在寻找一种在我可以要求计算它在本地托管的文件的校验和并给我哈希的服务器。 C# 哈希以 byte[] 的形式返回,以获取一个十六进制字符串,您可以调用 BitConverter.ToString(byte[]) 或自行循环
  • @Michael Oooooohhhhh。我现在明白了。您想通过文件名、文件 id 还是通过可以从对方流式传输的列表来识别文件?对方是跟踪可散列文件还是只是将大量文件放在一个文件夹中?
  • 现在我在想我会通过一个已知共享的路径,远程将被设置为取消引用。但如果有更好的方法,我愿意接受。
  • @Michael 如果对方有一个名为“数据库”的文件夹怎么办。然后这个数据库文件夹有一个名为“file.txt”的文件。要从主程序中引用“file.txt”,您只需键入“file.txt”。如果“file.txt”然后被放在一个子文件夹中,比如说“Database\subfolder”,那么你会从主程序中引用“file.txt”:“subfolder\file.txt”。听起来不错?
  • 啊,是的,我明白你的意思了!我建议的方式是否可行,或者我是否正在试图这样做?
猜你喜欢
  • 2015-06-04
  • 2010-11-26
  • 1970-01-01
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
  • 2011-11-23
  • 2016-02-24
  • 1970-01-01
相关资源
最近更新 更多