【问题标题】:.NET C# to PHP via local TCP connection slow.NET C# 到 PHP 通过本地 TCP 连接慢
【发布时间】:2014-03-25 03:21:46
【问题描述】:

我有一个充当客户端的 .NET C# 控制台应用程序和一个充当服务器的 PHP 脚本。两者都通过localhost 连接,因此不存在互联网速度依赖性。我遇到的问题是,当我的客户端应用程序发送一个二进制文件时,比如说只有 100KB,大约需要 30 多秒才能完成。我预计这个过程几乎是瞬间完成的。

这里有一些代码:

.NET 客户端应用程序:

public bool SendToServer(string data)
{
    data += "DONE"; // Terminator string
    TcpClient tcp = new TcpClient(this.serverURL, this.serverPort);
    NetworkStream stream = tcp.GetStream();
    byte[] b = Encoding.ASCII.GetBytes(data);
    int len = b.Length;
    for (int i = 0; i < len; i++)
        stream.Write(b, i, 1);
    stream.Flush();

    Console.WriteLine("Sent to server {0}", Convert.ToString(b) + " len: " + b.Length);
    return true;
}

PHP 服务器脚本:

class ImageService
{
    public $ip, $port, $soc;

    function Listen($ip, $port)
    {
        $this->ip = $ip;
        $this->port = $port;

        $this->soc = stream_socket_server("tcp://".$this->ip.":".$this->port, $errno, $errstr);
        if(!$this->soc) Out($errno);
        else
        {
            Out("created socket");
            $buf = "";
            while($con = stream_socket_accept($this->soc))
            {
                Out("accepted socket");

                do{
                    $buf .= fread($con, 1);
                    if(strlen($buf) == 0) break 2;
                }
                while(substr($buf, -4) != "DONE");
                $buf = substr($buf, 0, strlen($buf) - 4);

                switch($buf)
                {
                    case "quit":
                        break 2;
                }

                print '<img alt="image" src="data:image/jpeg;base64,'.$buf.'">';
                $buf = "";
            }
            fclose($con);
            fclose($this->soc);
            Out("Closed connection");
        }
    }
}

来电:

set_time_limit(0);
//error_reporting(E_ALL);
$is = new ImageService();
$is->Listen("127.0.0.1", 1234);

【问题讨论】:

  • 您应该一次写入整个数组,而不是一次写入一个字节。
  • 你一次写一个字节,这没有帮助。为什么不直接使用stream.Write(b, 0, b.Length);?如果您的字符串曾经包含“DONE”,并且无法处理非 ASCII 字符,那么您的协议也会失败,但这不是速度问题...
  • 更重要的是,二进制文件不是ASCII。除非您确实有文本,否则不要使用字符串。
  • @JonSkeet 我可能会发送可能只有几 MB 的图像文件,如果按照你说的做没有问题,那么我肯定会更改代码。另外,如果我一次只做一个字节,我可以根据总长度来衡量进度。 SLaks 我认为 ASCII 会使二进制文件安全传输,不是这样吗?
  • @Lee:100,000 个单字节调用肯定不如单个 100K 字节调用好,尤其是当您已经拥有内存中的所有数据时。在 ASCII 方面,不,当然不是这样。如果您尝试传输二进制数据,则根本不应该让您的方法接受string 参数。见msmvps.com/blogs/jon_skeet/archive/2013/06/21/…

标签: c# php .net sockets tcpclient


【解决方案1】:

看了你的PHP代码,我怀疑这实际上是速度问题的原因:

do{
    $buf .= fread($con, 1);
    if(strlen($buf) == 0) break 2;
}
while(substr($buf, -4) != "DONE");

假设 PHP 的工作方式与 Java 和 .NET 相同(即假设 .= 创建一个包含先前数据副本的新字符串),您正在为每个字节创建一个新字符串...一个 O(n^2) 的操作,很快就会变得很糟糕。

另外:

  • 如果您在原始数据中包含“DONE”,则您的协议已损坏
  • 您的协议专门处理文本,而不是二进制数据。您可能首先对二进制数据进行 base64 编码(给定 PHP),但这也是低效的。如果您确实有二进制数据,最好将其传输为 二进制数据。套接字完全能够做到这一点 - 没有理由让文本参与根本,除非您的问题域特别需要文本。

我建议您首先将数据长度写入套接字,以 4 或 8 字节整数的形式(取决于您是否认为您需要传输超过 4GB 的数据)。然后,您可以先在 PHP 代码中读取长度,分配适当大小的缓冲区,然后继续从套接字读取该缓冲区(一次读取大块),直到完成。这样会更有效率并且不会有上面提到的协议问题。

【讨论】:

  • 非常感谢乔恩的帮助。我知道终端序列是非常基本的,但是它目前用作临时解决方案,直到我让它按预期工作。您对 base64 编码的假设也是正确的,很好的提示。
  • @Lee:如果您使用的是 base64,那么您不会按照您的描述发送二进制文件 - 而且您还夸大了传输数据的大小.除非您使用本质上基于文本的传输,否则我不会使用 base64。 在 PHP 端对数据进行 base64 编码,以便您可以将其包含在响应中;那是另一回事。无论如何,希望关于重复字符串连接的解释已经成功了。
  • 我现在遇到的一个小问题是,当它到达fread() 中的某个点时,将二进制文件发送到我的 PHP 脚本会导致 PHP 错误 A non well formed numeric value encountered
  • @Lee:在我们真正提供帮助之前,您需要更多诊断信息。我建议你问一个新问题,因为此时它超出了“为什么这么慢”的范围。
猜你喜欢
  • 2021-09-30
  • 2012-12-14
  • 2012-04-26
  • 2018-11-18
  • 2016-05-02
  • 2020-06-13
  • 2016-03-24
  • 2018-02-05
  • 2017-11-23
相关资源
最近更新 更多