I am trying to learn about sockets in PHP and lots of reading has brought me down the route of stream_socket_server().
I got a basic chat working between two terminals in linux using code similar to what is below and was hoping my next step would be to build a chat or a notification system on the web.
What I expected to happen:
The event listener in eventSource.html would listen for an event in the while loop and output the message received from a linux terminal that was running server.php
What is happening:
Everything from the point of view of eventSource.html is working as it should. So if I was to take away the entire purpose of this and just replace the message with a standard string Hello World then this succesfully outputs <li>message:{Hello World}</li> every second.
However once I try and read data from the terminal nothing is appearing except <li>message: {}</li> every second. It is worth noting that when I run server.php it waits for the client, and when I then run eventSource.html it sucessfully connects.
I am misunderstanding how this works? I assumed that every second in the while loop it would look out for data in that stream.
Or am I going down a completely wrong road in terms of learning sockets.
server.php (I load this from the terminal in linux)
<?php
$PORT = 20226; //chat port
$ADDRESS = "localhost"; //adress
$ssock; //server socket
$csock; //chat socket
$uin; //user input file descriptor
$ssock = stream_socket_server("tcp://$ADDRESS:$PORT"); //creating the server sock
echo "Waiting for client...\n";
$csock = stream_socket_accept($ssock); //waiting for the client to connect
//$csock will be used as the chat socket
echo "Connection established\n";
$uin = fopen("php://stdin", "r"); //opening a standart input file stream
$conOpen = true; //we run the read loop until other side closes connection
while($conOpen) { //the read loop
$r = array($csock, $uin); //file streams to select from
$w = NULL; //no streams to write to
$e = NULL; //no special stuff handling
$t = NULL; //no timeout for waiting
if(0 < stream_select($r, $w, $e, $t)) { //if select didn't throw an error
foreach($r as $i => $fd) { //checking every socket in list to see who's ready
if($fd == $uin) { //the stdin is ready for reading
$text = fgets($uin);
fwrite($csock, $text);
}
else { //the socket is ready for reading
$text = fgets($csock);
if($text == "") { //a 0 length string is read -> connection closed
echo "Connection closed by peer\n";
$conOpen = false;
fclose($csock);
break;
}
echo "[Client says] " .$text;
}
}
}
}
client.php gets loaded from eventSource.html below
<?php
date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");
$PORT = 20226; //chat port
$ADDRESS = "localhost"; //adress
$sock = stream_socket_client("tcp://$ADDRESS:$PORT");
$uin = fopen("php://stdin", "r");
while (1) {
$text = fgets($uin);
echo 'data: {'.$text.'}';
echo "\n\n";
ob_end_flush();
flush();
sleep(1);
}
eventSource.html
<script>
var evtSource = new EventSource("client.php");
evtSource.onmessage = function(e) {
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
var div = document.getElementById('events');
div.appendChild(newElement);
}
</script>
<div id="events">
</div>
Related
I am trying to do a live output of a file called fail2ban.log this log is on my linux server and i try to proccess it using. The tail process stay opened so it uses loads of cpu performance after some pepoles open the page since the process stay opened
I tried some solution of killing it with
while(true)
{
if($flag === false) die(); // Or exit if you prefer
}
The server is on Apache2
My code :
<?php
echo "Number of banned ip (live) : ";
$hand = popen("grep 'Ban' /var/log/fail2ban.log | wc -l 2>&1", 'r');
while(!feof($hand)) {
$buff = fgets($hand);
echo "$buff<br/>\n";
ob_flush();
flush();
}
pclose($hand);
echo " ";
echo "Current Log (go at the bottom of the page for the live log)";
echo " ";
$output = shell_exec('cat /var/log/fail2ban.log 2>&1');
echo "<pre>$output</pre>";
echo "Live Logs";
echo "<h1> </h1> ";
echo " ";
$handle = popen("tail -f /var/log/fail2ban.log 2>&1", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer<br/>\n";
ob_flush();
flush();
}
pclose($handle);
?>
I want it to kill the process when the user quit the page.
No #jhnc In this case, popen is guilty, which does not end the process when the program is closed.
In general, PHP is one of the worst choices to implement tail -f. It's better to use node + websocket.
In this case, you need to check if something has been added to the file by another method. From http://php.net/manual/en/function.inotify-init.php#101093
<?php
/**
* Tail a file (UNIX only!)
* Watch a file for changes using inotify and return the changed data
*
* #param string $file - filename of the file to be watched
* #param integer $pos - actual position in the file
* #return string
*/
function tail($file,&$pos) {
// get the size of the file
if(!$pos) $pos = filesize($file);
// Open an inotify instance
$fd = inotify_init();
// Watch $file for changes.
$watch_descriptor = inotify_add_watch($fd, $file, IN_ALL_EVENTS);
// Loop forever (breaks are below)
while (true) {
// Read events (inotify_read is blocking!)
$events = inotify_read($fd);
// Loop though the events which occured
foreach ($events as $event=>$evdetails) {
// React on the event type
switch (true) {
// File was modified
case ($evdetails['mask'] & IN_MODIFY):
// Stop watching $file for changes
inotify_rm_watch($fd, $watch_descriptor);
// Close the inotify instance
fclose($fd);
// open the file
$fp = fopen($file,'r');
if (!$fp) return false;
// seek to the last EOF position
fseek($fp,$pos);
// read until EOF
while (!feof($fp)) {
$buf .= fread($fp,8192);
}
// save the new EOF to $pos
$pos = ftell($fp); // (remember: $pos is called by reference)
// close the file pointer
fclose($fp);
// return the new data and leave the function
return $buf;
// be a nice guy and program good code ;-)
break;
// File was moved or deleted
case ($evdetails['mask'] & IN_MOVE):
case ($evdetails['mask'] & IN_MOVE_SELF):
case ($evdetails['mask'] & IN_DELETE):
case ($evdetails['mask'] & IN_DELETE_SELF):
// Stop watching $file for changes
inotify_rm_watch($fd, $watch_descriptor);
// Close the inotify instance
fclose($fd);
// Return a failure
return false;
break;
}
}
}
}
// Use it like that:
$lastpos = 0;
$file = '/var/log/fail2ban.log'l
while (true) {
echo tail($file,$lastpos);
ob_flush();
flush();
}
?>
And you can't forget about max_execution_time and Apache limits
I am new to socket programming. I have a GPS Tracker that connects and send data to a defined public server:port through GPRS connection.
I am getting the result on the terminal.
I want to know how can I get that response in some variable so I can save those entries in database real-time.
Here is the code.
require_once("SocketServer.class.php"); // Include the File
$server = new SocketServer("xxx.xx.x.xxx",8153); // Create a Server binding to the given ip address and listen to port 31337 for connections
$server->max_clients = 10; // Allow no more than 10 people to connect at a time
$server->hook("CONNECT","handle_connect"); // Run handle_connect every time someone connects
$server->hook("INPUT","handle_input"); // Run handle_input whenever text is sent to the server
$server->infinite_loop(); // Run Server Code Until Process is terminated.
function handle_connect(&$server,&$client,$input)
{
SocketServer::socket_write_smart($client->socket,"String? ","");
}
function handle_input(&$server,&$client,$input)
{
// You probably want to sanitize your inputs here
$trim = trim($input); // Trim the input, Remove Line Endings and Extra Whitespace.
if(strtolower($trim) == "quit") // User Wants to quit the server
{
SocketServer::socket_write_smart($client->socket,"Oh... Goodbye..."); // Give the user a sad goodbye message, meany!
$server->disconnect($client->server_clients_index); // Disconnect this client.
return; // Ends the function
}
$output = strrev($trim); // Reverse the String
echo "output is".$trim;
SocketServer::socket_write_smart($client->socket,$output); // Send the Client back the String
SocketServer::socket_write_smart($client->socket,"String? ",""); // Request Another String
}
SocketServer.class.php
http://www.phpclasses.org/browse/file/31975.html
This line doesn't print the result on terminal
echo "output is".$trim;
I have tried this as well
$myfile = fopen("file.txt", "w") or die("Unable to open file!");
fwrite($myfile, $trim);
fclose($myfile);
and the file remain empty
Go to SocketServer.class.php and look for this code
the result is in this variable $input. So if you want to save data into database then add your code in else part.
if($input == null){
$this->disconnect($i);
}else{
SocketServer::debug("{$i}#{$this->clients[$i]->ip} --> {$input}");
$data= $input;
//add your code here
Note: The author has updated the code of its library. Here is the new code
https://github.com/navarr/Sockets
I have a weird issue and I can't seem to find a solution or anything closer to the issue I am having ,
Here is the thing , I have a scoket script run via php on command line, it accepts connection and reads data in json format from mobile app clients and sends appropriate response in json.
Everything works fine except the number of connection does not go above 256 connection.
I would like to know why is that, and how can I solve it ? I have been It on so many days, but no luck!
Here is the script snippet
<?php
date_default_timezone_set("UTC");
$server = stream_socket_server("tcp://192.168.1.77:25003", $errno, $errorMessage);
if (!$server) {
die("$errstr ($errno)");
}
echo "Server started..";
echo "\r\n";
$client_socks = array();
while (true) {
//prepare readable sockets
$read_socks = $client_socks;
$read_socks[] = $server;
//start reading and use a large timeout
if (!stream_select ($read_socks, $write, $except, 10000)) {
die('something went wrong while selecting');
}
//new client
if (in_array($server, $read_socks)) {
$new_client = stream_socket_accept($server);
if ($new_client) {
//print remote client information, ip and port number
echo 'Connection accepted from ' . stream_socket_get_name($new_client, true);
echo "\r\n";
$client_socks[] = $new_client;
echo "Now there are total ". count($client_socks) . " clients";
echo "\r\n";
}
// echo stream_socket_get_name($new_client, true);
//delete the server socket from the read sockets
unset($read_socks[array_search($server, $read_socks)]);
}
$data = '';
$res = '';
//message from existing client
foreach($read_socks as $sock) {
stream_set_timeout($sock, 1000);
while($resp = fread($sock, 25000)) {
$data .= $resp;
if (strpos($data, "\n") !== false) {
break;
}
}
$info = stream_get_meta_data($sock);
if ($info['timed_out']) {
unset($client_socks[array_search($sock, $client_socks)]);
#fclose($sock);
echo 'Connection timed out!';
continue;
}
$client = stream_socket_get_name($sock, true);
if (!$data) {
unset($client_socks[array_search($sock, $client_socks)]);
#fclose($sock);
echo "$client got disconnected";
echo "\r\n";
continue;
}
//send the message back to client
$decode = json_decode($data);
$encode = json_encode($res);
fwrite($sock,$encode."\n");
}
}
P.S.: What I did is, extensive search on the topic, and went over article like these,
http://smallvoid.com/article/winnt-tcpip-max-limit.html and two dozens others.
I have a windows 7 running this thing + wamp 2.5 which runs php 5.5.12
It's nothing to do with your code, it's a "feature" of MS Windows to make you buy the server edition (or upgrade to a different OS). Functionally there's no difference between the server and desktop editions of the NT kernel (some different optimization tweaks) its just a means of ensuring you are complying with the terms of the licence.
I am learning socket programming in PHP and so I am trying a simple echo-chat server.
I wrote a server and it works. I can connect two netcats to it and when I write in the one netcat, I recive it at the other. Now, I want to implement what NC does in PHP
I want to use stream_select to see if I have data on STDIN or on the socket to either send the message from STDIN to the server or reading the incoming message from the server.
Unfortunately the example at the php manual doesn't give me any clue how to do that.
I tried to simply $line = fgets(STDIN) and socket_write($socket, $line) but it doesnt work. So I started to go down and just want stream_select to act up when the user typed the message.
$read = array(STDIN);
$write = NULL;
$exept = NULL;
while(1){
if(stream_select($read, $write, $exept, 0) > 0)
echo 'read';
}
Gives
PHP Warning: stream_select(): No stream arrays were passed in
/home/user/client.php on line 18
But when I var_dump($read) it tells me, that it is an array with a stream.
array(1) {
[0]=>
resource(1) of type (stream)
}
How do I get stream_select to work?
PS: In Python I can do something like
r,w,e = select.select([sys.stdin, sock.fd], [],[])
for input in r:
if input == sys.stdin:
#having input on stdin, we can read it now
if input == sock.fd
#there is input on socket, lets read it
I need the same in PHP
I found a solution. It seems to work, when I use:
$stdin = fopen('php://stdin', 'r');
$read = array($sock, $stdin);
$write = NULL;
$exept = NULL;
Instead of just STDIN. Despite php.net says, STDIN is already open and saves using
$stdin = fopen('php://stdin', 'r');
It seems not, if you want to pass it into stream_select.
Also, the socket to the server should be created with $sock = fsockopen($host); instead of using socket_create on the client side... gotta love this language and it's reasonability and clear manual...
Here a working example of a client that connects to an echo server using select.
<?php
$ip = '127.0.0.1';
$port = 1234;
$sock = fsockopen($ip, $port, $errno) or die(
"(EE) Couldn't connect to $ip:$port ".socket_strerror($errno)."\n");
if($sock)
$connected = TRUE;
$stdin = fopen('php://stdin', 'r'); //open STDIN for reading
while($connected){ //continuous loop monitoring the input streams
$read = array($sock, $stdin);
$write = NULL;
$exept = NULL;
if (stream_select($read, $write, $exept, 0) > 0){
//something happened on our monitors. let's see what it is
foreach ($read as $input => $fd){
if ($fd == $stdin){ //was it on STDIN?
$line = fgets($stdin); //then read the line and send it to socket
fwrite($sock, $line);
} else { //else was the socket itself, we got something from server
$line = fgets($sock); //lets read it
echo $line;
}
}
}
}
I am trying to create a inetd-like service for Windows in PHP for future use with my other application.
So all I can think of is to use Steam Server and proc_open to pipe the stream directly to the process (like inetd). Because on Windows there is no pcntl_fork(), and PHP doesn't support threading.
So far, here is my code. The inetdtest program is a simple program with single printf (written in C). But the problem is that when I connected to my server (via netcat), I got no response message.
<?php
define ('SERVICE_COMMAND', 'inetdtest');
define ('SERVICE_PORT', 35123);
function main() {
echo "Simple inetd starting...\n";
$socket = stream_socket_server('tcp://0.0.0.0:' . SERVICE_PORT, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN);
if ($socket === false) {
echo "Can't bind to service port.\n";
echo "[$errno] $errstr";
die(1);
}
$processes = array();
while (true) {
$current = #stream_socket_accept($socket, 5, $host);
if ($current !== false) {
echo 'Incomming connection from client ' . $host . "\n";
echo "Lunching child process... ";
$io = array(
0 => $current,
1 => $current,
2 => array('file', 'stderr.log', 'a')
);
$proc = proc_open(SERVICE_COMMAND, $io, $pipes, NULL, NULL, array('bypass_shell'));
$status = proc_get_status($proc);
echo " DONE! PID : {$status['pid']}\n";
$processes[] = array($current, $proc);
}
foreach ($processes as $k=>$v) {
$status = proc_get_status($v[1]);
if (false === $status['running']) {
echo "Finalizing process {$status['pid']}... ";
fflush($v[0]);
fclose($v[0]);
proc_close($v[1]);
unset($processes[$k]);
echo "DONE!\n";
}
}
}
}
main();
The code justs works as it stands here (using cat as program and on linux), so the problem lies somewhere in the windows side of things.
For one thing, the option you are passing, to bypass the shell, should be given as
array('bypass_shell'=>true)
This may fix things already. The tricky part with these things, is that you're passing a socket fd to a process, which may or may not be expected to handle that properly. I don't know how these things are done in windows, but cutting cmd out of the equation can only help.
If it still doesn't work, you should create a loop which waits for data (either from network or child processes) and sends data from the network socket to the process pipe, and vice versa.