I have some Apache Thrift (v.0.6.1) test application with perl-server and php-client.
The behaviour I cannot explain: If we call server-method with invalid argument we see the error in server-output, but php-client stays waiting the response infinitely.
Here are the sources of server:
sub new {
my $classname = shift;
my $self = {};
return bless($self,$classname);
}
sub DateToTimestamp
{
my ($self, $date) = #_;
my $result = CommonAPI::DateToTimestamp($date);
return $result;
}
eval {
my $handler = new RPCHandler;
my $processor = new RPCPerformanceTest::RPCPerformanceTestProcessor($handler);
my $serversocket = new Thrift::ServerSocket(9091);
my $forkingserver = new Thrift::ForkingServer($processor, $serversocket);
print "Starting the server...\n";
$forkingserver->serve();
print "done.\n";
}; if ($#) {
if ($# =~ m/TException/ and exists $#->{message}) {
my $message = $#->{message};
my $code = $#->{code};
my $out = $code . ':' . $message;
die $out;
} else {
die $#;
}
}
and client:
try {
$socket = new TSocket($server_host, $server_port);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$client = new RPCPerformanceTestClient($protocol);
$transport->open();
$start = microtime(true);
$result = $client->DateToTimestamp('071/26/2011 01:23:45');
var_dump($result);
} catch (Exception $e) {
echo 'Exception: <b>' . $e->getMessage() . '</b>';
}
Why is this happening? Is it my fault? Is it expected behavour?
The Thrift PHP library is a bit broken. You need to manually set the timeouts
E.g.
$socket = new TSocket('host', 9095);
$socket->setSendTimeout(60000);
$socket->setRecvTimeout(60000)
This happens often with protocols that do not supply message length: a client sends more data then the server expects and waits for the server to receive the data. The server receives some of the data, tries to parse it and fails. Now the server-side of the protocol is in errorneous state. If it continues to read the data, it may block. Most probably, the server-side has sent you some error response and is waiting at the same time for the client to receive the response, but that will never happen too.
This is my guess. The best strategy IMHO is to set a time-out for both client and server sockets.
Related
My problem is when I try to connect the Android application with Java socket identified by an IP and a port 4444 with a PHP service, identified by the same port, and keep this connection open to accept multiple connections via the Android app.
So with a second Android device if we connect to PHP we will have the same communication channel as the one device and we cannot identify which device is connected via PHP.
Is there a possibility to differentiate these two connections via the PHP file or instantiate two different PHP service for the two devices.
The Android code:
public String connect() {
Socket socket = null;
try {
socket = new Socket();
// This limits the time allowed to establish a connection in the case
// that the connection is refused or server doesn't exist.
socket.connect(new InetSocketAddress(dstAddress, dstPort), context.getResources().getInteger(R.integer.time_out));
// This stops the request from dragging on after connection succeeds.
socket.setSoTimeout(context.getResources().getInteger(R.integer.time_out));
// socket = new Socket();
// socket.connect(new InetSocketAddress(dstAddress, dstPort), 2000);
if (!socket.isConnected())
throw new SocketException("Could not connect to Socket");
DataInputStream DIStream = new DataInputStream(socket.getInputStream());
DataOutputStream DOStream = new DataOutputStream(socket.getOutputStream());
DOStream.write(numCmd.getBytes(), 0, numCmd.getBytes().length);
DOStream.flush();
response = DIStream.readLine();
DOStream.close();
DIStream.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
response = "{\"Error_no\":5000}";
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
response = "{\"Error_no\":5000}";
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
response = "{\"Error_no\":5000}";
} finally {
if (socket != null) {
try {
socket.close();
if(response == null){
response = "{\"Error_no\":5000}";
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
response = "{\"Error_no\":5000}";
}
}
}
// Log.i("resp",response);
return response;
}
The PHP code:
<?php
echo "service php started \n";
$version = '2.2.0';
$address = '10.164.2.1';
$port = 4444;
$timeout= 2;
$cmd = explode("*", str_replace(array("/"), "*",
str_replace(" ","",shell_exec("netstat -tulpn | grep :".$port))));
echo "pid ".$cmd[1];
$cmd[1] = substr_replace($cmd[1], '', 0, 6);
echo "pid ".$cmd[1];
exec("kill -9 $cmd[1]");
sleep(1);
$socketPath = 'unix:///home/root/sockets/local_iapp_socket';
// Create WebSocket.
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
if(socket_bind($server, $address, $port)){
echo "Connected with tcp socket \n";
}else{
echo "connection with tc socket error \n";
}
socket_listen($server);
echo "server is listning \n";
$socket_fd = connect_socket_local();
echo "connected to unix socket \n";
while ($client = socket_accept($server)) {
$num_cmd = socket_read($client, 5000);
echo "Client Message : ".$num_cmd ."\n";
if($socket_fd != false){
$id=24;
$len= strlen($num_cmd);
$fwrite1 = fwrite($socket_fd, pack("L", $len),4);
if($fwrite1 === false){
$content = '{"Error_no":5000}';
echo "fwrite1 a échoué : raison : " . socket_strerror(socket_last_error()) . "\n";
exit;
}
$fwrite2 = fwrite($socket_fd,pack("L", $id),4);
if($fwrite2 === false){
$content = '{"Error_no":5000}';
echo "fwrite2 a échoué : raison : " . socket_strerror(socket_last_error()) . "\n";
exit;
}
$fwrite3 = fwrite($socket_fd,$num_cmd,strlen($num_cmd));
if($fwrite3 === false){
$content = '{"Error_no":5000}';
echo "fwrite3 a échoué : raison : " . socket_strerror(socket_last_error()) . "\n";
exit;
}
$content = fread($socket_fd, 5000) ;
if( $content == null){
echo "impossible d'atteindre la socket locale" . socket_strerror(socket_last_error()) . "\n";
$content = '{"Error_no":5000}';
}
}else{
$content = '{"Error_no":5000}';
echo "problème de connexion" . socket_strerror(socket_last_error()) . "\n";
}
$json_resp = $content;
echo "json_resp" . $json_resp."\n\r";
socket_write($client, $json_resp,strlen($json_resp));
socket_close($client);
}
socket_close($server);
function connect_socket_local()
{
global $socketPath , $timeout;
if (($socket_fd= stream_socket_client($socketPath , $errorno, $errorstr, $timeout)) === false) {
echo "stream_socket_client a échoué : raison : " . socket_strerror(socket_last_error()) . "\n";
}
return $socket_fd;
}
?>
You can run a separate server process for each client. Each server process needs to have an unique IP address or TCP port. Each client needs to be configured to connect to the correct server address and port. This is difficult to manage and doesn't scale.
At the server side, you can get the peer address, that is the IP address and port of the client. In PHP, you can use socket_getpeername() for this. However, depending on the network setup, this might not be the IP address of the client itself. It can be the IP address of an intermediate gateway or other network device. Also, the client IP address might be dynamic.
The best solution is to send some identification from the client to the server when the clients connects to the server. This can be a name or some other unique identification. You have to change the protocol between the client and server to support this. The client needs to know when and how to send the identification. The server needs to know when and how to receive the identification.
I am using SOAP to call a web servicefrom a Linux Centos 6 server and a php client. In this week I have been getting could not connect to host error from soapCall method. My code is as below and I have not changed it at all for some months but recently it gets this error most of the time. I have read most answers to related questions here but my problem have not been solved.
$wsdl="http://x.x.x.x:x/gw/services/Service?wsdl";
//Set key as HTTP Header
$aHTTP['http']['header'] = "key:" .$key ."\r\n";
$context = stream_context_create($aHTTP);
try
{
$client = new SoapClient($wsdl,array("soap_version" => SOAP_1_2,'trace' => 1,"stream_context" => $context));
}
catch(Exception $e)
{
return "something";
}
//I make $parametrs
try
{
$res = $client->__soapCall("send",array($parametrs));
}
catch(Exception $e)
{
print_r($e->getMessage()); //Most of the time it prints could not connect to host
}
I changed SOAP from _1_1 to _1_2 but nothing changed.
This is how I call SOAP webservice, please note Service?wsdl should be Service.wsdl
Example
//Initialize values
$wsdl = "Service.wsdl";
$url = "http://x.x.x.x:x/gw/services/";
$username = "********"; //add username
$password = "********"; //add password
$client = new SoapClient("$url".$wsdl);
$params = array(
"username"=>$username,
"password"=>$password
);
$response = $client->UserLogIn($params); //UserLogIn is the function name
var_dump($response); // to see webservice response
I believe Cloudant has recently changed some of their code. Recently, if you do a storedoc operation, in a try/catch statement. Cloudant will return an 'error' to the framework:
Uncaught exception 'couchException' with message 'Continue
Of course you can handle it in the catch statement, but it should really be coming back as 'successful' in the Try statement of the PHP-on-Couch library.
Anyone come across this or know how to handle it? The biggest issues is that you cannot grab the ID and Rev in the catch statement because it's coming up as an error:
try { // does not return here, goes to catch
$response = $client->storeDoc($doc);
$response_json['status'] = 'success';
$response_json['id'] = $response->id;
$response_json['rev'] = $response->rev;
} catch (Exception $e) { // even though the doc is successfully storing
// check for accepted BEG
$error = '';
$error = $e->getMessage();
$err_pos = strpos($error,"Accepted");
$err_pos_2 = strpos($error,"Continue");
if($err_pos !== false OR $err_pos_2 !== false){ // success
$response_json['status'] = 'success';
$response_json['id'] = $response->id; // returns null
$response_json['rev'] = $response->rev; // returns null
} else { // truely an error
$response_json['status'] = 'fail';
$response_json['message'] = $e->getMessage();
$response_json['code'] = $e->getCode();
}
// check for accepted END
}
I tested in both CouchDB and Cloudant and the behavior is the same. This is what I believe is happening. When you create a new couchDocument:
$doc = new couchDocument($client);
By default the document is set to autocommit. You can see this in couchDocument.php:
function __construct(couchClient $client) {
$this->__couch_data = new stdClass();
$this->__couch_data->client = $client;
$this->__couch_data->fields = new stdClass();
$this->__couch_data->autocommit = true;
}
As soon as you set a property on the document:
$doc->set( array('name'=>'Smith','firstname'=>'John') );
storeDoc is immediately called. You are then trying to call storeDoc a second time and couchDB returns an error.
There are 2 ways to fix this:
Turn off autocommit:
$doc = new couchDocument($client);
$doc->setAutocommit(false);
$doc->set( array('name'=>'Smith','firstname'=>'John') );
try {
$response = $client->storeDoc($doc);
$response_json['status'] = 'success';
$response_json['id'] = $response->id;
$response_json['rev'] = $response->rev;
Keep autocommit on and get the id and rev from the $doc after you set a property:
$doc = new couchDocument($client);
try {
$doc->set( array('name'=>'Smith','firstname'=>'John') );
$response_json['status'] = 'success';
$response_json['id'] = $doc->_id;
$response_json['rev'] = $doc->_rev;
I plan to use nodejs as the client, and php as the server. I use the official tutorial.thrift.
I set up a nginx server to host the localhost:9099 with the dir /path/to/my/tutorial/, generate a server script as index.php and place it in the root ,when visit the localhost:9099,so that client visit the server through index.php.
the index.php content:
<?php
error_reporting(E_ALL);
require_once __DIR__ . "/Thrift/ClassLoader/ThriftClassLoader.php";
use Thrift\ClassLoader\ThriftClassLoader;
$ROOT = realpath(dirname(__FILE__));
$GEN_DIR = $ROOT.'/gen-php';
$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', $ROOT);
$loader->registerNamespace('Swoole', $ROOT);
$loader->registerNamespace('tutorial', $GEN_DIR);
$loader->registerDefinition('shared', $GEN_DIR);
$loader->registerDefinition('tutorial', $GEN_DIR);
$loader->register();
if (php_sapi_name() == 'cli') {
ini_set("display_errors", "stderr");
}
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TPhpStream;
use Thrift\Transport\TBufferedTransport;
use Thrift\Server\TServer;
header('Content-Type', 'application/x-thrift');
if (php_sapi_name() == 'cli') {
echo "\r\n";
}
$handler = new \tutorial\Handler();
$processor = new \tutorial\CalculatorProcessor($handler);
$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));
$protocol = new TBinaryProtocol($transport, true, true);
$transport->open();
$processor->process($protocol, $protocol);
$transport->close();
the client.node.js content:
/**
* Created by Kron on 16/4/12.
*/
var thrift = require('thrift');
var ThriftTransports = require('thrift/transport');
var ThriftProtocols = require('thrift/protocol');
var Calculator = require('../gen-nodejs/Calculator');
var ttypes = require('../gen-nodejs/tutorial_types');
transport = ThriftTransports.TBufferedTransport();
protocol = ThriftProtocols.TBinaryProtocol();
var connection = thrift.createConnection("localhost", 9099, {
transport : transport,
protocol : protocol
});
var client = thrift.createClient(Calculator, connection);
connection.on('error', function(err) {
console.log(err);
//assert(false, err);
});
// Create a Calculator client with the connection
client.ping(function(err, response) {
console.log('ping()');
});
client.add(1,1, function(err, response) {
console.log("1+1=" + response);
});
work = new ttypes.Work();
work.op = ttypes.Operation.DIVIDE;
work.num1 = 1;
work.num2 = 0;
client.calculate(1, work, function(err, message) {
if (err) {
console.log("InvalidOperation " + err);
} else {
console.log('Whoa? You know how to divide by zero?');
}
});
work.op = ttypes.Operation.SUBTRACT;
work.num1 = 15;
work.num2 = 10;
client.calculate(1, work, function(err, message) {
console.log('15-10=' + message);
client.getStruct(1, function(err, message){
console.log('Check log: ' + message.value);
//close the connection once we're done
connection.end();
});
});
The client.php content:
<?php
error_reporting(E_ALL);
require_once __DIR__ . "/../Thrift/ClassLoader/ThriftClassLoader.php";
use Thrift\ClassLoader\ThriftClassLoader;
$ROOT = realpath(dirname(__FILE__).'/../');
$GEN_DIR = $ROOT.'/gen-php';
$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', $ROOT);
$loader->registerNamespace('Swoole', $ROOT);
$loader->registerNamespace('tutorial', $GEN_DIR);
$loader->registerDefinition('shared', $GEN_DIR);
$loader->registerDefinition('tutorial', $GEN_DIR);
$loader->register();
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\THttpClient;
use Thrift\Transport\TBufferedTransport;
use Thrift\Exception\TException;
try {
if (array_search('--http', $argv)) {
$socket = new THttpClient('localhost', 9099, '/clients_servers/tutorial.server.php');
} else {
$socket = new THttpClient('localhost', 9099); // 8080 for node server, 9099 for php server
}
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$client = new \tutorial\CalculatorClient($protocol);
$transport->open();
$client->ping();
print "ping()\n";
$sum = $client->add(1,1);
print "1+1=$sum\n";
$work = new \tutorial\Work();
$work->op = \tutorial\Operation::DIVIDE;
$work->num1 = 1;
$work->num2 = 0;
try {
$client->calculate(1, $work);
print "Whoa! We can divide by zero?\n";
} catch (\tutorial\InvalidOperation $io) {
print "InvalidOperation: $io->why\n";
}
$work->op = \tutorial\Operation::SUBTRACT;
$work->num1 = 15;
$work->num2 = 10;
$diff = $client->calculate(1, $work);
print "15-10=$diff\n";
$log = $client->getStruct(1);
print "Log: $log->value\n";
$transport->close();
} catch (TException $tx) {
print 'TException: '.$tx->getMessage()."\n";
}
They're almost the same as original.
when I use client.php visit the host, It works well.
when I use client.node.js visit the host, It doesn't work.
when I try to use a node server,not the host, visit it use the client.php,I have to change $socket = new THttpClient('localhost', 9099) to $socket = new TSocket('localhost', 9099) to make it work.
client.node.js never ever touched the localhost:9099, and without any stdout.
Finally my partner helped me found the answer.
the official demo of nodejs cient has some problems:
official code:
transport = ThriftTransports.TFramedTransport();
protocol = ThriftProtocols.TBinaryProtocol();
our code:
transport = ThriftTransports.TFramedTransport;
protocol = ThriftProtocols.TBinaryProtocol;
remove the (), that makes the code work.
[hbase-dir]/bin/hbase-daemon.sh start thrift -f
I am facing same error. Refer http://dailyjs.com/post/hbase
TFramedTransport is disable by default. You have to enable it by using above command.
Note : After enable you can't able to connect php. I AM using different protocols for that in php
Check this :
NodeJS Hbase thrift weirdness
i am using the below code to connect to salesforce using php
require_once ('SforcePartnerClient.php');
require_once ('SforceHeaderOptions.php');
require_once ('SforceMetadataClient.php');
$mySforceConnection = new SforcePartnerClient();
$mySforceConnection->createConnection("cniRegistration.wsdl");
$loginResult = $mySforceConnection->login("username", "password.token");
$queryOptions = new QueryOptions(200);
try {
$sObject = new stdclass();
$sObject->Name = 'Smith';
$sObject->Phone = '510-555-5555';
$sObject->fieldsToNull = NULL;
echo "**** Creating the following:\r\n";
$createResponse = $mySforceConnection->create($sObject, 'Account');
$ids = array();
foreach ($createResponse as $createResult) {
print_r($createResult);
array_push($ids, $createResult->id);
}
} catch (Exception $e) {
echo $e->faultstring;
}
But the above code is connect to salesforce database.
But is not executing the create commands. it's giving me the below error message
Creating the following: Element {}item invalid at this location
can any one suggest me to overcome the above problem
MAK, in your sample code SessionHeader and Endpoint setup calls are missing
$mySforceConnection->setEndpoint($location);
$mySforceConnection->setSessionHeader($sessionId);
after setting up those, if you still see an issue, check the namespace urn
$mySforceConnection->getNamespace
It should match targetNamespace value in your wsdl
the value of $mySforceConnection should point to the xml file of the partner.wsdl.xml.
E.g $SoapClient = $sfdc->createConnection("soapclient/partner.wsdl.xml");
Try adding the snippet code below to reference the WSDL.
$sfdc = new SforcePartnerClient();
// create a connection using the partner wsdl
$SoapClient = $sfdc->createConnection("soapclient/partner.wsdl.xml");
$loginResult = false;
try {
// log in with username, password and security token if required
$loginResult = $sfdc->login($sfdcUsername, $sfdcPassword.$sfdcToken);
}
catch (Exception $e) {
global $errors;
$errors = $e->faultstring;
echo "Fatal Login Error <b>" . $errors . "</b>";
die;
}
// setup the SOAP client modify the headers
$parsedURL = parse_url($sfdc->getLocation());
define ("_SFDC_SERVER_", substr($parsedURL['host'],0,strpos($parsedURL['host'], '.')));
define ("_SALESFORCE_URL_", "https://test.salesforce.com");
define ("_WS_NAME_", "WebService_WDSL_Name_Here");
define ("_WS_WSDL_", "soapclient/" . _WS_NAME_ . ".wsdl");
define ("_WS_ENDPOINT_", 'https://' . _SFDC_SERVER_ . '.salesforce.com/services/wsdl/class/' . _WS_NAME_);
define ("_WS_NAMESPACE_", 'http://soap.sforce.com/schemas/class/' . _WS_NAME_);
$urlLink = '';
try {
$client = new SoapClient(_WS_WSDL_);
$sforce_header = new SoapHeader(_WS_NAMESPACE_, "SessionHeader", array("sessionId" => $sfdc->getSessionId()));
$client->__setSoapHeaders(array($sforce_header));
} catch ( Exception $e ) {
die( 'Error<br/>' . $e->__toString() );
}
Please check the link on Tech Thought for more details on the error.