Question

I have a .NET C# console application acting as a client and a PHP script acting as a server. Both connect via localhost so there is no internet speed dependency. The problem I have is that when my client app sends a binary file, say only 100KB, it takes around over 30 seconds to complete. I expected this process to be just about instantaneous.

Here's some code:

.NET client app:

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 server script:

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");
        }
    }
}

Calls:

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

Solution

Having looked at your PHP code, I suspect this is actually the cause of the speed problem:

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

Assuming that PHP works the same way as Java and .NET (i.e. assuming that .= creates a new string containing a copy of the previous data), you're creating a new string for every single byte... that's an O(n^2) operation, which is going to get nasty really quickly.

Additionally:

  • Your protocol is broken if you have "DONE" in the original data
  • Your protocol specifically deals with text, not binary data. It's possible that you're base64-encoding the binary data first (given the PHP) but that's inefficient too. If you've really got binary data, it's a good idea to transfer it as binary data. Sockets are entirely capable of doing that - there's no reason to get text involved at all unless your problem domain specifically requires text.

I suggest you write the length of the data to the socket first, as a 4 or 8 byte integer (depending on whether or not you think you'll ever need to transfer more than 4GB). Then you can read the length first in your PHP code, allocate an appropriately-sized buffer, and then keep reading from the socket into that buffer (reading a large chunk at a time) until you're done. That will be much more efficient and won't have the protocol issues mentioned above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top