I've installed the following git: Flynsarmy/PHPWebSocket-Chat on my server, and successfully set up the chat, it works fine, and users can communicate with each other well.
Last 24 hours i tried sending a System message from another php file:
$host = '89.163.140.48'; //where is the websocket server
$port = 9300;
$local = "http://www.indiamea.com"; //url where this script run
$data = '{"message":"TEST - MESSAGE - TEST"}'; //data to be send
$head = "GET /?token=$jwt HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host:$port"."\r\n".
"Sec-WebSocket-Key: Bom4DUh5Brl8xmvUYbDQzA=="."\r\n".
"Sec-WebSocket-Version: 13"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
//WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 15);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, $data ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff"
fclose($sock);
Websocket is accepting the request (open event), but the message event is not fired inside the server.php script, which means that message is not sent to users.
I tried googling, but i couldn't find any solution.
I really hope that someone can help me with this.
If server.php code is required, you can acess it here: https://github.com/Flynsarmy/PHPWebSocket-Chat/blob/master/server.php
You can't do what you're trying to do directly.
You can do it indirectly though – I've done this before commercially and the solution we went with was database polling.
So what you do is you create a loop in the websocket server when it starts, which runs every few seconds (say 10 seconds) to check a database table for messages flagged as unsent. If it finds one then it sends it and flags it as sent.
Your PHP backend can write to this table of course, so the database acts as a mediator between your websocket client/server and your PHP back-end.
However if your websocket server is already running a while loop then this won't be possible without a separate thread.
Related
I'm using codeIgniter RESTful API (https://github.com/philsturgeon/codeigniter-restserver) that return information (json format) to my android/iphone app.
There are an operation where i send some values, if it is everything OK i return 200 code as response.
Now, i want to add a new operation at the same method: send notifications of this modifications with APNS (Apple Push Notificacion Service) and GCM (Google Cloud Messaging).
It works well when i have to send no more than 3-5 notifications, the problem is APNS, because i have to send this messages one by one and it takes a long time, so my apps recieves a timeout exception (all the notifications are sent but the user get the Error Connection...)
Can i send the 200 code response and then continue sending this notifications? (Something like this...)
function my_update_method_post(){
//....GET my POST values
update($data);
$this->response(array('result'=>1),200));
//Send Notifications
....
}
Thanks in advance...
I found a solution that works perfect for me because i don't expect any result value. If notification can't be send...i log it in my database.
This is the function that i use to send "async" request (yes, This is not an asynchronous request, but it works how i'm looking for)
function curl_post_async($url, $params)
{
$post_string = http_build_query($params);
$parts=parse_url($url);
$fp = fsockopen($parts['host'],
isset($parts['port'])?$parts['port']:80,
$errno, $errstr, 30);
if(!$fp)
{
//Perform whatever logging you want to have happen b/c this call failed!
}
$out = "POST ".$parts['path']." HTTP/1.1\r\n";
$out.= "Host: ".$parts['host']."\r\n";
$out.= "Content-Type: application/x-www-form-urlencoded\r\n";
$out.= "Content-Length: ".strlen($post_string)."\r\n";
$out.= "Connection: Close\r\n\r\n";
if (isset($post_string)) $out.= $post_string;
fwrite($fp, $out);
fclose($fp);
}
Yes this is possible.
You should look at PHP exec() and this link. You should set up a function in your controller to be called from the command line. you will then pass in an array of the GCM/APNS data to be used.
This solution is not ideal because you won't be able to tell the client that all message were sent successfully. You will send back 200 to say the request was received ok and that is all.
Since PHP doesn't natively support threads or asynchronus function calls you will have to use a kindof hacky solution.
Have a look at my question here: PHP file_get_contents() follow Content-length header
The Solution is to send a Connection: Close and Content-Length header, then make the client to be aware of these headers (see link above). In case of curl for example the connection will be closed as soon as the Content-Length is reached, but your PHP Script still runs "in the background" so you can start time consuming operations then.
Kind regards,
Stefan
P.S. If the Script takes really long to execute, make sure that the PHP max exection time doesn't get in your way
Take a look at this article. I like this solution much more than one where you have the client tell the server to hang up immediately; there are multiple benefits if you build this solution on the server side.
You know the server will continue processing once the client has disconnected
The client can still receive a response from the server
EDIT
I'd not realized OP doesn't have access to the service here. In this case, the article I've mentioned is of little value. The problem here is the server is taking a long time to respond and hanging the client up. For this I suggest curl_multi_init. This allows you to make a number of requests simultaneously.
I'm putting a paypal checkout onto my website but am falling down with the listener.
For those of you who are unfamiliar with the Paypal IPN system, basically Paypal sends your script with a message about the transaction, which you send back with a couple of bits added. If Paypal receives the correct reply, it'll reply with 'VERIFIED', and if not it'll say 'INVALID'.
I've succeeded with the first bit. My code is able to receive the info from paypal, add on the extras and post it back. However, I get no response from the Sandbox saying either 'VERIFIED' or 'INVALID'. I've pretty much copied my code from the paypal website so I was hoping this was going to be fairly straightforward, so if you could take a minute to look at my code, perhaps some new eyes could pick out where I've gone wrong.
Here's the code. Nothing special, it literally just gets the info, adjusts it, passes it back and reads the response (which it either isn't getting or doesn't realise it's getting)
<?php
$debug=true;
//Put together postback info
$postback = 'cmd=_notify-validate';
foreach($_POST as $key =>$value){
$postback .= "&$key=$value";
}
// build the header string to post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sandbox.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);//open the connection
if(!$fp){ //no conn
die();
}
//post data back
fputs($fp, $header . $postback);
while(!feof($fp)){
$res=fgets ($fp, 1024);
if((strcmp($res, "VERIFIED")) == 0){ //verified!
if($debug){
$filename = 'debug/debug5_verified.txt'; //create a file telling me we're verified
$filehandle=fopen($filename, 'w');
fwrite($filehandle,'VERIFIED!');
fclose($filehandle);
}
}
}
?>
Thanks in advance!
Switch over to using the HTTPS url, I'm not sure when but recently all of my test scripts started failing on the plain HTTP version. They look to be migrating over.
I'm using the same paypal sample code you are:
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
or
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
So I think I found a solution. Turns out it wasn't having trouble with connecting to ssl://sandbox...., it was actually retrieving the answer. The code was getting hung up on the
while(!feof($fp)){
$res=fgets($fp,1024);
}
bit. All I did was replace it with:
$res=stream_get_contents($fp, 1024);
and it worked first time! Now I can get on with my life. Thanks again for all the help on this one.
Perhaps the original code was missing:
$header .= "Connection: close\r\n\r\n";
Note that the Paypal sample code uses HTTP/1.0 so does not have that line. And HTTP/1.1 is fine but might need the line.
On another issue, Sandbox may no longer support port 80. I am getting a 302 redirect to https://www.sandbox.paypal.com.
I've noticed that the URL you are posting to is a little different than below, could this be it?
this is from the IPN Testing help page:
Check that your are posting your response to the correct URL, which is https://www.sandbox.paypal.com/cgi-bin/webscr or https://www.paypal.com/cgi-bin/webscr, depending on whether you are testing in the Sandbox or you are live, respectively.
Verify that your response contains exactly the same IPN variables and values in the same order, preceded with cmd=_notify-validate.
Ensure that you are encoding your response string and are using the same character encoding as the original message.
EDIT: Sorry I also wanted to mention that the port for HTTP and HTTPS are different, 80 as opposed to 443. I'm not too familiar with Paypal API but could look into it as I see you are using 80.
PayPal test server moved to:
$fp = fsockopen('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30);
check up php version tls socket. it should be tls 1.2 to get the response from sandbox account. upgrade the php version to 5.5 to get tls 1.2 socket.
paypal has disabled the service of sslv3 and changed to tls 1.2.
if you need to get the response,php version must require tls 1.2, in order to get tls 1.2 php can be upgraded to 5.5 or more.
visit the link.
I'd like to run a particularly expensive fql query in the background, log results to the database, and retrieve it later without the user having to wait for each step.
Can you share an example of how to run a facebook request asynchronously?
main.php
$uid = $facebook->getUser();
if ($uid) {
try {
echo $user;
////////////////////////////////////////////////////////
// Run lengthy query here, asynchronously (async.php) //
////////////////////////////////////////////////////////
// //
// For example: $profile = $facebook->api('/me'); //
// (I know this request doesn't take long, but //
// if I can run it in the background, it'll do. //
// //
////////////////////////////////////////////////////////
} catch (FacebookApiException $e) {
echo $e;
}
}
async.php
$profile = $facebook->api('/me');
$run = mysql_query("INSERT INTO table (id) VALUES (" . $profile['id'] . ");";
complete.php
echo getProfileId(); // assume function grabs id from db, as stored via async.php
My solution to running PHP jobs in the background is just to have the script itself make a new request which executes the actual job.
The code I've used before is the following, I'm not sure if there are more elegant solutions... but it has worked more than well enough for me in the past. The reason I use fsock and not file_get_contents() etc is of course so that I won't have to wait for the job to finish (which would defeat the purpose)
$sock = fsockopen("127.0.0.1", 80);
fwrite($sock, "GET /yourjoburl/ HTTP/1.1\r\n");
fwrite($sock, "Host: yourdomain.com\r\n");
fwrite($sock, "Connection: close\r\n");
fwrite($sock, "\r\n");
fflush($sock);
fclose($sock);
So, then you just have the other script write the results and progress to a database or whatever... also remember that MySQL supports mutexes, which means you can easily prevent multiple jobs from running at the same time... or to allow other scripts to wait for the job to finish.
PS. The reason I personally avoid exec and all that stuff is that it just seems like a hassle to work with, different servers, different setups, different OSes, etc. This works the same on all hosts that allow you to open sockets. Although you might want to add a private key to the request that you verify in the job, or check the IP of the caller, to prevent others from being able to start jobs.
EDIT: This is untested but should work if you want to forward the cookies as well, including the session cookie.
$sock = fsockopen("127.0.0.1", 80);
fwrite($sock, "GET /yourjoburl/ HTTP/1.1\r\n");
fwrite($sock, "Host: yourdomain.com\r\n");
fwrite($sock, "Cookie: " . $_SERVER['HTTP_COOKIE'] . "\r\n");
fwrite($sock, "Connection: close\r\n");
fwrite($sock, "\r\n");
fflush($sock);
fclose($sock);
I have a situation where I need to update one browser window based on input from the other. Right now I'm using WebSockets and it's working great.
Now I want to send data to the WebSocket using PHP instead of the browser (so instead of ws://, use PHP code). In other words, I want to simulate the WebSocket.send() call using PHP instead of JavaScript.
I have the following code which doesn't seem to work (the onmessage is not being called):
if (
function_exists('socket_create') AND
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) AND
$sock_data = socket_connect($sock, "127.0.0.1", 12345)
) {
$msg = "hello world";
$sock_data = socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 1); //Set
$sock_data = socket_write($sock, $msg, strlen($msg)); //Send data
socket_close($sock); //Close socket
}
Here's how it's done:
http://permalink.gmane.org/gmane.comp.lang.javascript.nodejs/18088
$host = 'localhost'; //where is the websocket server
$port = 9000;
$local = "http://localhost/"; //url where this script run
$data = 'hello world!'; //data to be send
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
//WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff"
fclose($sock);
In order to send data to the socket, you need to use fsockopen to open the connection to the socket at specified port. If the connection is successfully, all you need to do is use fwrite
However, you are going to be sending the data to the WebSocket server. The server will treat you as a client, and since you are not providing HTTP headers it expects for successful authentication - your connection will be refused.
Since you didn't say who is supposed to receive the message you are trying to send (all users or a specific user or something entirely different), without knowing what your goal is - it's hard to explain any further what you should do.
There is a lot more to WebSockets than just sending the raw data to a TCP socket.
Ok, to start, you're using a UDP socket, where WebSockets use TCP. WebSockets is an entire protocol for communication, similar to HTTP, so you need to follow that protocol, there is a handshake step that you need to perform first and headers you need to add to all communication. It's not difficult, but I'm not going to go into detail here.
You have two options from here, implement the WebSockets protocol in php, or use a pre-built library like this one: http://code.google.com/p/phpwebsocket/
I'm not being rude, or mean, but in the future, try a quick Google search. That library I linked was found after googling "PHP WebSockets".
The most important part is that the message needs to be sent on the existing socket, meaning you cant call socket_connect, fsockopen, or any other function within PHP that will attempt an unsolicited connection to the client. This isn't a websocket thing - that's a fundamental concept in network programing.
On phpwebsocket it would be somethin like:
$msg = "hello world";
$phpwebsocket->send($user->socket, $msg);
where '$phpwebsocket' is the PHP WebSocket object, $user->socket is a connected user who connected priory using with a javascript WebSocket(), and send() is a method within the WebSocket object that will properly encode the message into a frame (or should as it will soon be required).
However, if for any reason you want to connect to the websocket server using websockets from PHP, you'll want to check out https://github.com/nicokaiser/php-websocket. The server in the link wont be of any importance if your happy with your current solution, but the package also contains a PHP Websocket client class which is what you would need.
Checkout ratchet
You can use something like telnet with popen/proc_open to communicate with the socket server.
<?php
set_time_limit(0);
$errorArr = array();
if (!isset($argv[1]))
{
array_push($errorArr, "You forgot to enter a host.");
}
if ((isset($argv[1])) AND (!filter_var($argv[1], FILTER_VALIDATE_IP)))
{
array_push($errorArr, "The host you entered is not a valid IP address.");
}
if (!isset($argv[2]))
{
array_push($errorArr, "You forgot to select a port.");
}
if (!empty($errorArr))
{
echo "You have the following errors:\n";
print_r($errorArr);
die("Syntax is as follows: php {$argv[0]} host port\n");
}
$host = $argv[1];
$port = $argv[2];
echo ":::Connecting...\n";
$fh = fsockopen($host, $port);
if (!$fh)
{
die(":::Connection failed.\n:::Aborting.\n");
}
echo ":::Connected!\n:::Sending headers.\n";
$header = "PROPFIND /webdav/ HTTP/1.1\r\n";
$header .= "Host: {$host}\r\n";
$header .= "User-Agent: BitKinex/3.2.3\r\n";
$header .= "Accept: */*\r\n";
$header .= "Pragma: no-cache\r\n";
$header .= "Cache-Control: no-cache\r\n";
$header .= "Depth: 1\r\n";
$header .= "Content-Length: 220\r\n";
$header .= "Content-Type: text/xml\r\n\r\n\r\n";
if (!fwrite($fh, $header))
{
die(":::Couldn't send headers. Aborting.\n");
}
$exHeader = explode("\r\n", $header);
foreach ($exHeader as $ecHeader)
{
echo "<<<{$ecHeader}\n";
}
echo "\n:::Retrieving syntax...\n";
while(1)
{
while ($data = fgets($fh, 512))
{
echo ">>>{$data}";
flush();
}
}
?>
I'm working on a script to connect to WebDAV, upload a file, and disconnect. It connects and sends headers fine, but then it takes forever to retrieve syntax. At times, it takes several minutes, and I can't understand why. Is it a problem in my code?
And yes, I realize there's an infinite while loop there. That's done on purpose, because I haven't figured out how to know when the server is done sending information to me. So I guess that's another question, if anyone could provide insight to that.
Thanks
Your problem is because you are sending the Content-Length header with a value of 220, while not sending any content at all. The server hangs in there expecting content, but it never arrives...
And for your infinite loop thing, you don't need it at all. fgets will return false if the connection has closed. Send the Connection: close header to tell Apache to end the connection after the data has been sent. Your while loop will evaluate to false when the data has been read entirely and the connection has closed, and your loop will exit.
You might want to test it using cURL then. Try this one out: http://curl.haxx.se/mail/archive-2006-02/0000.html
That way you can see if it's server side or code side.
WebDAV can CHUG if the machine you are connecting to handles lots of traffic in general. And especially lots of web traffic. The reasons are complex, but solutions I have used in the past have primarily involved coding around the delay. Either by dumping things in a line to wait, or by pushing things to a box that isn't under heavy load but is more directly connected to the server in question and can push the files to it via different means.
This all requires access, however, and if you have control over the machines you are connecting to, you should be able to reconfigure them to give yourself priority. (which may not be an option if you are connecting to a production web server) However, I've never had to deal with this in PHP. So the problem certainly could be caused by other reasons.