I am using sockets to send data to a server that may not be responding. So I am trying to define a timeout by using this solution in SO.
Make PHP socket_connect timeout
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 1, 'usec' => 0));
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 1, 'usec' => 0));
This works when the connection is made and the server takes too long to respond.
But when it can't create a connection socket_connect($socket, $addr, $port); the timeout is about 20 seconds.
Why is this 20 second timeout happening and how can I force the connection creation to timeout after 1 second too?
You can do this by switching to a non-blocking socket, looping until either a connection is gained or a timeout was reached, then back to blocking again.
// an unreachable address
$host = '10.0.0.1';
$port = 50000;
$timeout = 2;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// switch to non-blocking
socket_set_nonblock($sock);
// store the current time
$time = time();
// loop until a connection is gained or timeout reached
while (!#socket_connect($sock, $host, $port)) {
$err = socket_last_error($sock);
// success!
if($err === 56) {
print('connected ok');
break;
}
// if timeout reaches then call exit();
if ((time() - $time) >= $timeout) {
socket_close($sock);
print('timeout reached!');
exit();
}
// sleep for a bit
usleep(250000);
}
// re-block the socket if needed
socket_set_block($sock);
edit: see #letiagoalves answer for an neater solution if you are using sockets created with fsockopen() or stream_socket_client()
I changed my socket communication mechanism to use stream_socket_client ($remote_socket, &$errno, &$errstr, $timeout) function instead. This function allows to define the connect timeout unlike socket_connect ($socket, $address, $port) which doesn't.
To force a timeout using socket_connect see #bigtallbill answer.
I tried a lot of variants with sockets..
fsockopen the best for simple operations, ex. testing connections
The SO_RCVTIMEO/SO_SNDTIMEO options don't work for socket_connect on some platforms, but only for socket_recv/socket_send. I can see it works on Ubuntu, but not Mac OSX.
Related
Currently I am trying to receive UDP broadcasts with PHP. The topic is very rare to find on the internet and if there are no real solutions that would have led me to success.
Here is my current code how I set up my server-socket to receive the UDP-broadcasts:
$context = stream_context_create([
'socket' => [
\SO_BROADCAST => 1,
'so_broadcast' => 1,
],
]);
$socket = #\stream_socket_server($address, $errno, $errstr, \STREAM_SERVER_BIND, $context);
since i'm not sure if i have to use the constant \SO_BROADCAST or the string 'so_broadcast' i've listed both in this example. of course i've tried everything in different variations. the documentation is actually quite clear here: https://www.php.net/manual/en/context.socket.php#context.socket.so_broadcast
still i can't receive a broadcast. i'm currently testing this via NC:
$ echo 'HELLO' | nc -u -v -b 10.88.0.255 20000
via tcpdump i can also see the broadcast on the server interface, but unfortunately it does not arrive at my socket? if i send a packet directly to the server ip it arrives and i can process it.
anyone else have an idea for me?
regards, volker.
EDIT:
an simple context params reverse check shows:
var_dump(stream_context_get_options($context));
# array(1) {
# ["socket"]=>
# array(1) {
# ["so_broadcast"]=>
# int(1)
# }
#}
so i think the option as string is the right decision...
Answer is easy, check the firewall settings. this was my problem, my local firewall block broadcasts to 255.255.255.255.
here is an complete sample code to receive DHCP broadcast packets on dedicated interface ep1:
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($socket, SOL_SOCKET, SO_BINDTODEVICE, 'ep1');
socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_set_option($socket, SOL_SOCKET, SO_REUSEPORT, 1);
socket_bind($socket, '255.255.255.255', 67);
while (1) {
if ($src = #socket_recv($socket, $data, 9999, 0)) {
echo $data . PHP_EOL;
}
}
I have a database where i have stored all IP address. Now i want to know these IP address is connected/normal or disconnected. I have tried:
$add = "example.com";
$result = checkdnsrr($add, "MX");
var_dump($result);
Its return boolean true or false. But i have IP address which is not connected dns. But how can i know the IP is active/normal/connected or disconnected?
LONG POLLING is BAD
As far as I have understood your question, you just want to check if the particular client is connected or not.
You will have to setup a cron job in PHP with a continuous loop which will be long polled by XHR (AJAX with Jquery etc) setting a status = true. So, when a user disconnects, the XHR will be broken and a status will be set = false. Thus you can check if a user is connected or not. However, please note that Long Polling is really resource intensive and is not appreciated.
I would highly suggest you to use go with Node and Websockets etc.
I could write a code for PHP Cron job and settle your problem but I don't appreciate Long Polling + Cron Job for it.
if you want to check if the website is alive then you can do it like this:
$add = "example.com";
$result = false;
if($fp = fsockopen($add, 80, $errno, $errstr, 10)){
fclose($fp);
$result = true;
}
var_dump($result);
// edit: so you want to know if the IP is present in your local network ? if so you can use ping like:
function ping($host, $timeout = 1) {
/* ICMP ping packet with a pre-calculated checksum */
$package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
$socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
socket_connect($socket, $host, null);
$ts = microtime(true);
socket_send($socket, $package, strLen($package), 0);
if (socket_read($socket, 255))
$result = microtime(true) - $ts;
else $result = false;
socket_close($socket);
return $result;
}
$present = ping('192.168.0.100');
I have the following php script (tcp server)
<?php
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, "0.0.0.0", 14010) or die('Could not bind to address');
$i=0;
for(;;) {
// Start listening for connections
socket_listen($sock);
/* Accept incoming requests and handle them as child processes */
$client = socket_accept($sock);
// Read the input from the client – 1024 bytes
$in = socket_read($client, 41984);
socket_write($client, $in);
//socket_close($client);
}
// Close the master sockets
socket_close($sock);
?>
How I can make the server wait the [FIN + ACK] from the client before closing the socket?
You can instead of calling close, do a recv on socket (not sure of php construct). recv system call returns 0 on peer closure. If this is the case you can then close on server side.
I am trying to receive a UDP multicast stream in PHP. The receive command never gets anything and waits forever.
I can watch the stream using VLC player so the stream is accessible on my machine. Any help on how to do this using PHP is highly appreciated.
Here is my code.
<?php
error_reporting(E_ALL | E_STRICT);
//create a new socket
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
//i am not sure about this command. I think i have to set this option to start receiving packets.
socket_set_option($socket, SOL_SOCKET, MCAST_JOIN_SOURCE_GROUP, array("group"=>"239.194.0.73","interface"=>"eth0","source"=>"239.194.0.73"));
$binded = socket_bind($socket, '127.0.0.1', 6073);
//receive data
$from = '';
$port = 0;
socket_recvfrom($socket, $buf, 12, MSG_WAITALL, $from, $port);
echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
?>
$binded = socket_bind($socket, '127.0.0.1', 6073);
Should be
$binded = socket_bind($socket, '0.0.0.0', 6073);
Or else you will only recv packets originating from the local host.
Following is my socket connection request and response order.
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
$connection = socket_connect($socket, $Host, $Port);
$Md5CheckSum = md5($msg);
$WillWait = 'SOAP '. $Md5CheckSum. ' WILL_WAIT'."\n";
socket_write($socket,$WillWait);
socket_write($socket,$msg);
socket_write($socket, SoapSender::$TERM_CHAR);
sleep(1);
$buf = socket_read($socket, 2048);
//socket_write($socket,"&\r\n");
echo "$buf\n";
Please could somebody tell me how to read response that I receive after last socket_write request. I have been searching for this answer all day but have not been able to find any help through Google.
Thanks a lot for your time.
Two functions should be used:
stream_set_blocking($socket, true);
And
stream_get_contents($socket);
Setting a block on your stream requires the return of data before your application will continue execution of the script.
If you do not set a stream block, sometimes latency will cause your PHP script to think there was no response, causing you to not receive data.
Also, use stream_get_contents to pull from the socket. This will grab by default the full buffer.
The correct way is to use socket_read, not stream_get_contests as someone else suggested.
Here is an example:
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($sock, "10.197.24.40", "5000");
$request = '{ "request" : { "id" : "some_function_id", "data": "55555555-5"} }';
// We send the request
socket_write($sock,$request);
socket_read($sock,1000000);
socket_close($sock);
I have tested this code in a live environment and it works correctly.