Page 1 of 1

Socket connection closes without all information received

Posted: August 20th, 2019, 9:48 pm
by mrpiano
Hello everybody,

I'm new to this forum but have been a fan for several years of MPD & GMPC on Linux. Recently took up the task to build a website with similar look & feel to GMPC. As a teacher I need a nice example of an Angular website and this is a nice 'pet project'.

All is going very well up until I used the command 'list Artist' to list all the artists. If I use the MPC command line tool like this:

Code: Select all

mpc list Artist
I get a lot of lines as expected. If I do a line count (

Code: Select all

wc -l
) I get e.g. 1500 artists. However, when using PHP and a socket (fsockopen) only 16384 are received at max. And this leads up to about 600-650 artists being listed. I detect an EOF (feof function used) and stop reading. Reopening the socket does not help.

I tried a lot and even installed the last MPD version (0.21) on my dev-machine from source (hurrah!). Changed the MPD config

Code: Select all

but to no avail. Checked if the new version was really started (from /usr/local/bin/mpd) and specified the right config file (/etc/mpd.conf).

Some info:

Code: Select all

Music Player Daemon 0.21.13 (0.21.13)
Copyright 2003-2007 Warren Dukes <>
Copyright 2008-2018 Max Kellermann <>
This is free software; see the source for copying conditions.  There is NO
lsb_release -a:

Code: Select all

Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.3 LTS
Release:	18.04
Codename:	bionic
Any help is greatly appreciated

Re: Socket connection closes without all information received

Posted: August 21st, 2019, 7:05 am
by max
MPD doesn't know if mpc or PHP connects to it, so this can hardly be a difference. I rather think your PHP code is buggy, but I can't tell without seeing it.

Re: Socket connection closes without all information received

Posted: August 21st, 2019, 5:51 pm
by mrpiano
Ok, have been testing some more. I switched from a higher level PHP function to low-level sockets. This is my code:

Code: Select all


  namespace MPD\Readers;

  const BUFFER_LENGTH = 8192 * 1024;

  class MPDConnectionReader

    public $status;
    public $errNo;
    public $errStr;
    public $nrOfBytesRead;
    public $version;

    public function __construct()
      $this->status = "";
      $this->errStr = "";
      $this->errNo = 0;
      $this->nrOfBytesRead = 0;

    public function sendCommand(String $command)
      return null;

    public function readResponse()
      return false;

  class MPDFileReader extends MPDConnectionReader
    private $foldername;

     * MPDFileReader constructor.
    public function __construct(String $foldername)
      $this->foldername = $foldername;

    public function sendCommand(String $command)
      return true;

  class MPDHTTPReader extends MPDConnectionReader
    private $_socket;
    private $_host;
    private $_port;

     * MPDHTTPReader constructor.
     * @param $_host
     * @param $_port
    public function __construct($host, $port)

      $this->_host = $host;
      $this->_port = $port;


    private function openSocket()
      $this->_socket = @socket_create(AF_INET, SOCK_STREAM, getprotobyname("tcp"));

      if ($this->_socket === FALSE) $this->handleSocketError("Could not connet");

      $status = @socket_connect($this->_socket, $this->_host, $this->_port);
      if ($status === FALSE) $this->handleSocketError("Could not connect socket");

      // after connect, MPD will send "MPD OK" + version number
      $this->version = socket_read($this->_socket, 2048, PHP_NORMAL_READ);


    private function handleSocketError($functionalDescription)
      $this->errNo = socket_last_error();
      $this->errStr = socket_strerror($this->errNo);
      throw (new \Exception($functionalDescription . "(" . $this->_host . ":" . $this->_port . ") ==> " . $this->errStr, $this->errNo));

    public function __destruct()
      if ($this->_socket !== false) socket_close($this->_socket);

    public function sendCommand(String $command)
      $buf = $command . "\n";
      $status = socket_write($this->_socket, $buf);

      if ($status === false) $this->handleSocketError("Could not send to socket");
      else $this->status = "ok";

    public function readResponse()
      $response = "";
      $end_of_stream = false;

      do {
        $buf = socket_read($this->_socket, BUFFER_LENGTH, PHP_BINARY_READ);

        if ($buf === false) $this->handleSocketError("Could not read from socket");
        elseif ($buf === "") $end_of_stream = true;
        else {
          $response .= $buf;
          $this->nrOfBytesRead += strlen($buf);
      } while (!$end_of_stream);

      return $response;

It is invoked like this:

Code: Select all

  $httpreader = new \MPD\Readers\MPDHTTPReader("localhost","6600");
  $api = new mpdInterface($httpreader);
and then

Code: Select all

      $this->connectionReader->sendCommand($cmd . "\n");
      $result = [];

      $response = $this->connectionReader->readResponse();
      $bytesRead = $this->connectionReader->nrOfBytesRead;
There is no error. After 16384 (16Kb?) exactly the data stops coming. If I keep on reading I get a socket error 104 (Connection reset by peer).

So what is wrong here?

Greetings Martin

Re: Socket connection closes without all information received

Posted: August 22nd, 2019, 7:14 am
by max
Your code is broken.

1. It sends the newline twice, which means MPD closes the connection due to a malformed command line. This is what may have caused the transfer to be closed.
2. It uses binary mode for a text connection.
3. It keeps on receiving forever until MPD closes the connection, and this will eventually block.
4. It doesn't put a limit on the allocated memory, possibly consuming gigabytes of RAM if a malicious server decides to send so much data.
5. It ignores the end of the response; it has no understanding of the MPD protocol.

Don't reimplement the MPD protocol, because this will waste time and lets you repeat mistakes other people already did - instead, check if a MPD client library is available for the language of your choice.

Re: Socket connection closes without all information received

Posted: August 22nd, 2019, 7:42 am
by mrpiano
@Max a great thanks for taking the time to inspect my code. The first problem seems to be the culprit. And the rest was there because I was so desperate to get it working. I removed the extra newline and it worked (sort of).

I totally agree with you not inventing something that can be obtained from the community. However, I was using this as an educational learning project for implementing e.g. Angular, REST API, design patterns etc. That's the main reason.

Regarding point nr 5: In this version it indeed does not understand. In my original version it kept on reading until the "OK" message was found. However, in fixing the problem I totally refactored my code, because I didn't understand the socket problem.

All in all: thanks again for helping me out!