Websocket server using php for flutter app [duplicate] - php

I am looking for a simple code to create a WebSocket server. I found phpwebsockets but it is outdated now and doesn't support the newest protocol. I tried updating it myself but it doesn't seem to work.
#!/php -q
<?php /* >php -q server.php */
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$master = WebSocket("localhost",12345);
$sockets = array($master);
$users = array();
$debug = false;
while(true){
$changed = $sockets;
socket_select($changed,$write=NULL,$except=NULL,NULL);
foreach($changed as $socket){
if($socket==$master){
$client=socket_accept($master);
if($client<0){ console("socket_accept() failed"); continue; }
else{ connect($client); }
}
else{
$bytes = #socket_recv($socket,$buffer,2048,0);
if($bytes==0){ disconnect($socket); }
else{
$user = getuserbysocket($socket);
if(!$user->handshake){ dohandshake($user,$buffer); }
else{ process($user,$buffer); }
}
}
}
}
//---------------------------------------------------------------
function process($user,$msg){
$action = unwrap($msg);
say("< ".$action);
switch($action){
case "hello" : send($user->socket,"hello human"); break;
case "hi" : send($user->socket,"zup human"); break;
case "name" : send($user->socket,"my name is Multivac, silly I know"); break;
case "age" : send($user->socket,"I am older than time itself"); break;
case "date" : send($user->socket,"today is ".date("Y.m.d")); break;
case "time" : send($user->socket,"server time is ".date("H:i:s")); break;
case "thanks": send($user->socket,"you're welcome"); break;
case "bye" : send($user->socket,"bye"); break;
default : send($user->socket,$action." not understood"); break;
}
}
function send($client,$msg){
say("> ".$msg);
$msg = wrap($msg);
socket_write($client,$msg,strlen($msg));
}
function WebSocket($address,$port){
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master,20) or die("socket_listen() failed");
echo "Server Started : ".date('Y-m-d H:i:s')."\n";
echo "Master socket : ".$master."\n";
echo "Listening on : ".$address." port ".$port."\n\n";
return $master;
}
function connect($socket){
global $sockets,$users;
$user = new User();
$user->id = uniqid();
$user->socket = $socket;
array_push($users,$user);
array_push($sockets,$socket);
console($socket." CONNECTED!");
}
function disconnect($socket){
global $sockets,$users;
$found=null;
$n=count($users);
for($i=0;$i<$n;$i++){
if($users[$i]->socket==$socket){ $found=$i; break; }
}
if(!is_null($found)){ array_splice($users,$found,1); }
$index = array_search($socket,$sockets);
socket_close($socket);
console($socket." DISCONNECTED!");
if($index>=0){ array_splice($sockets,$index,1); }
}
function dohandshake($user,$buffer){
console("\nRequesting handshake...");
console($buffer);
//list($resource,$host,$origin,$strkey1,$strkey2,$data)
list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
console("Handshaking...");
$acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";
socket_write($user->socket,$upgrade,strlen($upgrade));
$user->handshake=true;
console($upgrade);
console("Done handshaking...");
return true;
}
function getheaders($req){
$r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
}
function getuserbysocket($socket){
global $users;
$found=null;
foreach($users as $user){
if($user->socket==$socket){ $found=$user; break; }
}
return $found;
}
function say($msg=""){ echo $msg."\n"; }
function wrap($msg=""){ return chr(0).$msg.chr(255); }
function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
class User{
var $id;
var $socket;
var $handshake;
}
?>
and the client:
var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
If there is anything wrong in my code can you help me fix it? Console in Firefox says:
Firefox can't establish a connection to the server at ws://localhost:12345/.

I was in the same boat as you recently, and here is what I did:
I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)
I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.
I read the actual WebSocket draft. I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.
I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.
I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: (original link broken) Archived copy.
Please note that the code available at this link has a number of problems and won't work properly without further modification.
I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?
This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.
I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved: What is this data at the end of WebRTC candidate info?
At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.
That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great.
Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script.
Good luck!
Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.

As far as I'm aware Ratchet is the best PHP WebSocket solution available at the moment. And since it's open source you can see how the author has built this WebSocket solution using PHP.

I've searched the minimal solution possible to do PHP + WebSockets during hours, until I found this article:
Super simple PHP WebSocket example
It doesn't require any third-party library.
Here is how to do it: create a index.html containing this:
<html>
<body>
<div id="root"></div>
<script>
var host = 'ws://<<<IP_OF_YOUR_SERVER>>>:12345/websockets.php';
var socket = new WebSocket(host);
socket.onmessage = function(e) {
document.getElementById('root').innerHTML = e.data;
};
</script>
</body>
</html>
and open it in the browser, just after you have launched php websockets.php in the command-line (yes, it will be an event loop, constantly running PHP script), with this websockets.php file.

I was in your shoes for a while and finally ended up using node.js, because it can do hybrid solutions like having web and socket server in one. So php backend can submit requests thru http to node web server and then broadcast it with websocket. Very efficiant way to go.

Need to convert the the key from hex to dec before base64_encoding and then send it for handshake.
$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);
$rawToken = "";
for ($i = 0; $i < 20; $i++) {
$rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
}
$handshakeToken = base64_encode($rawToken) . "\r\n";
$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";

Related

php slim detect client abort (HTTP)

In slim framework V3 how can I detect if the client closed the connection ?
I have tried:
ignore_user_abort(false);
and tried checking connection_status() and connection_aborted() in a loop with no luck.
php 5.6.12
It depends not from Slim but from PHP in general. PHP close connection just after execution script.
If you want create persistance connection you need to look to long polling connection.
http://phptrends.com/dig_in/php-long-polling
It seems one need to output something from php and then call ob_flush() because php only handles errors from the browser. Here's a way to do it without outputing anything, which will work fine for long-polling scenarios:
/* returns ESTABLISHED, CLOSE_WAIT, LAST_ACK, etc */
function getConnectionStatus() {
$remote_ip = $_SERVER['REMOTE_ADDR']?:($_SERVER['HTTP_X_FORWARDED_FOR']?:$_SERVER['HTTP_CLIENT_IP']);
$remote_port=$_SERVER['REMOTE_PORT'];
$cmd="netstat -tn | fgrep ' $remote_ip:$remote_port '";
$pfp=popen($cmd,"r");
$buf = fgets($pfp, 1024);
pclose($pfp);
$buf=preg_replace('!\s+!', ' ', $buf); //remove multiple spaces
$buf=trim($buf);
$buf_r=explode(" ",$buf);
if (count($buf_r)) {
$state=$buf_r[count($buf_r)-1];
return trim($state);
}
return "NOTFOUND";
}
Use it as follows:
while(...) {
/* ... longpolling check code here ...*/
if (getConnectionStatus() != "ESTABLISHED") {
break;
}
}

Using Chat-API "the right way" to receive messages

I've been using Chat-API (https://github.com/WHAnonymous/Chat-API) since it was WhatsAPI, and yet I don't know for sure how to receive messages properly.
Right now, I have a cron file that runs once every minute with this basic structure:
$wa = new WhatsProt($WA_NUMBER, $WA_NICKNAME);
$wa->connect();
$wa->loginWithPassword($WA_PASSWORD);
$wa->pollMessage();
$data = $wa->getMessages();
foreach ($data as $item) {
$from_number = $item->getAttribute("from");
$from_nickname = $item->getAttribute("notify");
if ($item->getAttribute("type") == "text") {
$msg = $item->getChild("body")->getData();
} else {
$msg = $item->getChild("media")->getAttribute("url");
}
...
}
$wa->disconnect();
I've also tried running a PHP script constantly in the background like this:
while (true) {
$wa->pollMessage();
$data = $wa->getMessages();
...
}
The first option is more reliable than the second one, but neither is the right solution.
Is there any way making use of sockets to connect to Whatsapp servers as a phone would do? I mean, open a socket and keep it open, triggering a function every time a new message is received (using XMPP protocol).

PHP KeepAlive program for Amazon

So im fairly new to PHP and i was assigned a small project by my manager, now my problem is that this is the first time doing anything like this so was wondering for some advice, guidance, help in any shape or form just as abit of a leg up,
So here's the description,
PROBLEM
Amazon EC2 can have multiple instances running behind a load balancer.
The load balancer will check at a URL i.e /_healthcheck to verify if the machine is functional.
The term functional can be defined as
Webserver can be accessed (implied by ability to connect to this check)
Webserver can connect to network resources CouchDB and Elastic Search
The node is not experiencing extremely high loads currenty(?)
Create a MVC for AWS EC2 to verify that the node is online
so far this is what code i've wrote, confused on how to satisfy time requirements for PHP (e.g check every 5 seconds etc)
class KeepaliveController extends AbstractActionController {
public function statusAction() {
$host = 'http://localhost:8089';
$port = 80;
$waitTime = 0;
$attempt = 1;
if($fp = fsockopen($host,$port,$errCode,$errStr,$waitTime)){
$response = $this->getResponse();
$response->setStatusCode(200);
} elseif ($waittime > 5){
echo "server busy retrying";
$response = $this->getResponse();
$response->setStatusCode(503);
elseif ($attempt > 5){
echo "Internal Error";
$response = $this ->getResponse();
$response->setStatusCode(500)
$attempt++
} else {
echo "server offline";
$reponse = $this->getResponse();
$reponse->setStatusCode(404);
}
}
as i said any help would be great.
Thank you

Is there a real proxy written in PHP or, what would I have to do to implement one?

I have been looking around on the web for a PHP proxy script and I can't find any. All I keep finding are scripts that download pages and edit src and href attributes in the HTML and, require me to browse to whatever website they're installed on. I don't need this, I need a real proxy listening on a port that I can set my browser to route all it's requests to.
What I'm looking to do is:
run a PHP script that will listen on some port of my own computer
configure my web browser to use localhost:port as a proxy
have the PHP script serve the page to my browser
All the so called proxy scripts written in PHP, that I have found, will fail miserably if the page requested uses XMLHttpRequest to fetch content because they won't see that request at all. No amount of link rewriting will solve the issue short of injecting some clever JavaScript that is unnecessary with a real proxy.
What I want in the end, is to have access to the requests and responses going back and forth between my browser and some remote server. I want to be able to use my browser's support for JavaScript, cookies, flash, etc. Basically, I want to have programmatic access to all the communication so I can analyze or manipulate it using PHP.
So, the questions are
Have I missed the real proxy implemented in PHP because of all the noise in my search results?
If there isn't a real proxy in PHP I would like to try making one. Are there resources online to help me learn what exactly I should do? (note, I RTM religiously: looking more for caveats and architecture of a basic proxy)
Links are appreciated. I know there are several MITM proxies out there but I want one in PHP.
I don't know if maybe I could do something with PHP's built-in webserver but I'll mess with that as well.
UPDATE
I've got a router script for the PHP built in webserver that is beginning to show promise. I can fire up the webserver and tell my web browser to use it as a proxy. The router script I've made differentiates between local and external resources and gives an easy way of handling either case. The only problem I have is with https. The server reports Invalid Request (Malformed HTTP Request). I think that means this server won't do https at all with just scripts and settings. I don't know. Maybe I'll be able to do it with Apache but transparently proxying https sounds hard, especially if I want to alter the data before it hits my browser.
AtropaToolbox/php_router/router.php
The router script my PHP built in webserver is pointed at, pulls in the actual classes from other files.
<?php
require_once('AtropaToolbox/php_proxy/proxy.php');
$proxy = new atropa_proxy();
if($proxy->process_request() === false) {
return false;
}
?>
AtropaToolbox/php_proxy/proxy.php
Extends atropa_mod_proxy to redefine the handlers.
<?php
require_once('AtropaToolbox/php_proxy/mod_proxy.php');
class atropa_proxy extends atropa_mod_proxy
{
protected function local_resource_handler() {
return false;
}
protected function external_resource_handler() {
$ext = $this->get_page();
//echo '<pre>' . print_r($ext, true) . '</pre>';
//$ext['page'] = preg_replace('/<a /', '<p ', $ext['page']);
$this->show_page($ext);
}
}
?>
AtropaToolbox/php_proxy/mod_proxy.php
The generic router script
<?php
/**
* Rev. 1 Atropa mod_proxy for php built in webserver
*/
class atropa_mod_proxy
{
protected function is_external_resource() {
$host = parse_url($_SERVER['REQUEST_URI'], PHP_URL_HOST);
if(isset($host) && $host !== $_SERVER['SERVER_NAME']) {
return true;
} else {
return false;
}
}
protected function local_resource_handler() {
return false;
}
protected function external_resource_handler() {
$ext = $this->get_page();
$this->show_page($ext);
}
public function process_request() {
if($this->is_external_resource()) {
return $this->external_resource_handler();
} else {
return $this->local_resource_handler();
}
}
public function get_request_headers() {
$arr = array();
foreach($_SERVER as $svar => $sval) {
if(substr($svar, 0, 4) === 'HTTP') {
$svar = substr($svar, 5);
$svar = preg_replace('/_/', ' ', $svar);
$svar = ucwords(strtolower($svar));
$svar = preg_replace('/ /', '-', $svar);
$arr[$svar] = $sval;
}
}
return $arr;
}
public function pack_request_headers($headers_array) {
$packed = '';
foreach($headers_array as $header_name => $header_value) {
$packed .= $header_name . ': ' . $header_value . "\r\n";
}
return $packed;
}
public function echo_response_headers($http_response_header_array) {
foreach($http_response_header_array as $val) {
if(strpos(strtolower($val), 'connection') !== 0) {
header($val);
}
}
}
protected function get_page() {
$request_headers = $this->get_request_headers();
$request_headers = $this->pack_request_headers($request_headers);
$method = $_SERVER["REQUEST_METHOD"];
$scheme = parse_url($_SERVER['REQUEST_URI'], PHP_URL_SCHEME);
$opts = array(
$scheme => array(
'method' => $method,
'header' => $request_headers
)
);
if(count($_POST) > 0) {
$content = http_build_query($_POST);
$opts[$scheme]['content'] = $content;
}
$context = stream_context_create($opts);
$ext = array();
$ext['page'] = file_get_contents($_SERVER['REQUEST_URI'], false, $context);
$ext['http_response_header'] = $http_response_header;
return $ext;
}
protected function show_page($ext) {
header_remove();
$this->echo_response_headers($ext['http_response_header']);
echo $ext['page'];
}
}
?>
Please read on Socket Programming with PHP and An Introduction to Sockets in PHP. You will be running a PHP Daemon to make PHP listen to a port and process all the requests.
In order to do this, you might want to look at http://sourceforge.net/projects/poxy/ so you can base your PHP Daemon there. You also have to learn how proxies communicate with clients. Poxy is a web-based application, you enter a URL and it loads it for you. It wouldn't allow you to listen to ports and it's not a php daemon so you will have to do a lot of coding for your PHP daemon proxy.

json parser empty result

I'm new in gwt and new to using Firebug. MY gwt version is 2.0.0. using eclipse and WAMP. my IIS is stoped to run WAMP apache. I run my program on firefox
I have valid json result from tesdb3.php located in "http://localhost/phpmyadmin/tesdb3/datauser.php"
{"item": [{"kode":"002","nama":"bambang gentolet"},
{"kode":"012","nama":"Algiz"}]}
I add the xml with
<inherits name='com.google.gwt.json.JSON'/>
<inherits name="com.google.gwt.http.HTTP" />
then I try to show it in the gwt with this code.
public class Tesdb3 implements EntryPoint {
String url= "http://localhost/phpmyadmin/tesdb3/datauser.php";
public void LoadData() throws RequestException{
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
builder.sendRequest(null, new RequestCallback(){
#Override
public void onError(Request request, Throwable exception) {
Window.alert("error " + exception);
}
public void onResponseReceived(Request request,
Response response) {
if (200 == response.getStatusCode()) {
Window.alert("ok -" + response.getText() + "-" + response.getStatusCode());
} else {
Window.alert("error2 -" + response.getText()+ response.getStatusText() + "-" + response.getStatusCode());
}
}
});
}
public void onModuleLoad() {
try {
LoadData();
} catch (RequestException e) {
e.printStackTrace();
}
}
}
I run it in development mode. not hosted mode.
My code didn't show any error. But the result in window alert is "error2 --OK-0".
result Net from firebug is 7 request:
get Tesdb3.html?gwt.codeserv = 200ok
get Tesdb3.css = 200ok
get tesdb3.nocache.js = 200ok
get hosted.html?tesdb3 = aborted
get standard.css = 304 not modified
get hosted.html?tesdb3 = 403 not modified
get datauser.php = 200ok
My question is:
Why the response status code is 0, and the response status text is 'OK'? there was no error in json or Java code.
Why response.getText is empty? Why I can't get any json result even a single character?
Please help me. already 2 months I try to solve this with many source type and I can't get a single result.
This is my datauser.php
header('Content-type: application/json; charset=UTF-8');
header('Cache-Control: no-cache');
header('Pragma: no-cache');
$link = mysql_connect("localhost", "root", "")
or die("Could not connect : " . mysql_error());
mysql_select_db("tesku1") or die("Could not select database" . mysql_error());
$query = "select * from tabel1";
$result = mysql_query($query);
$jumlah_data = mysql_num_rows($result);
echo '[';
for ($i=0; $i<=count($result); $i++){
$row = mysql_fetch_array($result);
echo '{';
echo "\"kode\":\"$row[kode]\",";
echo "\"nama\":\"$row[nama]\"";
if ($i==count($result)){
echo '}';
}else
echo '},';
}
echo ']';
mysql_free_result($result);
I know this is an old post, but I intend to post an answer to all who are having this problem currently.
The cause of this problem is SOP (Same Origin Policy). This problem arises from the fact that PHP script is not in the same domain as your GWT or JavaScript web application.
The solution is quite simple just add a new header to your PHP script like this:
header('Access-Control-Allow-Origin: *');
this will tell GWT that the domain (site) from where the php script runs accepts requests from any other domain (sites).
To restrict request to a given site just add the following header:
header('Access-Control-Allow-Origin: http://mysite.com');
Where a java script from http://mysite.com makes the http request.
Well, looks like the problem is SOP(Same Origin Policy), and cross site request. From what I get(not detailed though) if the request is cross-site, RequestBuilder can't be used.For exchange, use getJson() and JSNI overlay types. All example from this tutorial : http://code.google.com/webtoolkit/doc/latest/tutorial/Xsite.html. I change the price value into my database value. At the end..my database showing up in my browser(yeah!yeahhh! (T-T)).
I have the same problem. What we are trying to do should not be a SOP problem because both pages are in the same computer. The problem is about the execution that eclipse do when we are testing the web. To solve the problem, copy the war folder into htdocs and run it using your internet explorer and you will check that your code is right. There is the possibility to config the run in eclipse, but I don't know how yet.

Categories