Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

socket_recv not receiving full data

I'm sending from browser through Websocket an image data of around 5000 bytes but this line is receiving total of 1394 bytes only:

while ($bytes = socket_recv($socket, $r_data, 4000, MSG_DONTWAIT)) {
    $data .= $r_data;
}

This is after handshake is done which is correctly being received. The json data is being cutoff after 1394 bytes. What could be the reason?

In the browser interface it is sending image as JSON:

websocket.send(JSON.stringify(request));

The browser interface is fine as it is working with other PHP websocket free programs I've tested.

Here is the full source code.

like image 351
user5858 Avatar asked Jul 22 '15 04:07

user5858


People also ask

What does recv() do in socket programming?

If data is not available for the socket socket, and socket is in blocking mode, the recv () call blocks the caller until data arrives. If data is not available and socket is in nonblocking mode, recv () returns a -1 and sets the error code to EWOULDBLOCK.

Why is my recv() call not working?

The recv () call was interrupted by a signal that was caught before any data was available. The request is invalid or not supported. The MSG_OOB flag is set and no out-of-band data is available. There has been a network or transport failure. Insufficient system resources are available to complete the call.

Why does the socket hang when I send no data?

The server correctly identifies invalid port numbers, too few or too many port numbers, or other malformed requests. If you send the server no data ("") and keep the socket open, however, it will hang at data=fd.recv (1024).strip () on the recv instruction. Setting a timeout on the socket via settimeout.

Why is my So_rcvtimeo returning a small amount of data?

Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, the connection is terminated, an error is pending, or SO_RCVTIMEO is set and the timer is expired for the socket.


2 Answers

You have our socket set up as non-blocking by specifying MSG_DONTWAIT, so it will return EAGAIN after it reads the first chunk of data, rather than waiting for more data. Remove the MSG_DONTWAIT flag and use MSG_WAITALL instead, so that it waits for all the data to be received.

There are a few ways of knowing if you have received all the data you are expecting:

  1. Send the length of the data. This is useful if you want to send multiple blocks of variable length content. For example if I want to send three strings, I might first send a "3" to tell the receiver how many string to expect, then for each one I would send the length of the string, followed by the string data.
  2. Use fixed length messages. If you are expecting multiple messages but each one is the same size, then you can just read from the socket until you have at least that many bytes and then process the message. Note that you may receive more than one message (including partial messages) in a single recv() call.
  3. Close the connection. If you are sending only one message, then you can half-close the connection. This works because TCP connections maintain separate states for sending and receiving, so the server and close the sending connection yet leave the receiving one open for the client's reply. In this case, the server sends all its data to the client and then calls socket_shutdown(1)

1 and 2 are useful if you want to process the data while receiving it - for example if you are writing a game, chat application, or something else where the socket stays open and multiple messages are passed back and forth. #3 is the easiest one, and is useful when you just want to receive all the data in one go, for example a file download.

like image 155
MrZebra Avatar answered Sep 19 '22 16:09

MrZebra


Just my 2 cents on this. socket_recv can return false on an error. Where it can also receive zero (0) bytes in non-blocking IO.

Your check in your loop should be:

while(($bytes = socket_recv($resource, $r_data, 4000, MSG_DONTWAIT)) !== false) {}

Altough I would check the socket for errors also and add some usleep call to prevent "CPU burn".

$data = '';
$done = false;
while(!$done) {
    socket_clear_error($resource);
    $bytes = @socket_recv($resource, $r_data, 4000, MSG_DONTWAIT);

    $lastError = socket_last_error($resource);

    if ($lastError != 11 && $lastError > 0) {
        // something went wrong! do something
        $done = true;
    }
    else if ($bytes === false) {
        // something went wrong also! do something else
        $done = true;
    }
    else if (intval($bytes) > 0) {
        $data .= $r_data;
    }
    else {
        usleep(2000); // prevent "CPU burn"
    }
}
like image 35
Ronald Swets Avatar answered Sep 19 '22 16:09

Ronald Swets