Jump to content

NotionCommotion

Members
  • Posts

    2,446
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by NotionCommotion

  1. A little better with my DealWithSocket class... <?php class DealWithSocket implements Evenement\EventEmitterInterface{ // Should Evenement be used? use Evenement\EventEmitterTrait; private $socket, $buffer='', $messageLength; public function __construct(React\Stream\DuplexStreamInterface $socket){ $this->socket = $socket; $this->socket->on('data', function($data){ $this->buffer .= $data; $this->parseBuffer(); }); } public function send($string){ $this->socket->write(strlen($string).$string); // I don't think this is right } private function getLength(){ //My understanding is that "N" represents an unsigned long (always 32 bit, big endian byte order) and "len" is just what ever you want the array index name to be return strlen($this->buffer)>=4?unpack('Nlen', substr($this->buffer,0,4))['len']:0; } private function parseBuffer(){ // Is using string functions like strlen() appropriate? if(!$this->messageLength && strlen($this->buffer)>=4) { //Save the first time data is received or it happened to end perfectly at the end? $this->messageLength=$this->getLength(); } while ($this->messageLength && strlen($this->buffer)>=($this->messageLength+4)){ $message = substr($this->buffer, 4, messageLength); $this->emit('data', $message); $this->buffer = substr($this->buffer, messageLength+4); $this->messageLength=$this->getLength(); } } }
  2. Well, then delimiters it is! I've gone through too much just to settle for "slightly simpler". As far as implementation, I am thinking of something like the following. Before going down the path to invent something new, has this requirement been implemented many times before resulting in a better solution? <?php require 'vendor/autoload.php'; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $socket->on('connection', function (\React\Socket\ConnectionInterface $socket) use ($loop){ $superSocket = new DealWithSocket($socket); $superSocket->on('data', function($message) use ($superSocket){ echo($message.PHP_EOL); $superSocket->send('thank you!'); }); }); $socket->listen(1337,'0.0.0.0'); $loop->run(); <?php class DealWithSocket implements Evenement\EventEmitterInterface{ // Should Evenement be used? use Evenement\EventEmitterTrait; private $socket, $buffer='', $messageLength, $messageLengthPointer=0; public function __construct(React\Stream\DuplexStreamInterface $socket){ $this->socket = $socket; $this->socket->on('data', function($data){ $this->buffer .= $data; $this->parseBuffer(); }); } public function send($string){ $this->socket->write(strlen($string).$string); // I don't think this is right } private function getLength($string, $start=0){ //My understanding is that "N" represents an unsigned long (always 32 bit, big endian byte order) and "len" is just what ever you want the array index name to be return unpack('Nlen', substr($data,$start,4))['len']; } private function parseBuffer(){ // And this needs help... if(is_null($this->messageLength)) { //Save the first time data is received? $this->messageLength=$this->getLength($this->buffer); } while (strlen($this->buffer)>($this->messageLength+4)){ $message = substr($this->buffer, 4, messageLength); $this->buffer = substr($this->buffer, messageLength+4); $this->emit('data', $message); } } }
  3. I had just naively assumed delimiters as the C++ author and I never discussed it. If necessary, the C++ code can be changed to use delimiters. Do you feel one design choice is better than the other, or is the better approach based on the situation? If the situation, what aspects influence the decision? Thanks
  4. Your right, I still don't completely understand the concept of streaming, but understand way more than I did a while ago. What I have been doing is using the JSONStream class kicken posted under https://forums.phpfreaks.com/topic/302840-http-server-with-two-hosts-and-same-port/?p=1540924. It is my understanding that I can either use the length to extract the message as you just indicated, or do it the kicken way which looks for an end of line. If I have the length (which I do), think that is the better way? How do I actually get these four bytes as a number? Can you point me in the right direction to implement? Thanks
  5. Thanks ginerjm, Would it be substr with 2 or 4? 2 bytes per character, right? Funny but I seem to get the same results using both 2 or 4. Don't think I need http://php.net/manual/en/function.mb-substr.php, do you? If I do the following and echo $firstFourBytes, it displays some unprintable text. $firstFourBytes=substr($data,2); $remainingContent=substr($data,0,2); The four bytes are added with the following c++ script. uint32_t size = message.size(); unsigned char mSize[sizeof(size)]; memcpy(&mSize, static_cast<void*>(&size), sizeof(size)); data.insert(data.begin(), mSize, mSize + sizeof(size)); How could I get the numerical value of those four bytes using PHP?
  6. I have the following script. When echoing $data, it included some strange character at the beginning of the message <?php require 'vendor/autoload.php'; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $socket->on('connection', function (\React\Socket\ConnectionInterface $client) use ($loop){ $client->on('data', function($data){ echo($data."\r\n"); }); }); $socket->listen(1337,'0.0.0.0'); $loop->run(); Turns out that $data has 4 bytes pretended on it to represent the length of the message. On the receiving connection, how can I get those 4 bytes and also remove them from $data.
  7. Thanks requinix I've never seen __construct() put in an interface, however, I suppose it makes sense. Include the fly() method as well, right? You are right, I don't wish to make a class for every single possible combination of features, and your approach makes sense. What was specific about the model I am using which prevents doing so? Are there other models which are commonly used that better support doing so?
  8. Thanks Jacques1, My need is to learn how to extend more than one class. I go from general to specific, and all is good. Then I need to add some more methods and get stuck. Okay, I maybe see your point about traits should be public methods and not attributes, however, it still seems useful sometimes to have the ability to tweak the resultant object. Regardless, let me concede this point. What if I have both CorvetteStingray extends Corvette extends Chevrolet extends Car and and ShelbyGT350 extends Mustang extends Ford extends Car. The dealer I am talking to offers some cool option where they strap a propeller to the back of any car, and now it can fly! Where can I add the fly() method without giving all cars the ability to fly and without duplicating the fly() method script in multiple classes?
  9. I am new to traits, and am trying to figure out when and how to use them. Please comment on the following. While this is a question about traits, I would welcome (provided advice is also given) any criticism about my use of interfaces should criticism be warranted. Thanks <?php interface CarInterface { public function go(); public function stop(); public function turn(); public function resistRust(); } class Car implements CarInterface { public function go() { //bla bla bla $acceration=$this->accerationRate(); //bla bla bla } public function stop() { //bla bla bla $deacceration=$this->deaccerationRate(); //bla bla bla } public function turn() { //bla bla bla $turnRadius=$this->turnRadius(); //bla bla bla } public function resistRust() { //bla bla bla $resistanceFactor=$this->resistanceFactor(); //bla bla bla } } class Ford extends Car { protected function accerationRate(){/* some script */} protected function deaccerationRate(){/* some script */} protected function turnRadius(){/* some script */} protected function resistanceFactor(){/* some script */} } class Chevrolet extends Car { protected function accerationRate(){/* some script */} protected function deaccerationRate(){/* some script */} protected function turnRadius(){/* some script */} protected function resistanceFactor(){/* some script */} } class FordWithDealerRustProofOption extends Ford { use DealerRustProofOption; } class ChevroletWithDealerRustProofOption extends Ford { use DealerRustProofOption; } trait DealerRustProofOption { protected function resistanceFactor() {/* some script */} } $car=new Ford(); $car=new Chevrolet(); $car=new FordWithDealerRustProofOption(); $car=new ChevroletWithDealerRustProofOption();
  10. I'm good at confusing! Agree, the clients will need a unique id. I like your use of a static variable. The server also needs to generate a unique id. SuperClient sends to Server a unique ID (I will refer to it as superClientID), a method (forward), and params (the GUID of the intended NormalClient along with the method and parameters to send to that NormalClient). Server determines a new ID unique for just the intended NormalClient (I will refer to it as serverID), uses serverID to save superClientID along with the method sent to NormalClient, and sends NormalClient the method, parameters, and serverID. NormalClient processes the method/parameters, and return a response along with serverID. Server uses the received serverID to determine the method it sent as well as the superClientID, processes the response, and replies to SuperClient using superClientID. Two possible variants are to make serverID unique across NormalClients and to not have Server save the method sent to NormalClient, but to have NormalClient return the method, but I don't think I will do either. Yeah, it is a little confusing... It seems like it should work fine, but I am always nervous when I makeup some complicated approach without first reading how others have done it.
  11. $clientList = new SplObjectStorage(); $socket->on('connection', function (\React\Socket\ConnectionInterface $client) use (&$clientList, $guidMap, $commandsForClientRequests, $commandsForClientResponses){ $clientList->attach($client, ['guid'=>null,'callback'=>[]]); $client->on('data', function($data) use (&$clientList, $client, $guidMap, $commandsForClientRequests, $commandsForClientResponses){ $count=++$clientList[$client]['counter']; $clientList[$client]['callback'][$count]=['method'=>$data->params->method,'id'=>$data->id]; }); }); Should work, right?
  12. How would you recommend implementing an ever-increasing counter variable for the id? Use a separate counter, add a flag to the callback array, or something else? I guess I answered my question as I wrote this to reply. Option 1 is probably the way to go as it doesn't needlessly keep in memory a bunch of obsolete data in the array. Option 1 if(empty($data->method)) { // A response to a message set from the receiver if(isset($clientList[$client]['callback'][$data->id])) { $callback=$clientList[$client]['callback'][$data->id]; //['method'=>'subtract','id'=>123] // Deal with the response... // Unset the response so it can't be implemented again unset($clientList[$client]['callback'][$data->id]); } else { //error. Response already received } } else { switch($data->method) { case 'forward': // A request to forward... if(!empty($data->id)) { //Superclient wants a response $count=++$clientList[$client]['counter']; $clientList[$client]['callback'][$count]=['method'=>$data->params->method,'id'=>$data->id]; $forward['id']=$count; } // Forward the request $guidMap[$data->params->guid]->send($forward); break; } } Option 2 if(empty($data->method)) { // A response to a message set from the receiver $callback=$clientList[$client]['callback'][$data->id]; //['method'=>'subtract','id'=>123, 'available'=true/false] if($callback['available']) { // Deal with the response... // Set flag to false so it can't be implemented again $clientList[$client]['callback'][$data->id]['available']=false; } else { //error. Response already received } } else { switch($data->method) { case 'forward': // A request to forward... if(!empty($data->id)) { //Superclient wants a response $count=count($clientList[$client]['callback'])+1; $clientList[$client]['callback'][]=['method'=>$data->params->method,'id'=>$count, 'avaiable'=>true]; $forward['id']=$count; } // Forward the request $guidMap[$data->params->guid]->send($forward); break; } }
  13. Typically, servers do not initiate requests, but only respond to clients' request. 1. The one exception is when the superclient sends a forward request to the server... {"jsonrpc": "2.0", "method": "forward", "params": {"guid": "1234abcd", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}}, "id": 123} which causes the server to save information about the request... $count=count($clientList[$client]['callback'])+1; //which happens to be 321 $clientList[$client]['callback'][]=['method'=>$data->params->method,'id'=>$count];. // or given the request... $clientList[$client]['callback'][]=['method'=>'subtract','id'=>321];. and initiate a new request to the client which has a GUID of 1234abcd. {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 321} 2. The client responds with JSON per the standard which must not have a method property and must have a result property. {"jsonrpc": "2.0", "result": 19, "id": 321} 3. The server sees that either there is no method property or there is a result property (or to be extra sure, maybe both, but why bother), and knows this is the one exception, and must be a response to a forwarded request. It then finds the original method sent to the client as well as the original request id sent to the server based on the returned request id... $callback=$clientList[$client]['callback'][$data->id]; //['method'=>'subtract','id'=>123] deals with it... $rs=$commandsForClientResponses($callback['method'],$data->result); //Returns server state plus normalClient state as applicable and if a original request id exists, returns the if(!empty($callback['id'])) $client->send(["jsonrpc"=>"2.0", "result"=>[$data->result], "id"=>$callback['id']]); } I think I am good. Maybe not? PS. Am I using the term "member" correctly? I am using it based on your 05:04 PM post, but might have misinterpreted its use. EDIT. I think I did misinterpret their use, and have edited this post to replace the term "member" with "property". Thanks!
  14. I don't believe relevant, but there is only one super client which happens to be a sockets client created for one time use by an Ajax request to a normal HTTP server. I will need to make that client's send method blocking which I have never done, but expect I can figure it out. I suppose I could add two new members, but my intention was to just make the method name "forward", and include the intended normal clients guid as well as the method and parameters to send to that client in the main params field. Something like the following. As such, I am not adding any members. Am I missing the meaning of what you are saying? {"jsonrpc": "2.0", "method": "forward", "params": {"guid": "1234abcd", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}}, "id": 123} I recognize it is a bunch of script, but would appreciate any critic of the script that you or others are able to provide. Thanks
  15. Your definitely correct. Couldn't find anything... Let me give you more definition. There is one server, one superClient, and multiple normalClients. The normalClients can make requests to the server, and gets a response if they want one. The superClient can make requests to the server which in turn gets forwarded to a specific normalClient, and gets a response if it wants one. Below is my attempt. Thoughts? <?php require 'vendor/autoload.php'; $port = isset($argv[1])?$argv[1]:1337; $clientList = new SplObjectStorage(); $guidMap=[]; $commandsForClientRequests = new commandsForClientRequests(); $commandsForClientResponses = new commandsForClientResponses(); $loop = new \React\EventLoop\StreamSelectLoop(); $socket = new React\Socket\Server($loop); $socket->on('connection', function (\React\Socket\ConnectionInterface $client) use (&$clientList, $guidMap, $commandsForClientRequests, $commandsForClientResponses){ $clientList->attach($client, ['guid'=>null,'callback'=>[]]); $client->on('data', function($data) use (&$clientList, $client, $guidMap, $commandsForClientRequests, $commandsForClientResponses){ $data=json_decode($data); //I will add a little data validation... if(empty($data->method)) { /* A response to a message set from the server and looks like the following: {"jsonrpc": "2.0", "result": 19, "id": 3} Modify server state and then respond to super-client the results */ $callback=$clientList[$client]['callback'][$data->id]; //['method'=>'subtract','id'=>123] $rs=$commandsForClientResponses($callback['method'],$data->result); //Returns server state plus normalClient state as applicable if(!empty($callback['id'])) { //Only reply to non-notification requests $client->send(["jsonrpc"=>"2.0", "result"=>[$data->result], "id"=>$callback['id']]); } } else { // A new message from a client switch($data->method) { case 'forward': /* This is a special message coming from super-client which forwards the sub-message to client identified by guid 1234abcd, and looks like the following: {"jsonrpc": "2.0", "method": "forward", "params": {"guid": "1234abcd", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}}, "id": 123} */ if(isset($guidMap[$data->params->guid])) { $forward=[]; $forward['jsonrpc']=$data->jsonrpc; $forward['method']=$data->params->method; $forward['params']=$data->params->params; if(!empty($data->id)) { //Superclient wants a response $count=count($clientList[$client]['callback'])+1; $clientList[$client]['callback'][]=['method'=>$data->params->method,'id'=>$count]; $forward['id']=$count; } $guidMap[$data->params->guid]->send($forward); } elseif(!empty($data->id)) { $client->send(["jsonrpc"=>"2.0", "error"=>['code'=>-32601, 'message'=>'Client not found'], "id"=>$data->id]); } break; case 'registrar': /* This is a special message which registrars normal client identified by guid 1234abcd, and looks like the following: {"jsonrpc": "2.0", "method": "registrar", "params": {"guid": "1234abcd"}, "id": 123} */ $clientList[$client]=['guid'=>$data->params->guid,'callback'=>[]]; $guidMap[$data->params->guid] = $client; if(!empty($data->id)) { $client->send(["jsonrpc"=>"2.0", "result"=>['success'=>true], "id"=>$data->id]); } break; default: //Deal with normal requests coming from normal clients... $rs=$commandsForClientRequests($data->method,$data->params); if(!empty($data->id)) { //Only reply to non-notification requests $client->send(["jsonrpc"=>"2.0", "result"=>$rs, "id"=>$data->id]); } } } }); }); $socket->listen($port, '0.0.0.0'); $loop->run();
  16. Thanks! At least I have something to search for. Could it have a more descriptive name? "Protocol" seems to be used at so many levels. Maybe "application protocol"? I expect you either already know this or it is not really a standard. It is JSON-RPC 2.0. The id in the JSON uniquely describes the message sent for a given client. See http://json-rpc.org/wiki/specification and http://www.jsonrpc.org/specification. No, server, doesn't initiate the connection, client does. Server, however, has the ability to initiate a message using an established connection. ClientA and ClientB both establish connection to Server, ClientA sends message to Server, and Server sends message to ClientB. Assuming my loosely thought out 8-step strategy is somewhat correct, I would expect I would need to store callback[] in a SplObjectStorage tied to the connection? I am sure there are many ways to implement a protocol, however, also expect it has been done many times, and some ways are better than others. For me: JSON-RPC 2.0 is being used as the protocol. Multiple clients can establish a TCP socket connection to a single server. Clients need to have the ability to send several types of methods with optional parameters to the server, and need to be able to react to the server's response based on the type of method the client had sent to the server. Server need to have the ability to send several types of methods with optional parameters to each individual client (thus subscriptions will be used), and need to be able to react to the client's response based on the type of method the server had sent to the client. Make sense? Any advice? Thanks
  17. I am creating an incredible math server application which has the ability to subtract two numbers. I think I have the math down, but need some help with the strategy for how the client should react to the servers response. I have no idea whether this is correct, but am thinking of something like the following: Client creates a socket connection to the server. Upon server’s acceptance of the connection, client initiates sets $this->callbacks=[]. Client decides to send some big math request to the server. Based on what it wants to do with the results, client sets $this->callback[]='someCallBackFunction', and then performs a count of the array which shows that the it happens to have three elements. Client sends the following to the server: {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3} Client listens for the response, and eventually is returned: {"jsonrpc": "2.0", "result": 19, "id": 3} Client looks up $this->callbacks=[3], unsets it so it cannot be called initiated again, and does something with it. Maybe after a sufficient duration of time, client unsets any other elements in the array which are old? Am I on the right track? What is this requirement typically called so I may google and get more information? Can anyone offer any additional description on how this is typically accomplished? If the server initiates the communication to the communication to the client, is the process basically the same. I expect the client will have needed to subscribe to the server after initiating the connection, but expect/hope the process is close. Thanks
  18. Thanks kicken, Guess I didn't realize that resources are cleared on a per function basis, but now makes sense. Sounds like it is not necessary unless one experiences some rare issues.
  19. I agree with ginerjm that the database should surely be vetted out before coding. But before designing the database, make sure you accurately understand user requirements!
  20. What if this is my model? use React\EventLoop\Factory; $loop = Factory::create(); $loop->addPeriodicTimer(60, function() { ... $stmt=$db->prepare('some query goes here...'); $stmt->execute([$x,$y]); //fetch if it happens to be a SELECT query... ... }); $loop->run();
  21. Thanks Jacques1, So, if I have a PHP script which runs indefinitely, I may need to use it? What do you consider a large result set?
  22. When for MySQL? When for SQLite? Does it make a difference if it is a single hit webserver or a PHP server or client that continuously runs?
  23. Can I do so when using twig? I tried the following but no change. return $rsp[1]==204?$response->withStatus($rsp[1],'whatever'):$response->withJson($rsp[0],$rsp[1]);
  24. PHP should return only status code 204 if the request was successful, else status code 422 along with a description of the error if an error occurred. Firebug has been displaying error XML Parsing Error: no root element found when it receives a 204 status code. I finally tracked it down to the following. It appears that if $rs is [null,204], I am telling slim to return JSON, however, 204 should not have any content. How should the below script be modified to prevent these errors? <?php require __DIR__.'/../vendor/autoload.php'; $app = new \Slim\App(); $app->get('/{name}', function (\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response) { $rs=($request->getAttribute('name')=='error')?["error message",422]:[null,204]; return $response->withJson($rs[0],$rs[1]); }); $app->run();
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.