I am trying to send data by PHP sockets, the client is an Arduino device, it receives the data OK when I send it multiple times, but if I reset the client (Arduino device), it reboots in a few seconds, it says it connected to the PHP socket, then when I want to send data again by socket_send()
it fails silently, the PHP socket_send()
is not returning an error on first actual error, only the second time I try (and fail), only then it returns error ("zero bytes sent"). When this error is received, I create another socket_accept()
and successfully send the message.
What could cause this ? I want it to properly detect a lost connection so I can resend data if needed.
It feels like it sends data to an old connection and only realizes it on second try, is that possible ?
Can this be fixed by socket_select()
? I have trouble understanding what that does.
Clarification: If client restarts and connects again, then sending data to it returns int
, then false
, false
, false
(unless I unset the $accept
and I do a socket_accept()
again). If client remains offline, then the sending always returns int
, int
, int
.
int
being the size of the string sent.
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socketn");
// reuse any existing open port to avoid error
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
$result = socket_bind($socket, $host, $port) or die("Could not bind to socketn");
$result = socket_listen($socket) or die("Could not set up socket listenern");
do{
if(!isset($accept)){
echo "nwaiting for clients";
$accept = @socket_accept($socket) or die("Could not accept incoming connection");
echo "nclient connected";
}
// memcached will return a message here like: "my messagern"
$message_to_send = $memcached->get('my_socket_message');
if($message_to_send!=''){
echo "nsending: ".$message_to_send;
$total_data_sent = @socket_send($accept, $message_to_send, strlen($message_to_send), MSG_EOR);
// if data was not send (sent to an old connection ?!)...
// then clear $accept, so a new connection is accepted
// and keep the my_socket_message variable, so message is sent again
if($total_data_sent === false){
echo "nSEND FAILED, will retry message: ".$message_to_send;
unset($accept);
} else {
$memcached->delete('my_socket_message');
}
}
} while (true);
2
Answers
This is what I ended up using and it seems to work nicely. It waits for a reply/confirmation from client, if no confirmation received,
message_to_send
is preserved, connection is cleared, on next loop it reconnects and retries to send the existentmessage_to_send
.I’ve experimented a bit and was able to reproduce the issue. Here is my solution for it (maybe not the best way, but works):
edit
I came up with a more neat solution. We just do what the
socket_send
function is supposed to do. Returnfalse
orint
number of received bytes.On the receiving end you just decode the message (
$data
) and send back thestrlen
of$data['payload']
. The client can verify it has received all data like this too. To do so just comparestrlen
of$data['payload']
with$data['length']
. If you really get paranoid you can implement some checksum aswell.Code for Server use
send
instead ofsocket_send
:Even though this works, I thought TCP does exactly that – ensure the data has been received. Maybe we are still missing something here.