Bind socket, and recvfrom in separated functions - php

I'm trying to receive UDP data from a game. And update it on the page constantly. Using JSON to retrieve the data from a function.
Is it possible to separate socket_create/socket_bind from socket_recvfrom?
(I've cut some unnecessary code out of it)
private $socket;
public function socketConnect($port)
{
$ip = '0.0.0.0'; // local IP
$port = 20777; // port to listen
$this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($this->socket, $ip, $port) or die("Could not connect");
return view('telemetry');
}
public function getData()
{
while (socket_recvfrom($this->socket, $buf, $bytes, 0, $remote_ip, $remote_port)) {
// do something
return response()->json($data);
}
}
I've tried setting $this->socket to keep the data but that doesn't seem to work.

You cannot do that, no. Sockets created with the socket_* methods are not persistent, since they are low level sockets. And here your two functions are executed in two different page calls that are independent from each other (this is how PHP works).
Furthermore, what you are trying to do right now is a socket server (binding a socket is for server purposes usually), and socket servers need to be open continually to receive data. I would advise you to try to do a CLI program (that can still be done in PHP) and use the socket_recvfrom and socket_sendto methods to get and send your data (after doing your bind), and then store the received data in a database or in a file, to then have your website read from that to send it to your browser. Here is a howto that shows how to create a simple UDP server in PHP.

Related

PHP Checking if a port is Active

I'm in the process of creating my own service status script as both a chance to become more familiar with the PHP language and to design it from the ground up as being as efficient as possible for my needs.
A section of my code used in both my cron job and testing a connection parts queries the IP/Port of a service to make sure it is online. My issue is that the script simply queries whether the port is "Unblocked" on that IP so if for instance I was querying port 21 with an FTP server and that FTP server crashed my script would not detect any changes meaning its not doing what I want it to do. Instead I would be wanting the IP and port to be queried and for my script to see if there is actually something running on that port, if there is show online if not error out. I've had a look on google and it seems like I would have to send a packet/receive a response so PHP can tell there's something active? I'm not sure.
This is my current code below:
<?php
$host = $_POST['servip'];
$port = $_POST['servport'];
if (!$socket = #fsockopen($host, $port, $errno, $errstr, 3)) {
echo "Offline!";
} else {
echo "Online!";
fclose($socket);
}
?>
http://php.net/manual/en/function.fsockopen.php
fsockopen — Open Internet or Unix domain socket connection The socket
will by default be opened in blocking mode. You can switch it to
non-blocking mode by using stream_set_blocking(). The function
stream_socket_client() is similar but provides a richer set of
options, including non-blocking connection and the ability to provide
a stream context.
Since fsockopen will either connect or not connect (timeout) then that tells you whether or not a connection is available ("open") or being blocked (firewall, etc).
// Ping by website domain name, IP address or Hostname
function example_pingDomain($domain){
$starttime = microtime(true);
$file = #fsockopen($domain, 80, $errno, $errstr, 10);
$stoptime = microtime(true);
$status = 0;
if (!$file) {
$status = -1; // Site is down
} else {
fclose($file);
$status = ($stoptime - $starttime) * 1000;
$status = floor($status);
}
return $status;
}
If you really want to know if the FTP server is working or not, your best option is to actually send FTP commands through to it.
An FTP server, upon connect, should typically reply with the first three bytes "220" or "120". 220 is a "greeting". You can read more in RFC 959.
To be completely sure, you might be better off using ftp:// handling in PHP, e.g. actually authenticating a user (maybe user authentication is broken, but it's still able to send a greeting - does that count is "down"?)
Anyway, if you want better than "was I able to connect on that port?" or "did the connect succeed in a timely fashion?", you have to delve into actual communication over the socket. Ultimately, this means you have to do something special for each type of service (for some, read bytes, for others write bytes, etc.)

PHP socket server, check if client is alive

I have a php server listening for 1 c# client.
When a connection is established, it is kept alive until client sends the command "quit" which kills the PHP server.
But when the c# client disconnects without the "quit" command (ie : clicking the close (x) button in the windows form) the server just keep listening, and can't receive any other connection from that client.
Is there a way to check from the server side (PHP) if connection is still alive with client?
My php server code is based on example1 of: http://php.net/manual/en/sockets.examples.php
If someone is interested in reproducing the bug/error behavior, paste code from example1 : http://php.net/manual/en/sockets.examples.php, connect by telnet from a remote client in lan, unplug client wire... php server will hang around forever, no new connection is accepted.
In your loop, you need to check the return value of socket_read(). If it returns FALSE, then there was a read error (which can be caused by the remote host closing the connection). The example code in the link you provided covers this case.
If you need to gracefully handle certain error states, you can always check the socket error code using socket_last_error() -- this note decribes the possible codes.
Edit:
When using putty for telnet, if i close with X button, connecion is closed properly in PHP, but if i unplug the ethernet wire of the putty machine, PHP server just hangs around.
The reason that the connection is closed when killing PuTTY is that PuTTY closes its open connection(s) when exiting. This causes socket_read() to return with an error code (I believe ECONNRESET). If you pull the network cable, it doesn't have a chance to do that.
Depending on how your network is configured, the TCP connection should eventually fail. You can attempt to control the timeout by setting SO_RCVTIMEO with socket_set_option(), but this doesn't always work on all platforms (I'm looking at you, WinSock).
Alternatively, you can roll your own polling loop using socket_select() with a reasonable timeout. If none of the connected sockets have data to send after your timeout, then kill the server.
I did this by checking if the socket_select() $read array includes the connection in question but socket_read() data is empty (twice).
It seems that socket_select() adds disconnected clients to the $read array and socket_read() gives an empty string '' when trying to read the disconnected client's data.
The following code will show the state of connection.
$address='example.com';
$port = '9065';
if (isset($port) && ($socket=socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
&& (socket_connect($socket, $address, $port))) {
$text="Connection successful on IP $address, port $port";
socket_close($socket);
}
else {
$text='Unable to connect<pre>'.socket_strerror(socket_last_error()).'</pre>';
}
echo $text;
As Michael Dodd says, using socket_select() can have all sockets that have received data plus those they have closed. Also you can have all those that their buffer can accept data for transmission, and / or those that have raised some exception. Do have this info, a (separate) copy of sockets array must be placed in 2nd and / 3d parameter of function.
(if not needed, this function must have $null as a variable equal to null, not just null)
The following example shows how to read data and test sockets if they are still open. I am using this code for sockets that have been created by socket_accept() on a listening socket, and it is working without problems. Instead of using socket_recv, socket_read can be used.
//all sockets created have been inserted to an array $socketArray
//get a copy of the sockets array
$tempArray = $socketArray;
//this command will remove from $tempArray all sockets that have no data and are alive, with a timeout of 0 sec, 100 msec
socket_select($tempArray, $null, $null, 0, 100);
if (count($tempArray)) {
//if we have some sockets in the array
foreach($tempArray as $socket) {
//read some data
$count = socket_recv($socket, $socketData, 1024, 0);
if ($count) {
//your code to do what you want with $socketData
} else {
//find socket position in initial socket array
$index = array_search($socket, $socketArray);
//if found, remove it from array and close the socket
if ($index !== false) {
array_splice($socketArray, $index, 1);
socket_close($socket);
}
}
}
}

PHP script connecting TCP/IP server?

I know that PHP does allow you to create a server but what about client? I would need a script that connects to my TCP/IP server on given port and send some data. Is that possible in PHP and if so, could you help me please? I did not find anything useful.
I have my TCP/IP server running on port 1301 and I would need users to be able by clicking on web page send one char to the server.
It's similar to how you would create a server. I'd recommend taking a look at the documentation for socket_connect.
Summaries:
socket_create
socket_bind
socket_connect
socket_write
socket_read
socket_close
Workflow:
Create the socket
Optionally bind it
Connect to the server
Read/write data
Close the socket
I've used this piece before. It's fairly simple; it connects to $ip_address on port $port, and sends the $sendData data to the server, and then reads the response and returns the response.
$sendData = chr(6).chr(0).chr(255).chr(255).'info';
function sendAndGetResponse($ip_address, $port, $sendData){
$socketHandler=#fsockopen($ip_address, $port, $errno, $errstr, 1);
if(!$socketHandler)
{
return false; //offline
}
else
{
$response = '';
stream_set_timeout($socketHandler, 2);
fwrite($socketHandler, $sendData);
while (!feof($socketHandler))
{
stream_set_timeout($socketHandler, 2);
$response .= fgets($socketHandler, 1024);
}
fclose($socketHandler);
return $response;
}
}
You can use CURL if it is HTTP server or create a socket connection http://php.net/manual/en/function.socket-connect.php
Yes, php can act as a HTTP-client with CURL, fsockopen and most easiest way to fetch URL - with file_get_contents()

Correct usage of socket_select()

What is the correct way to use socket_select within PHP to send and receive data?
I have a connection to the server that allows for both TCP & UDP packet connections, I am utilizing both. Within these connections I'm both sending and receiving packets on the same port, but the TCP packet will be sent on one port (29999) and UDP will be sent on another port (30000). The transmission type will be that of AF_INET. The IP address will be loopback 127.0.0.1.
I have many questions on how to create a socket connection within this scenario. For example, is it better to use socket_create_pair to make the connection, or use just socket_create followed by socket_connect, and then implement socket_select?
There is a chance that no data will be sent from the server to the client, and it is up to the client to maintain the connection. This will be done by utilizing the time out function within the socket_select call. Should no data be sent within the time limit, the socket_select function will break and a keep alive packet can then be sent. The following script is of the client.
// Create
$TCP = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$UDP = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// Misc
$isAlive = TRUE;
$UDPPort = 30000;
define('ISP_ISI', 1);
// Connect
socket_connect($TCP, '127.0.0.1', 29999);
socket_connect($UDP, '127.0.0.1', $UDPPort);
// Construct Parameters
$recv = array($TCP, $UDP);
$null = NULL;
// Make The Packet to Send.
$packet = pack('CCCxSSxCSa16a16', 44, ISP_ISI, 1, $UDPPort, 0, '!', 0, 'AdminPass', 'SocketSelect');
// Send ISI (InSim Init) Packet
socket_write($TCP, $packet);
/* Main Program Loop */
while ($isAlive == TRUE)
{
// Socket Select
$sock = socket_select($recv, $null, $null, 5);
// Check Status
if ($sock === FALSE)
$isAlive = FALSE; # Error
else if ($sock > 0)
# How does one check to find what socket changed?
else
# Something else happed, don't know what as it's not in the documentation, Could this be our timeout getting tripped?
}
I'm a bit confused - you seem to be trying to deal with asynchronous requests coming in via 2 sockets but both are acting as clients? This is a very unusual scenario. To be trying to implement them using different protocols (tcp and udp) is even odder (H323 VOIP is the only applciation I know of which does this). A quick google suggests you are trying to write a client for LFS - but why do you need a TCP and UDP client running at the same time? (BTW they publish suitable PHP client code on their Wiki at http://en.lfsmanual.net )
The socket which has data waiting to be read will be in the $recv array after the call to socket_select() (i.e. the array is trimmed down and needs to be repopulated before the next iteration of socket_select()).
If socket_select returns 0 it just means that the sockets are non-blocking and none of them have any data available.
HTH
C.

PHP: socket listen problem

Here is my code:
<?php
$host = "127.0.0.1";
$portListen = 1234;
$portSend = 1240;
// create socket
$sSender = socket_create(AF_INET, SOCK_STREAM, 0);
socket_connect($sSender, $host, $portListen);
socket_write($sSender, "test", strlen ("test"));
$sListen = socket_create(AF_INET, SOCK_STREAM, 0);
socket_set_option($sListen, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($sListen, $host, $portSend);
socket_listen($sListen,1);
$dataSock = socket_accept($sListen);
echo socket_read($dataSock, 3, PHP_NORMAL_READ);
// close sockets
socket_close($sSender);
socket_close($sListen);
?>
I send "test" to another application, it receives, and send back "ack". Problem is, I can only do it once. If I refresh, I get the address is already used error. I tried the solutions suggested on php.net but to no avail. Trying to socket_shutdown() before socket_close() only give me not connected warning, and upon refreshing will give me a never ending loading.
From what I understand the reason socket is not immediately closed is because there is still data in the buffer. But as you can see I explicitly state to listen to only 1 connection. Plus I am only sending 3 characters from my application and reading 3 in this script.
What am I doing wrong?
edit: The reason I'm using 2 sockets is because I cannot listen() after write() which give me socket is already connected error. Skipping listen() and going straight for read() after write() give me invalid argument error.
I see, after having a few hours sleep and re-analyzing my code and the documentations, I managed to fix everything. You guys are right, 1 socket is indeed enough and the correct way:
<?php
$host = "127.0.0.1";
$portListen = 1234;
$sSender = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
socket_connect($sSender, $host, $portListen) or die("Could not connect\n");
socket_write($sSender, "test", strlen ("test")) or die("Could not write output\n");
echo socket_read($sSender, 3, PHP_NORMAL_READ);
socket_close($sSender);
?>
So simple!
After a connection is closed, the socket enters a linger state so that if (during the close) packets were lost and retransmitted, the response would be a clean acknowledgement instead of RST (reset) indicating no such socket was open. It's part of the TCP specification, you can't make it stop happening.
Listen(1) doesn't mean accept only one connection, it means maintain a queue of up 1 connections waiting for an application to accept() them. So as soon as you accept the first, the socket is ready to listen for more.
Like everybody else, I'm wondering why the odd design, but I assume it's a boiled-down example that presents your problem and doesn't necessarily present your real plan.
Why do you need to create 2 sockets for read and write? It looks like an odd design. Client apps usually open a socket connection to the server, then send a request and read the server's response on the same socket.
Also, creating a listening socket (iow a server) won't scale past any firewall or NAT gateway.
Answer to yor comment: No need to listen. just read (possibly blocking operation if your server hasn't replied yet).

Categories