Jump to content

kicken

Gurus
  • Posts

    4,704
  • Joined

  • Last visited

  • Days Won

    179

Everything posted by kicken

  1. Your server and it's clients should communicate with some standard data layout. In my RLGL example for example every JSON structure contained a message field that indicated what type of data structure was being sent. You can use that field to then decide what to do with any data you receive. One of those actions could be something to forward data to a given client which is what your index.php script would use. $client->on('data', function($data) use ($client){ switch ($data['message']){ case 'register': $this->addConnection($client, $data['guid']); break; case 'forward': $forwardClient = $this->findConnectionByGuid($data['guid']); $forwardClient->send($data['data']); //Somehow determine success/failure and send the result //$client->send(['message'=> 'forward-result', 'success' => true]); break; default: $guid = $this->getConnectionID(); $this->app->process($data, $guid, $client); break; } }); Your index.php script can just use the react framework stuff to send your data and receive a response. Something like this: function sendToClient($guid, $forwardData){ $success = false; $loop = new \React\EventLoop\StreamSelectLoop(); $socket = new \React\SocketClient\TimeoutConnector(new \React\SocketClient\TcpConnector($loop), 15, $loop); $socket->create($server, $port)->then(function($stream){ $client = new \Kicken\RLGL\JSONStream($stream); $stream->on('data', function($data) use (&$success, $loop, $stream, $guid, $forwardData){ if ($data['message'] == 'forward-result'){ $success = $data['success']; $stream->close(); $loop->stop(); } }); $client->send([ 'message' => 'forward' , 'guid' => $guid , 'data' => $forwardData ]); }); //Generic timeout so this function doesn't block forever. $loop->addTimer(30, function() use ($loop){ $loop->stop(); }); $loop->run(); return $success; }
  2. You could still make a guid map if you wanted to after you know what the GUID is. private function addConnection($client, $guid) { $this->clientList[$client]['guid']=$guid; $this->guidMap[$guid] = $client; } private function findConnectionByGuid($guid) { return isset($this->guidMap[$guid])?$this->guidMap[$guid]:null; } If there are not a lot of clients I'd probably just stick with the loop, but use a foreach to make it simpler. private function findConnectionByGuid($guid) { foreach ($this->socketClients as $client){ if ($this->socketClients[$client]['guid'] == $guid){ return $client; } } return null; } That way things are kept simple with only one member variable tracking the list of clients.
  3. I would ask why the clients are the ones generating these keys rather than the server? If you're going to be using them to identify individual clients then they would need to be unique and to guarantee they are unique the server should be the one generating them and assigning them to the client. If the server generates the key then you'd have it from the beginning. As for your option 2 method, why use a hash? You could just store the object into the map array. public function assign2($obj,$id) { $this->map[$id]=$obj; } public function findByID2($id) { return $this->map[$id]; } No need for SplObjectStorage or the hash for a simple id to object mapping like that.
  4. Unless you use port 465 (SMTPS) then you need to determine whether your application supports STARTTLS or not as that's what is needed to enable encryption on the other ports. In general it's up to your SMTP client to request that encryption be enabled by issuing a STARTTLS command. Only SMTPS provided encryption from the get-go and as mentioned that has been deprecated (though still supported in places). Regarding your initial concerns for privacy, keep in mind that even if you can get SSL working for delivering email to the GMAIL servers it may be transmitted to it's final destination over a plain-text channel. If the final address is somewhere other than google then the message will need to be forwarded and not all servers support encrypted server-to-server communication so the message would then be sent in plain text. Google tracks some statistics about this if you're interested.
  5. Yep. Yep, you'd still have to be able to call the createAdjustableTimer method on the client. $this->timer = $this->client->createAdjustableTimer(60, function(){ echo "Cron"; }); //... $this->timer->setInterval(10);
  6. According to the specification the content-id is supposed to follow the addr-spec format which is defined as addr-spec = local-part "@" domain. Examples I can find in my email seem to follow that pattern, so I'd suggest you try the same.
  7. Their notice about it being split into components and the fact that the main repository has not had any significant updates in a long time provides a strong hint. If you dig into the the issues area you'd also find an issue named Mark react/react as abondoned in order to redirect people to new components. Wrap your connection code into a simple connect() method. You can then monitor the socket for a disconnect and simple call the connect() method again to reconnect if desired. There shouldn't be any need to clean up any of the socket objects or what not. PHP will take care of the memory management and the socket will already have been closed. To handle an error connecting to the server you can use the otherwise method on the promise. You'll also likely want to use a TimeoutConnector so you can control timeout. Also note that my JSONStream doesn't forward events, it only emits it's own data event. To monitor the close event you'd have to either modify the class to forward it or watch the underlying stream object. class Client { //... public function start(){ $this->loop = Factory::create(); $this->connect(); $this->loop->run(); } private function connect(){ $socket = new TimeoutConnector(new TcpConnector($this->loop), 10, $this->loop); $socket->create($this->app->config->host, $this->app->config->port)->then(function($stream){ $this->connection = new \Kicken\RLGL\JSONStream($stream); $this->connection->send($this->app->identifyCommand()); //Sends clients identity to server $this->connection->on('data', function($data){ $this->app->process($data); }); $stream->on('close', function(){ $this->connection = null; //Determine whether or not to reconnect //call $this->connect(); if you want to reconnect }); })->otherwise(function($reason){ //Handle connection error //call $this->connect() to try again }); } } You'd just have to implement your own buffering on top of it's buffering. I probably wouldn't really bother though since their buffer limit is implemented more as a suggestion rather than a hard limit. Their buffer class will accept whatever data you throw at it but begins returning false after 64k as hint that maybe you should wait a bit before sending more. If you want to protected against them possibly changing their code to make it a hard limit then you'd just change send to append data to a variable and another variable to track whether the socket buffer is full or not. Add a listener for the drain event and if you have any data in your buffer try to write it and update the buffer full variable. You can look at their buffer class as an example, you'd be doing basically the same thing but without a limit. You could create some kind of adjustable timer class that handles re-registering a timer and use that from within your application class. For example class AdjustableTimer { private $loop; private $interval; private $callback; private $timer; public function __construct(LoopInterface $loop, $interval, callable $callback){ $this->loop = $loop; $this->interval = $interval; $this->callback = $callback; $this->start(); } public function setInterval($interval){ $this->interval = $interval; $this->start(); } public function stop(){ $this->loop->cancelTimer($this->timer); $this->timer = null; } public function start(){ if ($this->timer){ $this->stop(); } $this->timer = $this->loop->addPeriodicTimer($this->interval, $this->callback); } } Add a method to your client that will construct a variable timer and return it. class Client { //... public function createAdjustableTimer($interval, $callback){ return new AdjustableTimer($this->loop, $interval, $callback); } }
  8. react/react is older and not really intended to be used anymore, instead you're supposed to just include the individual libraries as needed. My composer requirements for the example were: "require": { "react/socket": "^0.4.4", "react/stream": "^0.4.5", "react/event-loop": "^0.4.2", "react/socket-client": "^0.5.3" } Using the factory is probably better in general. The factory will look for the best event loop implementation your setup can support. So I can also use it in the sendClientInfo function later on. In reality it'd be better to create some classes for the client and server so that state like the connection and client information can be tracked as member variables.
  9. Here's that example. To try and simulate your bi-direction traffic plus having a timed job I coded up a little Red-light/Green-light system. The server accepts clients and sends them random red or green light messages. When a client sees a green-light it fakes moving and stops when it gets a red-light. Periodically it also sends the server how far it's moved and it's name. The server periodically lists the players and their distance. There is pretty much no error handling (such as for disconnects) in the code, I'll leave that as an exercise to the reader. JSONStream.php - Sends and receives json documents via a socket stream. <?php namespace Kicken\RLGL; use Evenement\EventEmitterInterface; use Evenement\EventEmitterTrait; use React\Stream\DuplexStreamInterface; class JSONStream implements EventEmitterInterface { use EventEmitterTrait; private $socket; private $buffer; public function __construct(DuplexStreamInterface $socket){ $this->socket = $socket; $this->buffer = ''; $this->socket->on('data', function($data){ $this->buffer .= $data; $this->parseBuffer(); }); } public function send($structure){ $json = json_encode($structure); $this->socket->write($json."\r\n"); } private function parseBuffer(){ $eol = strpos($this->buffer, "\r\n"); while ($eol !== false){ $json = substr($this->buffer, 0, $eol); $this->buffer = substr($this->buffer, $eol + 2); $data = json_decode($json, true); if (json_last_error() == JSON_ERROR_NONE){ $this->emit('data', [$data]); } $eol = strpos($this->buffer, "\r\n"); } } } server.php - Short script to act as the server. Listens on port 1337 for connections and listens for info messages from each client. Sends light messages periodically. <?php require 'vendor/autoload.php'; require 'JSONStream.php'; $port = isset($argv[1])?$argv[1]:1337; $clientList = new SplObjectStorage(); $loop = new \React\EventLoop\StreamSelectLoop(); $socket = new React\Socket\Server($loop); $socket->on('connection', function (\React\Socket\ConnectionInterface $client) use (&$clientList){ $client = new \Kicken\RLGL\JSONStream($client); $clientList->attach($client, []); $client->on('data', function($data) use (&$clientList, $client){ if ($data['message'] == 'info'){ $clientList[$client] = $data['info']; } }); echo "New connection accepted.\r\n"; }); $loop->addPeriodicTimer(5, function () use ($clientList){ $color = mt_rand(1,2) == 1?'red':'green'; echo "Sending {$color} light.\r\n"; /** @var \Kicken\RLGL\JSONStream $client */ foreach ($clientList as $client){ $client->send([ 'message' => 'light' , 'color' => $color ]); } }); $loop->addPeriodicTimer(60, function() use ($clientList){ echo "Current results:\r\n"; foreach ($clientList as $client){ $info = $clientList[$client]; if (!empty($info)){ echo "{$info['name']} has travelled {$info['distance']} feet.\r\n"; } } }); $socket->listen($port, '0.0.0.0'); $loop->run(); client.php - Connects to the server and listens for light messages. Periodically tells the server how far it has travelled. <?php require 'vendor/autoload.php'; require 'JSONStream.php'; $server = isset($argv[1])?$argv[1]:'127.0.0.1'; $port = isset($argv[2])?$argv[2]:1337; /** @var \Kicken\RLGL\JSONStream $client */ $client = null; $clientInfo = [ 'name' => getRandomName() , 'speed' => getRandomSpeed() , 'traveled' => 0 , 'moving' => false , 'movingStartTime' => null ]; $loop = new \React\EventLoop\StreamSelectLoop(); $socket = new \React\SocketClient\TcpConnector($loop); $socket->create($server, $port)->then(function ($stream) use (&$client, &$clientInfo){ $client = new \Kicken\RLGL\JSONStream($stream); sendClientInfo(); $client->on('data', function ($data) use (&$clientInfo){ if ($data['message'] == 'light'){ if ($data['color'] == 'green' && !$clientInfo['moving']){ $clientInfo['moving'] = true; $clientInfo['movingStartTime'] = time(); echo "Received green light!\r\n"; } else if ($data['color'] == 'red' && $clientInfo['moving']){ $movingTime = time() - $clientInfo['movingStartTime']; $distance = $movingTime * $clientInfo['speed']; $clientInfo['traveled'] += $distance; $clientInfo['moving'] = false; $clientInfo['movingStartTime'] = null; echo "Received Red light! Total distance = {$distance} feet.\r\n"; } } }); }); $loop->addPeriodicTimer(15, 'sendClientInfo'); $loop->run(); function sendClientInfo(){ global $client, $clientInfo; $infoToSend = [ 'name' => $clientInfo['name'] , 'distance' => $clientInfo['traveled'] ]; echo "Updating server with data.\r\n"; $client->send(['message' => 'info', 'info' => $infoToSend]); } function getRandomName(){ static $names = ['Edmundo', 'Roxane', 'Evelynn', 'Isaias', 'Buena', 'Evan', 'Katharina', 'Darnell', 'Sharyl', 'Malka', 'Sharla', 'Delena', 'Leah', 'Marnie', 'Tammi', 'Joanna', 'Marva', 'Karyn', 'Kristal', 'Jayson']; shuffle($names); return $names[0]; } function getRandomSpeed(){ return mt_rand(1, 100) / 100; }
  10. I feel like your two machine client processed should be able to be combined as well as your two separate http servers which handle them. That would simplify things. Perhaps there is some reason for them being separate but from what I can recall of previous threads I don't see it. I'd think a setup something more like this could work. Your application server consists of two services. - Apache: Handles your normal web traffic and browsers. Scripts run here can use cURL to communicate with the PHP Server - PHP Server: Handles your remote client communications Your remote clients consist of a single service: - PHP Client: Handles your bi-directional communication needs. Has a periodic timer that does whatever your 1-minute cron task does. HTTP May not work for your bi-directional needs however, at least not using any pre-written library. The problem is you'd need to alternate which side is the server vs the client based on which side needs to initiate communication. To send data to the application server you'd have: HTTP Client(Remote client) ---> HTTP Server(Application server) where as to send data to the remote client you'd have HTTP Server(Remote client) <--- HTTP Client(Application server). You could stick with the two socket interface with say port 1337 being Remote Client ---> Application Server and 1338 being Remote Client <--- Application Server The JSON documents idea from the other thread would work fine with a single socket. You could just do something simple like each line is a json document and examine some field in that json to determine what to do. I'll code up a small example later.
  11. Not really, which is why I was unsure if the two servers did the same thing or not. My understanding of what you wrote is you have Apache on port 80 and your custom server on port 1337. Scripts running via apache need to connect to your server to pass it some data. You also have some other external client scripts that need to connect to the server and send/receive data. It sounds like somewhere where a single server socket (on say 192.168.1.200:1337) would work, but two in order to easily distinguish between your apache clients and custom clients would also work fine.
  12. If both of those HTTP servers do the same thing there's no need to create two, just have one that listens on all addresses (unless you have a reason to limit the IPs). $http_socket1 = new SocketServer($loop); $http1 = new HttpServer($http_socket1); $http1->on('request', function ($request, $response) { $response->writeHead(200, array('Content-Type' => 'text/plain')); $query = $request->getQuery(); echo print_r($query,1) . PHP_EOL; $response->end(print_r($query,1)); }); $http_socket1->listen('1337', '0.0.0.0'); If they do different things then I'd use different port numbers. While there's nothing to technically stop you from using the same port but different IP's it will be confusing and likely lead to trouble down the road.
  13. You should be able to use debugging in a CLI context as well. You'll have to look into your IDE's settings to determine how to set it up. With PHPStorm you'd go to the run/debug configuration and add a configuration for 'PHP Script'. After the configuration is added you can then just select that configuration and hit the debug button to run it.
  14. I would suggest your not run your server scripts via apache. You're adding extra layers of complexity by doing that which may cause issues you wouldn't have otherwise. Run your scripts via the CLI interpreter instead.
  15. How is your controller processing your ajax request? What is the code you currently have to build your form? Your PRE_SUBMIT handler is passed an event object and you can get the posted data through that. You can use that to modify your form as needed. For example, I have a form that does: $builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event){ $this->updateTypeConfigurationForm($event); }); The updateTypeConfigurationForm method modifies the form based on the selected type which is a select field in the form. private function updateTypeConfigurationForm(FormEvent $event){ $data = $event->getData(); $type = null; if (isset($data['answerType'])){ $type = $data['answerType']; } $form = $event->getForm(); $form->add('typeConfiguration', new SurveyAnswerTypeConfigurationForm(), [ 'required' => false , 'label' => 'Type Configuration' , 'error_bubbling' => true , 'type' => $type ]); }
  16. Many specs and implementations do exist, for example HTTP and WebSocket. If you don't want to use any of the existing protocols though then you need to develop your own. Developing your own protocol involves solving problems like how to know when a complete message has arrived, how to distinguish between different messages, how to detect failures, etc. If you'd rather not deal with designing a protocol of your own, use one of the existing ones. HTTP is popular because it's very flexible and is widely supported. The main disadvantage to HTTP is that generally uni-directional and stateless. WebSocket is fairly new still but I suspect it will gain popularity as a generic bi-directional protocol with good library support.
  17. Yes, each packet can take a completely different route across the internet. Due to lag and what not, yes they can be received out of order by the target system. All this however is handled by the TCP protocol and not something you need to worry about yourself. By the time the data reaches your application it is guaranteed to be intact and in-order. When TCP splits the data into packets and sends them each packet sent is tagged with a sequence number. The receiving end tracks which sequence numbers it has received and re-arranges the received data into the correct order if necessary. In addition it will send back and ACKnowledgement packet to the sender telling it that the data has been successfully received. If the sender doesn't get an ACK for a given packet it will keep trying to re-transmit that packet until it's received successfully. If you're interested in more details about how it all works I'd suggest you read up on the TCP protocol and maybe install something like Wireshark and monitor some actual traffic. Both, to some extent. The OS has a buffer that will hold data until it can be successfully sent. If that buffer is full then attempting to send more data will fail. For data that the OS has already accepted and placed into this buffer it will automatically re-transmit as necessary until it receives an ACK. When the buffer is full and a write would fail one of two things could happen. If your socket is in blocking then the OS will sleep for a bit and try again later. This results in your program being paused on the write() call until the data can be sent (or some other error occurs). If your socket is non-blocking then the OS will return an error code indicating the buffer is full and you need to try again. In this instance it's up to your program to keep track of the data and attempt to send it again later. React uses non-blocking sockets and handles this buffering of written data for you. If write() returns false it means that react's buffer is full and you should not write any more data until after a drain event is received, however looking at the code it appears as though it currently doesn't actually prevent you from adding more data. That's something you have to build into your protocol. For example HTTP knows what is headers vs content by looking for the first "\r\n\r\n" sequence. Some other methods involve pre-pending the length of the content, for example you could write data such as "$length\n$json" where $length is strlen($json). On the receiving end you'd read the first line to get the length then read $length bytes more from the stream.
  18. Why do you need such a specific check?
  19. Don't add an extra item at the top to represent the currently selected item. Just add a selected attribute to the appropriate option value. You should also clean up your spacing in your html tags some, like remove the space between value and =, it'll read better and look nicer. <?php $smt = $DB_con->prepare('select ComID, ComputerName From ComputerNameView ORDER BY ComputerName'); $smt->execute(); ?> <select name="computername" id="computername" class='form-control'> <?php foreach ($smt as $row): ?> <option value="<?=$row["ComID"]?>" <?=($ComID==$row['ComID'])?'selected':''?>><?=$row["ComputerName"]?></option> <?php endforeach; ?> </select> That is assuming you have a variable named $ComID that represents the currently selected ID. Change that variable to whatever is appropriate for your actual code.
  20. svn ls ^/Should show only your branches, tags, and trunk directories. Then to make a tag something you copy it to the tags directory under a new name, for example: svn cp ^/trunk ^/tags/release-1.0 -m "Release 1.0 tag" Branching is the same, but then you copy to the branches directory then switch to the new copy. svn cp ^/trunk ^/branches/foo-feature -m "A branch to work on the new foo feature" svn switch ^/branches/foo-feature To merge a branch back into trunk switch back to trunk and use the merge command. svn switch ^/trunk svn merge ^/branches/foo-feature . # Check for conflicts or any problems. Once everything is good svn commit -m "Merge foo feature"
  21. Use svn move to move the files. Eg: svn move addFile.php addUser.php alertmanagement.php ... trunk/ After the commit just rename your working copy folder to move it out of the way so you can check out a new copy. daniel@daniel:~/NetBeansProjects$ mv development.xxxx.com development.xxxx.com-old daniel@daniel:~/NetBeansProjects$ svn co svn://development.xxxx.com/trunk development.xxxx.com
  22. Yea, if it lets you commit fine then go ahead and get things organized. I'm not sure why it shows as revision 0 when you do an info check. You can try doing an info check against the URL and see if that is any different: svn info svn://development.xxxx.comOr ask for the log and ensure it shows all your history: svn log svn://development.xxxx.comWhat's the structure of the files and folders under /usr/local/svn/ like? Post the output of some ls -l commands (or tree if you have it).
  23. If you use inline instead of attachment it'll still display in the browser while also using the custom filename (in chrome and firefox at least).
  24. Yes, this is due to daylight saving time. For areas affected by DST there is no 2:00 to 2:59 am on that day. The time goes directly from 1:59:59am to 3:00:00am
  25. That seems incorrect. That implies that you've made no revisions since creating your repository, but you claim above to be on revision 11. The root directory should always be on the most recent revision.
×
×
  • 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.