Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous Socket Client Buffer Size

I have to connect a remote server with asynchronous socket connection and retrieve data. I can connect but there is a problem.

Packages are sending by pieces. I have two options; I can set a buffer and get whole package in one piece or combine pieces when all transfer done. I think first option (buffer thing) is the right way.

I'm defining a buffer size but it is not working in first part. In other parts, it works but with this method I can not get whole package in one piece because first part limited with 5,24 Kb.

You can find my code below:

$loop = React\EventLoop\Factory::create();

        $dnsResolverFactory = new React\Dns\Resolver\Factory();
        $dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
        $connector = new React\SocketClient\Connector($loop, $dns);
        $connector->create( ENDPOINT_IP , ENDPOINT_PORT )->then(function (React\Stream\Stream $stream) use ($loop) {

            $command = '{C:"EL",bmId:43,inst:"my_instance",tok:"my_token"}';

            $command_length = strlen($command);
            $command_length = pack("N", $command_length);

            $stream->write($command_length);
            $stream->write($command);

            $stream->bufferSize = 999999;
            $stream->on('data', function ($data) {

            $package    =   substr($data, 0, 4);
            $unpack     =   unpack('N', $package); // I'm getting whole package size

            echo $data;



            });


        });

        $loop->run();

I tried to define a buffer size under $stream->on('data', function ($data) { line but as you guess it failed. I don't know how to handle it right way.

Thanks in advance.

like image 472
Deniz B. Avatar asked Apr 09 '16 05:04

Deniz B.


1 Answers

"I can set a buffer and get whole package in one piece or combine pieces when all transfer done. I think first option (buffer thing) is the right way."

First option is not the right way simply because it's not the way socket communication works.

If you are receiving, for example, 5 kB of data and you set your buffer to be large enough, let's say 10 kB, you cannot expect that in one call to $stream->on('data', function ($data) { ... you will receive all 5 kB.

You must do three things:

  • You need to know the exact size of the data you are receiving in one message block. Either you know that the message will always have fixed and known size, or the data block has a header from which the length of the message can be read. In your case you are reading the message size from the first 4 bytes of the received data.
  • In loop you need to read the data chunks which come from the server and concatenate them until you have enough bytes to read the size of the whole message. In your case this is 4 bytes. However weird it sounds, there is a possibility that you receive 1 and then 3 bytes in two chunks, in two calls to $stream->on('data', function ($data) { .... When the size of concatenated data is >=4 then you read the size of the message.
  • In the same loop, you need to continue reading the data chunks which come from the socket and concatenate them until you receive all of the message bytes. Of course, this means that you need to have a variable defined outside of the loop in which you will store received data. After you received the whole message then you need to exit the loop.

Good idea is that you set a timer for loop so you can wait for the whole message to be received for a limited amount of time. It could happen that connection between client and server gets broken during transmission and if you do not have a timeout logic your loop will wait forever for the whole message to be received.

like image 153
BJovke Avatar answered Sep 23 '22 13:09

BJovke