We're using a system at the moment that takes an incoming JSON request over TCP and responds using JSON too. Currently I've set up my socket like so in PHP:
$socket = fsockopen($host, $port, $errno, $errstr, $timeout);
if(!$socket)
{
fwrite($socket, $jsonLoginRequest); // Authentication JSON
while(json_decode($loginResponse) == false) // We know we have all packets when it's valid JSON.
{
$loginResponse .= fgets($socket, 128);
}
// We are now logged in.
// Now call a test method request
fwrite($socket, $jsonMethodRequest);
while(json_decode($methodResponse) == false) // We know we have all packets when it's valid JSON.
{
$methodResponse .= fgets($socket, 128);
echo $methodResponse; // print response out
}
// Now we have the response to our method request.
fclose($socket);
}
else
{
// error with socket
}
This works at the moment, and the server responds to the method request. However, some methods will respond like this to acknowledge the call, but will also respond later on with the results I'm after. So what I really need is a TCP listener. Could anyone advise how I could write a TCP listener using fsock like I have above?
Thanks
To create a listening socket use the following functions:
socket_create_listen (this one is cool)
socket_accept
I'm not shure if fwrite()/fread() are working with those sockets otherwise you have to use the following functions:
socket_recv
socket_send
Message-loop
I have now written some function to read a single JSON responses with the assumption that multiple responses are separated by CRLF.
Here's how I would do it (assuming your php-script has unlimited execution time):
// ... your code ...
function readJson($socket) {
$readData = true;
$jsonString = '';
while(true) {
$chunk = fgets($socket, 2048);
$jsonString .= $chunk;
if(($json = json_decode($jsonString)) !== false) {
return $json;
} elseif(empty($chunk)) {
// eof
return false;
}
}
}
// ....
// Now call a test method request
fwrite($socket, $jsonMethodRequest);
$execMessageLoop = true;
while($execMessageLoop) {
$response = readJson($socket);
if($response === false) {
$execMessageLoop = false;
} else {
handleMessage($socket, $response);
}
}
function handleMessage($socket, $response) {
// do what you have to do
}
Now you could implement the "handleMessage" function which analyses the response and acts to it.
Related
This Is my code to connect java socket :-
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 12345);
while(true)
{
// read a line from the socket
$line = socket_read($socket, 1024, PHP_NORMAL_READ);
var_dump($line);
$someArray = json_decode($line, true);
$otp = $someArray["otp"];
if($someArray["msg"] == "otp_generation")
{
$myObj = new \stdClass();
$myObj->msg = "OTP RECEIVED NEED TO CONNECT";
$send = json_encode($myObj);
socket_send($socket, $send, strlen($send), 0);
}
exit;
}
My Question is -
When connection is established successfully server send one OTP to client and received successfully in client. Then i send data to server OTP RECEIVED acknowledgement, it also received in server. After OTP RECEIVED acknowledgement server send welcome msg to client. I cant get the welcome message. if i remove the "exit" code browser is still loading, finally crashing. Why i didn't receive the second data. anyone solve my issue. what i need to modify. am beginner for socket.
I need to display Welcome msg. What can i do?
You need to continue looping and read the next message, then break out of the loop.
while(true)
{
// read a line from the socket
$line = socket_read($socket, 1024, PHP_NORMAL_READ);
var_dump($line);
$someArray = json_decode($line, true);
if($someArray["msg"] == "otp_generation")
{
$otp = $someArray["otp"];
$myObj = new \stdClass();
$myObj->msg = "OTP RECEIVED NEED TO CONNECT";
$send = json_encode($myObj);
socket_send($socket, $send, strlen($send), 0);
} elseif ($someArray["msg"] == "welcome") {
// do whatever you need to do with the rest of the message
break; // then get out of the loop
} else {
echo "Unknown message received";
var_dump($someArray);
break;
}
}
I had to make a guess about how the welcome message is formatted, but this should give you the general idea.
Without new line cmd data is not send. This is the mistake i done. Finally i got the answer from my friend.
I just add the line below;-
socket_send($socket, $send."\r\n", strlen($send."\r\n"), 0);
Thanks #hemanth kumar and #Barmar
$ip="****"; //Set the TCP IP Address to connect too
$port="8088"; //Set the TCP PORT to connect too
$command="hi"; //Command to run
$req['path'] = $path;
$post = json_encode($req);
//Connect to Server
$socket = stream_socket_client("tcp://{$ip}:{$port}", $errno, $errstr, 30);
if($socket) {
//Start SSL
stream_set_blocking ($socket, true);
stream_socket_enable_crypto ($socket, true, STREAM_CRYPTO_METHOD_SSLv3_CLIENT);
stream_set_blocking ($socket, false);
//Send a command
fwrite($socket, $post);
$buf = null;
//Receive response from server. Loop until the response is finished
while (!feof($socket)) {
$buf .= fread($socket, 20240);
}
//close connection
fclose($socket);
echo "<pre>";
print_r($buf); exit;
//echo our command response
return json_decode($buf);
}
This is my code.
this code is working fine for below 8192 bytes value. but
it can't get the above 8192 bytes what i need to get above this bytes.
because i need get the more bytes of data here
Please provide one example
Thanks in advance
When you write to a network stream, a single call to fwrite() does not necessary writes the whole data.
There is a note at http://php.net/manual/en/function.fwrite.php which says:
Writing to a network stream may end before the whole string is
written. Return value of fwrite() may be checked:
<?php
function fwrite_stream($fp, $string) {
for ($written = 0; $written < strlen($string); $written += $fwrite) {
$fwrite = fwrite($fp, substr($string, $written));
if ($fwrite === false) {
return $written;
}
}
return $written;
}
?>
You may wonder, where does this 8192 number come from.
It seems like this is the default chunk size for the stream.
You can check and change chunk size via stream_set_chunk_size() function
You have set the socket in non-blocking mode:
stream_set_blocking ($socket, false);
In non-blocking mode, you should wait for data availability on the socket before trying to perform read operations. PHP provides stream_select function for this purpose.
So you should whether make the socket blocking, or handle events with stream_select.
We're working on a SEO related script in PHP, and we need to run different modules (each one of them are a file .php) at the same time once we finish with the crawling process. In other words, we need to execute more than 10 .php files, in parallel.
The application used to work with a sequence, so once when one script was ending, the user's browser was forwarded into the next one. Each one of the scripts is establishing a connection to the database, and sending different HTTP packets to the crawled web application.
I understand that this could be approached using popen? Is there any way to receive information from each one of this modules into the main script that triggers them? Could anyone provide a very short snippet to see how this would work?
try this technique for running multiple parallel jobs in PHP. In this example, we have two job files: j1.php and j2.php we want to run. The sample jobs don't do anything fancy. The file j1.php looks like this:
$jobname = 'j1';
set_time_limit(0);
$secs = 60;
while ($secs) {
echo $jobname,'::',$secs,"\n";
flush(); #ob_flush(); ## make sure that all output is sent in real-time
$secs -= 1;
$t = time();
sleep(1); // pause
}
The reason why we flush(); #ob_flush(); is that when we echo or print, the strings are sometimes buffered by PHP and not sent until later. These two functions ensure that all data is sent immediately.
We then have a 3rd file, control.php, which does the coordination of jobs j1 and j2. This script will call j1.php and j2.php asynchronously using fsockopen in JobStartAsync(), so we are able to run j1.php and j2.php in parallel. The output from j1.php and j2.php are returned to control.php using JobPollAsync().
#
# control.php
#
function JobStartAsync($server, $url, $port=80,$conn_timeout=30, $rw_timeout=86400)
{
$errno = '';
$errstr = '';
set_time_limit(0);
$fp = fsockopen($server, $port, $errno, $errstr, $conn_timeout);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
return false;
}
$out = "GET $url HTTP/1.1\r\n";
$out .= "Host: $server\r\n";
$out .= "Connection: Close\r\n\r\n";
stream_set_blocking($fp, false);
stream_set_timeout($fp, $rw_timeout);
fwrite($fp, $out);
return $fp;
}
// returns false if HTTP disconnect (EOF), or a string (could be empty string) if still connected
function JobPollAsync(&$fp)
{
if ($fp === false) return false;
if (feof($fp)) {
fclose($fp);
$fp = false;
return false;
}
return fread($fp, 10000);
}
###########################################################################################
if (1) { /* SAMPLE USAGE BELOW */
$fp1 = JobStartAsync('localhost','/jobs/j1.php');
$fp2 = JobStartAsync('localhost','/jobs/j2.php');
while (true) {
sleep(1);
$r1 = JobPollAsync($fp1);
$r2 = JobPollAsync($fp2);
if ($r1 === false && $r2 === false) break;
echo "<b>r1 = </b>$r1<br>";
echo "<b>r2 = </b>$r2<hr>";
flush(); #ob_flush();
}
echo "<h3>Jobs Complete</h3>";
}
Good Read
Divide-and-conquer and parallel processing in PHP
from the source
If the various files in PHP have no dependency, I think you can use a multi-curl approach which can be implemented as shown :-
$linkArray = array('file1.php', 'file2.php','file3.php','file4.php','file5.php');
$nodes = ($linkArray);
$node_count = count($nodes);
$curl_arr = array();
$master = curl_multi_init();
$counter = 0;
for($i = 0; $i < $node_count; $i++)
{
$url =$nodes[$i];
$curl_arr[$i] = curl_init($url);
curl_setopt($curl_arr[$i], CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($master, $curl_arr[$i]);
}
do {
curl_multi_exec($master,$running);
} while($running > 0);
for($k=0;$k<$node_count;$k++){
$result = curl_multi_getcontent ($curl_arr[$k]); // contains the output of individual files
}
I'm trying to integrate proxy usage (with authentication) into a script that queries whois data.
What I'm trying to do is
1) Connect to the proxy IP and port
2) authenticate a username and password
3) connect to the whois server and send domain details, receiving the request in return.
I have the script working without proxies
private function whois($domeinnaam, $whoisrule)
{
list ($server, $poort, $domein, $vrij) = $whoisrule;
$domein = str_replace("{domein}", $domeinnaam, $domein);
$fp = fsockopen($server, $poort);
if($fp)
{
fputs($fp, $domein."\r\n");
$data = "";
while(!feof($fp))
{
$data .= fread($fp, 1000);
}
fclose($fp);
}
else
{
$data = "error";
}
// Cache whois data
$this->_whoisdata[$domein] = $data;
return $data;
}
But does anyone how I would integrate a proxy server and authentication into this code?
cURL has some handy CURLOPT_PROXY* options. This answer shows how to use them.
I am testing integrating a website to Paypal, using Web Payments standard and HTML Variables.
I have written a simple PHP script to handle the IPN notifications.
According to the Paypal documentation, the Paypal server responds with a simple 'VERIFIED' or 'INVALID' response, once you ping the received data back to Paypal.
In my handler, I am doing a case sensitive string comparison for those two keywords, if either one of these known keywords is not found, then it is treated as an error.
<?php
$fp = fsockopen ($socket_url, 80, $errno, $errstr, 10);
if (!$fp){
// SOCKET ERROR
return false;
}
else {
fputs ($fp, $header . $req);
$is_ok = false;
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp("VERIFIED",$this->ipn_response)==0) {
//do something ...
}
// if the IPN POST was 'INVALID'
else if (strcmp ($res, "INVALID") == 0) {
fclose ($fp);
return false;
}
else {
echo "Unknown response from Paypal: $res";
fclose ($fp);
return false;
}
}
fclose ($fp);
return true;
}
?>
My error message shows that I am receiving an 'HTTP/1.1 200 OK' response from Paypal.
Unknown response from Paypal:
'HTTP/1.1 200 OK'
Has the PayPal API changed, or am I doing something wrong?
PayPal's response is in the body of the HTTP response. You need to process the HTTP headers before you can get at the body. Or, alternately, just keep reading lines until you find a blank line, and the next line will be the body.
Sorry, my original response was really badly worded :)
fgets is only reading line by line, the first line being the HTTP response. Your code never gives the opportunity to continue reading, though, as you 'error out' after the first line is processed.
I'd be inclined to replace your while loop with something like:
while (!feof($fp)) {
$res = trim(fgets($fp, 1024));
if (strcmp($res, "VERIFIED")==0) {
//do something ...
}
else if (strcmp($res, "INVALID") == 0) {
fclose ($fp);
return false;
}
}
As #Mike said, you've to read the whole body - in your case you're just testing if VERIFIED or INVALID appears in the first 1024 bytes of the reply, which is probably the header.
By the way, you don't need to use fsockopen(), most people do because PayPal provides an example using it so that it works in old box setups. CURL or file_get_contents with an HTTP POST context will do the job just fine, here is an example I coded for the phunction PHP framework:
public static function PayPal($email, $status = 'Completed', $sandbox = false)
{
if (preg_match('~^(?:.+[.])?paypal[.]com$~', gethostbyaddr($_SERVER['REMOTE_ADDR'])) > 0)
{
$url = ($sandbox !== true) ? '' : '.sandbox';
$result = self::CURL('https://www' . $url . '.paypal.com/cgi-bin/webscr/', array_merge(array('cmd' => '_notify-validate'), $_POST), 'POST');
if ($result == 'VERIFIED')
{
$email = strlen($email) * strcasecmp($email, $_POST['receiver_email']);
$status = strlen($status) * strcasecmp($status, $_POST['payment_status']);
if (($email == 0) && ($status == 0))
{
return true;
}
}
}
return false;
}