Hope this wasn't solved here before, but I was really trying to look for answer!
I Have a problem with receiving frames via socket_recv in PHP server. Problem appears when I send more than 1 messages from client in one time (e.g. in cycle). By the stream length it's match frames count but I'm not able to unmask frames correctly and my unmask function return only first frame. I'm using part of php server I found here and for one message it's working properly, but when I receive more messages at time, it don't unmask all.
I tryed to add loop to go thru all frames, but I'm not able to find end of frame and continue to other.
Here is main while of server:
while (true) {
//manage multipal connections
$changed = $clients;
//returns the socket resources in $changed array
socket_select($changed, $null, $null, 0, 10);
//check for new socket
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket); //accpet new socket
$clients[] = $socket_new; //add socket to client array
$header = socket_read($socket_new, 10240); //read data sent by the socket
perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
socket_getpeername($socket_new, $ip); //get ip address of connected socket
$response = mask(json_encode(array('type' => 'system', 'status' => true, "id" => "SRV_CONNECTED", 'message' => $ip . ' connected'))); //prepare json data
send_message($response); //notify all users about new connection
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
foreach ($changed as $changed_socket) {
while (@socket_recv($changed_socket, $buf, 1024, 0) >= 1) {
$received_text = unmask($buf); //unmask data
$tst_msg = json_decode($received_text, true); //json decode
$response_text = parse_msg($tst_msg, $changed_socket);
break 2; //exit this loop
}
$buf = @socket_read($changed_socket, 10240, PHP_NORMAL_READ);
if ($buf === false) { // check disconnected client
// remove client for $clients array
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
//notify all users about disconnected connection
$response = mask(json_encode(array('type' => 'system', 'message' => $ip . ' disconnected')));
send_message($response);
}
}
I Need to call parse_msg by count of frames received in @socket_recv which should be returned by unmask function.
And here is unmask function:
function unmask($payload){
$decMessages = Array();
do { // This should be running until all frames are unmasked and added to $decMessages Array
$length = ord($payload[1]) & 127;
if($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
$len = (ord($payload[2]) << 8) + ord($payload[3]);
}elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
$len = (ord($payload[2]) << 56) + (ord($payload[3]) << 48) +
(ord($payload[4]) << 40) + (ord($payload[5]) << 32) +
(ord($payload[6]) << 24) +(ord($payload[7]) << 16) +
(ord($payload[8]) << 8) + ord($payload[9]);
}else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
$len = $length;
}
$text = '';
for ($i = 0; $i < $len; ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
$decMessages[] = $text;
// Here is problem. It doesn't put correct substr to $payload, so it could run again on stream shorted by last message
$payload = substr($payload, $length, strlen($payload));
}while (($len < strlen($data)) and $countert < 10);
return $decMessages;
}
I think my problem is here: $payload = substr($payload, $length, strlen($payload)); Where I'm putting wrongly rest of stream?
So my questions are: Why @socket_recv return more than one frame How can i change my unmask function to go thru all frames, not just a first one
I will be extremelly greatfull for any help, because I'm fighting with this more than I should.
Thanks in advance Klara!
so, I found solution for this. As I thought, problem was multiple frames in one receive. I needed to edit my unmask function to check if there are still frames in stream.
Basically, what I had was right but $length in
$payload = substr($payload, $length, strlen($payload));
was wrong. Instead of $length I used $len + 8, 14 od 6 depend on $length. I'm not sure how to describe why this but I'm sure somebody will. That's all. Now it's working as it should!
Thanks!