Guide

Come Creare un Servizio Windows in .Net

Questo articolo affronta la creazione di un servizio windows .net con tre approcci diversi. Il più comune è quello basato su un timer (timer-based service), che semplicemente invoca il thread in background periodicamente…

Esistono anche le altre due alternative, a single thread e multi thread. Mentre l’approccio a timer è il più semplice, quello a single thread e sopratutto quello multi thread offrono alcuni vantaggi aggiuntivi.

La scrittura di un servizio .net è molto semplice. Bisogna semplicemente creare un progetto usando il Windows Service template. Poi bisogna inizializzare il servizio nell’evento OnStart e gestire la terminazione nell’evento OnStop aggiunto dal modello di Visual Studio.

protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary
// to stop your service.
}
La soluzione con il Timer

Naturalmente, non è molto difficile far richiamre le funzioni che effettuano il lavoro vero e proprio, bisogna solo scrivere poche righe di codice per gestire il timer che attiva le chiamate periodicamente.
L’approccio a timer è il metodo più comune ed è probabilmente il più semplice da scrivere e capire. Devi creare un timer sull’evento OnStart ed agganciare le funzioni che effettuano il lavoro al timer.
Ecco l’esempio della soluzione a Timer:

// declare class-level variable for the timer
private Timer serviceTimer;

protected override void OnStart(string[] args)
{
TimerCallback timerDelegate =
new TimerCallback(DoWork);

// create timer and attach our method delegate to it
serviceTimer =
new Timer(timerDelegate, null, 1000, _interval);
}
DoWork è il metodo che contiene il codice per fare quello che vuoi periodicamente, quando scatta il timer.
Il prossimo listato mostra l’implementazione del metodo DoWork che effettua semplicemente la scrittura nell’EventLog.

private void DoWork(object state)
{
if (_workStartTime != DateTime.MinValue)
{
// probably check how much time has elapsed since work
// started previously and log any warning
EventLog.WriteEntry(“Warning! Worker busy since ” +
_workStartTime.ToLongTimeString(),
System.Diagnostics.EventLogEntryType.Warning);
}
else
{
// set work start time
_workStartTime = DateTime.Now;

// Do some work
// Note: Exception handling is very important here
// if you don’t, the error will vanish along with your worker thread
try
{
EventLog.WriteEntry (“Timer Service Tick :” + DateTime.Now.ToString());
}
catch (System.Exception ex)
{
// replace this with some robust logging technique
EventLog.WriteEntry(“Error! ” + ex.Message,
System.Diagnostics.EventLogEntryType.Error);
}

// reset work start time
_workStartTime = DateTime.MinValue;
}
}

Per ogni evento del timer si controlla se il lavoro che si sta eseguendo deriva dal precedente evento. Questo viene testato settando la variabile “_workStartTime” a DateTime.Now quando la funzione DoWork parte. La variabile è resettata a DateTime.MinValue quando tutto il lavoro da eseguire è completato.
Se la funzione sta ancora girando ed non ha finito registriamo un warning.

Si noti la gestione degli errori nella funzione DoWork. Se non gestite le eccezzioni e ne capitasse una, non riuscireste mai a sapere che errore è accaduto ed il vostro worker thread si limiterebbe a morire. Il servizio continuerebbe a funzionare normalmente senza sapere che il thread è terminato.

Con poche righe di codice abbiamo realizzato quello che volevamo. Ma ci sono altri modi per fare la stessa cosa ma in modo più elegante, vediamo come.

Alternativa 1: Usiamo un Thread Separato

La prima volta che ho scritto un servizio, mi son chiesto se potevo fare qualcosa di simile al listato sotto piuttosto che prendermi la briga di aggiungere un timer.

Il servizio instoppabile

protected override void OnStart(string[] args)
{
while (true)
{
// do some work

// idle
Thread.Sleep(0, interval, 0)
}
}
Ma subito mi son reso conto che cosi facendo il servizio sembrava rimaner appeso. Quando facevo partire il servizio non ottienevo una risposta da windows fintantoche il servizio non si bloccava nell’evento OnStart e il sistema operativo segnalava un errore. Un altro problema stava nel fatto che cosi facendo non si poteva dire al servizio di arrestarsi, perchè girava sempre nell’evento OnStart!

Per risolvere allora possiamo fare qualcosa di simile (noi dobbiamo invocare il metodo worker di lavoro). Useremo un thread separato. Iniziamo dichiarando alcune variabili di classe.

// This is a flag to indicate the service status
private bool serviceStarted = false;

// the thread that will do the work
Thread workerThread;

Il listato sotto mostra l’esempio con il single thread nell’evento OnStart

protected override void OnStart(string[] args)
{
// Create worker thread; this will invoke the WorkerFunction
// when we start it.
// Since we use a separate worker thread, the main service
// thread will return quickly, telling Windows that service has started
ThreadStart st = new ThreadStart(WorkerFunction);
workerThread = new Thread(st);

// set flag to indicate worker thread is active
serviceStarted = true;

// start the thread
workerThread.Start();
}
Il codice precedente istanzia un thread separato e vi attacca la nostra funzione “WorkerFunction”, nella quale metteremo il codice per eseguire i nostri task. Poi fa partire il thread e fa terminare l’evento OnStart, cosi che Windows non pensi che il servizio sia rimasto appeso.

L’implementazione della funzione WorkerFunction con l’implementazione single-thread

///

/// This function will do all the work
/// Once it is done with its tasks, it will be suspended for some time;
/// it will continue to repeat this until the service is stopped
///

private void WorkerFunction()
{
// start an endless loop; loop will abort only when “serviceStarted”
// flag = false
while (serviceStarted)
{
// do something
// exception handling omitted here for simplicity
EventLog.WriteEntry(“Service working”,
System.Diagnostics.EventLogEntryType.Information);

// yield
if (serviceStarted)
{
Thread.Sleep(new TimeSpan(0, interval, 0);
}
}

// time to end the thread
Thread.CurrentThread.Abort();
}
Questa funzione girà in un loop senza fine fino a quando la variabile “serviceStarted” non diventa false. Il parametro viene settato nell’evento OnStop come potete vedere nel listato sotto.

Implementazione dell’evento OnStop con l’implementazione single-thread

protected override void OnStop()
{
// flag to tell the worker process to stop
serviceStarted = false;

// give it a little time to finish any pending work
workerThread.Join(new TimeSpan(0,2,0));
}
Notate che abbiamo lasciato un tempo ragionevole al thread che esegue il lavoro (2 minuti nel listato sopra) affinchè riesca a completare il suo task. Ma se non ci riuscisse in quel tempo, la clausola “workerThread.Join” lo fa terminare in qualunque caso.

Alternativa 2: Usiamo più Threads (Multiple Threads)

Questa tecnica è molto simile alla precedente con il single-thread, ma il concetto viene esteso usando più threads. Questo pattern può semplicemente rimpiazzare l’alternativa a single-thread, usando soltanto un thread nell’array. Ora vediamo il codice. Per prima cosa dichiariamo le variabili di classe.

Variabili di classe nell’approccio multithread

// array of worker threads
Thread[] workerThreads;

// the objects that do the actual work
Worker[] arrWorkers;

// number of threads; typically specified in config file
int numberOfThreads = 2;
Vediamo l’evento OnStart

protected override void OnStart(string[] args)
{
arrWorkers = new Worker[numberOfThreads];
workerThreads = new Thread[numberOfThreads];
for (int i =0; i < numberOfThreads; i++)
{
// create an object
arrWorkers[i] = new Worker(i+1, EventLog);

// set properties on the object
arrWorkers[i].ServiceStarted = true;

// create a thread and attach to the object
ThreadStart st = new ThreadStart(arrWorkers[i].ExecuteTask);
workerThreads[i] = new Thread(st);
}

// start the threads
for (int i = 0; i < numberOfThreads; i++) { workerThreads[i].Start(); } } Per prima cosa creiamo un array di Worker objects della dimensione che ci necessita. Il Worker è una classe separata che contiene un metodo chiamato ExecuteTask che fa tutto il lavoro. Poi andiamo a dichiarare un array di threads e ci attacchiamo un Worker object ad ogni thread, specificando ExecuteTask come metodo da chiamare. Infine, lanciamo tutti i threads. Dopo di che, il metodo OnStart termina e il controllo ritorna a Windows. Il metodo “ExecuteTask” è simile ai metodi worker dei precedenti esempi. Il metodo ExecuteTask (Worker) nell’approccio multithread public void ExecuteTask() { DateTime lastRunTime = DateTime.UtcNow; while (serviceStarted) { // check the current time against the last run plus interval if ( ((TimeSpan) (DateTime.UtcNow.Subtract(lastRunTime))).TotalSeconds >= _interval)
{
// if time to do something, do so
// exception handling omitted here for simplicity
_serviceEventLog.WriteEntry(
“Multithreaded Service working; id = ” + this._id.ToString(),
EventLogEntryType.Information);

// set new run time
lastRunTime = DateTime.UtcNow;
}

// yield
if (serviceStarted)
{
Thread.Sleep(new TimeSpan(0,0,15));
}
}

Thread.CurrentThread.Abort();
}
Notate che usiamo un piccolo intervallo per il metodo Thread.Sleep (15 secondi) per aumentare la reattività quando il servizio viene arrestato. Se non avessimo fatto cosi il servizio sarebbe rimasto bloccato fino alla terminazione del periodo _interval (oppure sarebbe semplicemente andato in time out se l’intervallo fosse stato troppo lungo).

L’evento OnStop è molto simile a quello trattato nell’approccio single-thread, ad eccezzione del fatto che qui dobbiamo notificare a tutti i thread lo stop.

Metodo OnStop per l’approccio multithread

protected override void OnStop()
{
for (int i = 0; i < numberOfThreads; i++)
{
// set flag to stop worker thread
arrWorkers[i].ServiceStarted = false;

// give it a little time to finish any pending work
workerThreads[i].Join(new TimeSpan(0,2,0));
}
}

Compariamo i vari approcci

Oltre che per l’implementazione i tre metodi differisco in alcuni aspetti:

Performance Non ci sembra essere molta differenza anche se l’approccio col timer richiede un paio di thread supplementari per il timer.
Implementazione (codifica) L’approccio timer è il più semplice, quello a single-thread è leggermente più complesso e l’approccio con multithread richiede più codice.
Possibilità di eseguire più thread Ciò è disponibile nell’approccio multithread. Il numero di thread può essere specificato nel file di config, cosi chè è molto semplice aumentare o diminuire i worker thread. E’ inoltre possibile realizzare più thread anche nell’approccio timer, ma con codice aggiuntivo.
Possibilità di settaggio dei ritardi per ogni thread Con l’approccio multithread è possibile specificare il ritardo per ogni thread nel file di configurazione. L’approccio con il timer non è cosi flessibile; il timer interviene ad orari specifici, e per ottere la stessa cosa bisogna fare qualche accrocchio.
Il ritardo di un thread disturba l’esecuzione degli altri thread Questo non può accadere nell’approccio multithread. Ogni thread è in esecuzione ed ignora gli altri thread. Invece nell’approccio con il timer, se intervalliamo più worker thread, quello con tempo di esecuzione più lungo determina quando l’esecuzione successiva sarà possibile.

E ‘anche possibile generare più servizi all’interno dello stesso processo del servizio principale. Il listato sotto mostra il metodo Main() che il modello dei servizi .net genera.

static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;

// More than one user service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new System.ServiceProcess.ServiceBase[]
// {new Service1(), new MySecondUserService()};
//
ServicesToRun = new System.ServiceProcess.ServiceBase[]
{ new Service1() };

System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
Non ho parlato di questa opzione in questo articolo perché abbiamo considerato la possibilità di creare più thread in background in un determinato servizio, piuttosto che avere più servizi in esecuzione nello stesso processo.

Conclusioni

Questo articolo ha spiegato nel dettaglio come creare un servizio in .net con tre differenti approcci. Metre l’approccio col timer è il più semplice, quello multithread è molto flessibile e può lavorare con uno o più thread. Quest’ultimo approccio permette anche di gestire diversi ritardi per ogni thread. Qualsiasi ritardo di esecuzione nel metodo worker di un thread non interferirà con l’esecuzione degli altri thread.

Guide

Recensione Logitech G502 Proteus Spectrum

Logitech è un’azienda svizzera con una lunga storia alle spalle nel mondo del gaming, dai mouse alle tastiere, dalle webcam agli altoparlanti. Oggi daremo un’occhiata al mouse gaming Logitech G502 Proteus Spectrum, predecessore del Proteus Core, nonché aggiornamento evolutivo volto all’integrazione dell’illuminazione RGB personalizzabile.

mouse

Design
Misurando 13,2 x 7,5 x 4,0 cm. (A x L x P), Proteus Spectrum rimane in grande parte invariato rispetto al suo predecessore per quanto riguarda la posizione dei pulsanti, il colore e il design complessivo. L’unica differenza percepibile è che lo Spectrum vanta un design completamente nero, fatta eccezione per la rotella di scorrimento grigio scuro e un indicatore dei dpi color argento.
Presenta una finitura opaca sul corpo e sui pulsanti sinistro e destro, che dopo più di 3 settimane di utilizzo intenso reggono bene contro il sudore e il grasso delle dita. Il resto degli 11 pulsanti programmabili, inclusa la rotella di scorrimento, ha una finitura lucida.
Entrambi i lati del mouse hanno impugnature in gomma texturizzata, mentre il lato sinistro presenta anche una comoda scanalatura per il pollice con tre pulsanti: avanti e indietro sopra il pollice e il pulsante DPI (per passare istantaneamente e temporaneamente da una impostazione all’altra). L’unico svantaggio potenziale è che il solco e la posizione dei pulsanti rendono lo Spectrum un mouse inutilizzabile per mancini.
Continuando a parlare dei pulsanti programmabili, troviamo due pulsanti di regolazione della sensibilità, a sinistra del pulsante sinistro del mouse, per aumentare o diminuire i DPI. Sotto la rotella di scorrimento ci sono altri due pulsanti: il pulsante in alto modifica il feed di scorrimento della rotella, mentre mentre quello inferiore consente di “navigare” fra i tre profili di gioco che è possibile impostare nel software proprietario Logitech. La rotella di scorrimento dual-mode stessa è rigata, il che aiuta a scorrere con precisione e può essere fatto clic in tre direzioni (sinistra, destra e al centro).
Il mouse pesa 121 g. (168 g. incluso il cavo), rendendolo più pesante rispetto ai concorrenti come il Corsair M65 PRO RBG (115 g.), ma leggermente più leggero del Razer Mamba Tournament Edition (133 g.). Presentato come mouse personalizzabile, viene fornito con una scatola di plastica contenente cinque pesi da 3,6 grammi ciascuno, per un totale di 18 grammi che puoi aggiungere tramite una copertura magnetica situata nella parte inferiore del mouse.
Se preferisci che il tuo mouse sia più pesante al centro, per esempio, o più sintonizzato verso il palmo, è facile sperimentare. Ho provato il mouse con varie configurazioni di peso: è difficile tra 2 o 3 pesi, c’è invece un cambiamento evidente tra 0 e 4-5 pesi.
La più grande differenza dal Proteus Core è l’aggiunta della illuminazione RGB personalizzabile nel Proteus Spectrum. Sebbene sia possibile utilizzare il software per impostare diversi profili ed effetti di illuminazione, non c’è una vera ragione nel farlo. Le uniche parti LED del mouse sono il logo della serie G sul corpo del mouse, che è quasi sempre coperto dal palmo della mano, e l’indicatore del profilo dei DPI.

Software
Il software Logitech per il G502 Proteus Spectrum offre una miriade di opzioni per la messa a punto e la personalizzazione del mouse. Le configurazioni possono essere memorizzate sul mouse stesso tramite la memoria integrata.
Anche se all’inizio il tutto può risultare complicato, ho scoperto che le varie impostazioni sono estremamente facili da regolare. La maggior parte delle persone potrebbe semplicemente modificare una o due cose, come la gamma dei DPI. Ciò ti consente di cambiarli al volo dall’alto verso il basso a seconda che tu stia correndo e sparando.
Puoi impostare fino a cinque valori per singolo profilo di gioco (3 profili al massimo), con un valore massimo di 12.000 DPI (a saltelli di 50 DPI), con la possibilità di scegliere con quale pulsante personalizzabile alzarli o abbassarli.
Un’altra caratteristica del software Logitech che mi è veramente piaciuta è chiamata “Regolazione in base alla superficie”, e si tratta di una procedura guidata che consente al mouse di esaminare la superficie e configurarsi per funzionare in modo ottimale, indipendentemente dal fatto che si tratti di un tavolo o di un mousepad. Esistono impostazioni predefinite per i tappetini Logitech G240 – G440 – G640, ma è possibile aggiungere regolazioni per qualsiasi tappetino di qualsiasi marca e superficie. È davvero scorrevole, e il processo di configurazione è semplicissimo.
Infine c’è l’illuminazione RGB, che sembra un must per qualsiasi periferica venduta in questi ultimi anni. Come già accennato, l’unica parte illuminata è la grande G sul corpo principale e l’indicatore DPI. Il colore, la luminosità, la velocità e gli effetti (ciclo colori o dissolvenza ciclica) possono essere modificati e salvati secondo per i 3 profili esistini. Puoi anche sincronizzare l’illuminazione con altri dispositivi Logitech come tastiera, cuffie o mousepad, se RGB anche questi.
Nel complesso, l’illuminazione di questo mouse non aggiunge molto all’esperienza, soprattutto perché la nostra mano copre ogni illuminazione. Veramente superficiale illuminare ciò che viene nascosto durante l’utilizzo vero e proprio.

Prestazioni
Nei il mio personale utilizzo, lo Spectrum è risultato comodo e reattivo sia per i giochi che per l’uso quotidiano. Durante i test con Fortnite, sono stato in grado di sfruttare al meglio i pulsanti programmabili i quali, per la maggior parte, sono ben posizionati, specialmente quelli situati vicino al pollice. Personalmente ho scoperto che, a causa della loro posizione, i pulsanti di regolazione dpi preimpostati richiedono concentrazione per essere usati efficacemente. Se non stai attento, può capitare di premete accidentalmente il pulsante sinistro del mouse, quando tutto ciò che vuoi fare è aumentare o diminuire i DPI. Fortunatamente, è facile riassegnare queste funzioni ai pulsanti che ritieni più intuitivi. Il pulsante DPI, che utilizzo per la mira di precisione, è facile da raggiungere, e la sua posizione appena oltre la punta del pollice impedisce qualsiasi clic involontario.

Verdetto
Il Logitech G502 Proteus Spectrum è un mouse da gaming ben costruito, ergonomico, con un sacco di funzioni utili e prestazioni eccellenti. Il software consente di effettuare regolazioni che possono essere sia radicali che superficiali, e i pesi inclusi sono un bel tocco che migliora ulteriormente la precisione. Invece penso che l’illuminazione RGB sia stata inserita con troppa superficialità. Tuttavia, ho apprezzato le funzionalità, l’attenzione ai dettagli e la suite software.

Guide

Come Realizzare Crawler da PHP-CLI

Oggi vi voglio parlare di Crawler scritti in PHP, anche se in un articolo precedente ho già affrontato questo argomento.
Partiamo dall’esigenza che mi ha spinto a costruire questo piccolo spider in php: devo popolare nella cache le pagine del mio sito…
Soluzione intrapresa: leggo tutte le pagine (o meglio tutti gli url all’interno del mio dominio) in un momento di basso traffico (la notte).

Il primo problema da affrontare è stato l’esecuzione di script php da riga di comando, come fare?.

In Php è possibile eseguire tutti gli script normalmente eseguiti da apache e php anche a riga di comando tramite PHP CLI.

L’acronimo CLI è l’abbreviazione di Command Line Interface (‘Interfaccia da Linea di Comando’), una modalità di accesso al sistema attraverso un interprete per comandi testuali; questi comandi sono espressi essenzialmente attraverso delle stringhe alfanumeriche che possono essere eseguite da altre applicazioni.

Nel caso di PHP, per esempio, sarà possibile utilizzare le CLI per integrare determinati programmi con funzionalità messe a disposizione da questo linguaggio.

Le differenze tra il tradizionale sviluppo di pagine Web e l’utilizzo di PHP tramite interfaccia testuale non sono poche, è possibile però elencarne le principali:

l’output generato tramite CLI è “pulito” (di default non vengono introdotti degli headers), l’utilizzatore lo vede esattamente così com’è cioè nel modo in cui viene prodotto;
l’output presenta una formattazione minima se non assente, il formato in “semplice testo” permette una più semplice elaborazione da parte di programmi di terze parti;
il tempo di esecuzione dei comandi è solitamente molto più veloce di quello richiesto nel caso in cui debba essere caricata una pagina Web;
l’utilizzatore può agire dall’interno dello spazio fisico dedicato a PHP nel file system;
gli input vengono gestiti a livello di runtime e cioè nel momento in cui un programma viene eseguito, cosa non possibile per le pagine Web dove la definizione degli input precede l’esecuzione dello script.
In molti potrebbero chiedersi come mai si è sentita l’esigenza di integrare in PHP la possibilità di inviare istruzioni tramite linea di comando, esistono infatti linguaggi molto più collaudati come per esempio Perl grazie ai quali interagire con il sistema. L’obiezione è sensata, ma nello stesso tempo è necessario elencare i vantaggi derivanti dall’introduzione della CLI in questo linguaggio:

non è necessario imparare altri linguaggi come appunto Perl, Bash o Awk;
consente di riutilizzare componenti e codici preesistenti;
mette a disposizione uno strumento per sfruttare al meglio il multithreading in PHP5;
permette di integrare nuove funzionalità in applicazioni PHP preesistenti;
introduce la possibilità di automatizzare e programmare (CRON) i processi, cosa impossibile tramite il tradizionale sviluppo limitato alle pagine Web;
consente di creare interfacce grafiche (GUI) utilizzando PHP e GTK.
Se volete maggiori dettagli su PHP Cli potete vedere questo tutorial in inglese.

Data questa piccola introduzione a PHP CLI vediamo in dettaglio come costruire l’algoritmo di ricerca delle pagine del Crawler. L’idea è quella di usare una struttura dati ti tipo Coda FIFO (FIFO Queue), dove memorizzare tutti gli url del sito mano a mano che vengono reperiti.

Recupero gli url dalla home page del sito e li salvo nella coda (urls).

Setto il contatore di avanzamento a zero (index=0).

Prendo l’url i-esimo nella coda (urls[index]) dalla coda e lo leggo la pagina ricavando nuovi url.

Salvo gli url nella coda se appartengono al dominio del sito e se non sono già presenti nella coda.

Incremento il contatore di avanzamento (index++).

Riprendi l’url i-esimo nella coda (urls[index]) dalla coda e leggo la pagina ricavando nuovi url.

Proseguo finchè il contatore è minore della dimensione della coda (index < count(urls) -1).
Cosi facendo sono sicuro di processare tutti gli url presenti nel sito.
Vediamo l’implementazione del crawler in PHP:

/*
Script Name: Crawler.php
Description: Un semplice crawler in php per la lettura di tutte le pagine di un sito da lanciare da PHP CLI
Autore: Gianluca Moroni http://www.gianjey.it
Autore URI: http://www.programmandofacile.it
Version: 1.0
*/

function LeggiUrls($urlPagina,$urlDominio,&$queue) {
$result = file_get_contents($urlPagina);
// Se ho scaricato qualcosa
if( $result )
{
// Cerco gli href sulla pagina
preg_match_all( ‘/<a.+?href=”(http:\/\/www.[^0-9].+?)”/’, $result, $output, PREG_SET_ORDER );

// Ciclo tutti i risultati e verifico se gli url sono nel dominio del sito
// e li aggiungo alla coda se non erano già presenti
foreach( $output as $item )
{
//print_r($item[1]);
$host = parse_url($item[1], PHP_URL_HOST);
if($host == $urlDominio) {
if (!in_array($item[1], $queue)) {
array_push($queue,$item[1]);
}
}
}
}
}

// La coda che contiene gli url
$queue = array();

// Leggo il sito dallo riga di comando, deve avere http:// davanti al dominio
$urlSito = $_SERVER[“argv”][1];
$host = parse_url($urlSito, PHP_URL_HOST);

// Variabile per ritardare le chiamate
// se non passata da riga di comando viene settata ad un secondo
$DELAY = 1;

if(isset($_SERVER[“argv”][2])) {
$DELAY = $_SERVER[“argv”][2];
}

// Leggo gli url della prima pagina e li aggiungo alla coda
LeggiUrls($urlSito,$host,$queue);

$index=0;

// Ciclo e recupero tutte le pagine nella coda lanciando
// la funzione che mi cerca gli url e li aggiunge alla coda
// se nel dominio e se non presenti nella coda
while($index < count($queue)-1) { LeggiUrls($queue[$index],$host,$queue); $index++; sleep($DELAY); echo(“Pagina:” . $queue[$index] . “\n”); echo(“Index:” . $index . “\n”); echo(“Count:” .count($queue) . “\n”); } print_r(‘Finito’); ?>

Per lanciare lo script salvate la pagina in un file chiamato ad esempio “Crawler.php”, date i permessi di esecuzione se siete sotto Linux, e scrivete a riga di comando:

php Crawler.php http://www.sito.it 5
Cosi facendo lanciate lo spider verso http://www.sito.it con un intervallo di 5 secondi tra una chiamata e l’altra, ed ecco che il gioco e fatto, tutte le pagine del sito verranno lette e la cache verrà popolata.

Partendo da questo semplice esempio, e giocando un po con le regular expression potrete costruire crawler che recuperano tutto quello che vi serve.
Spero di essere stato chiaro nell’esposizione, se avete dubbi non esitate a contattarmi.

Guide

Strutture dati – Liste Concatenate

In informatica, una lista concatenata è una struttura dati costituita da un gruppo di nodi che insieme rappresentano una sequenza…

In quest’articolo vedremo la più semplice delle liste, la lista semplicemente concatenata. Esistono anche altri tipi di lista, le liste doppiamente concatenate, le liste circolari semplicemente concatenate e le liste circolari doppiamente concatenate ma per approfondimenti guardate pure wikipedia

Lista Concatenata

La lista semplicemente concatenata (Singly-linked List) è una lista di elementi in cui gli elementi possono essere posizionati ovunque nella memoria heap. Tutti gli elementi della lista sono collegati tra loro utilizzando un campo per memorizzare l’indirizzo del nodo successivo.
La lista semplicemente concatenata (Singly-linked List) ha una dimensione dinamica e può essere determinata solo a run-time e non in fase di compilazione.
L’immagine sopra mostra una Singly-linked List. Ci sono quattro elementi nella lista. Il puntatore “Head” è il puntatore che punta al primo elemento della lista.

Aggiunta di un nuovo elemento alla lista

 

L’algoritmo è semplice:

– Se la lista è vuota dobbiamo inserire un nuovo elemento (o nodo) come nodo di partenza.
– In caso contrario, si attraversa la lista esistente per ottenere il puntatore all’ultimo nodo;

Creiamo un nuovo nodo.
Cambiamo il puntatore successivo dell’ultimo nodo al nuovo nodo.
Il puntatore del prossimo nuovo nodo punterà a NULL e sarà l’ultimo nodo della lista.

Implementazione di una Lista Concatenata in C
#include
#include

struct node{
int data;
struct node *next;
};

struct node* add(struct node *head, int data){
struct node *tmp;

if(head == NULL){
head=(struct node *)malloc(sizeof(struct node));
if(head == NULL){
printf(“Error! memory is not available\n”);
exit(0);
}
head-> data = data;
head-> next = head;
}else{
tmp = head;

while (tmp-> next != head)
tmp = tmp-> next;
tmp-> next = (struct node *)malloc(sizeof(struct node));
if(tmp -> next == NULL)
{
printf(“Error! memory is not available\n”);
exit(0);
}
tmp = tmp-> next;
tmp-> data = data;
tmp-> next = head;
}
return head;
}

void printlist(struct node *head)
{
struct node *current;
current = head;
if(current!= NULL)
{
do
{
printf(“%d\t”,current->data);
current = current->next;
} while (current!= head);
printf(“\n”);
}
else
printf(“The list is empty\n”);

}

void destroy(struct node *head)
{
struct node *current, *tmp;

current = head->next;
head->next = NULL;
while(current != NULL) {
tmp = current->next;
free(current);
current = tmp;
}
}
void main()
{
struct node *head = NULL;
head = add(head,1); /* 1 */
printlist(head);

head = add(head,20);/* 20 */
printlist(head);

head = add(head,10);/* 1 20 10 */
printlist(head);

head = add(head,5); /* 1 20 10 5*/
printlist(head);

destroy(head);
getchar();
}

Guide

Come Raggruppare una Serie di Dati in Excel

Con Excel possiamo racchiudere e nascondere una serie di dati disposti su più righe. Tali dati potranno essere nuovamente espansi premendo un pulsante contrassegnato con il simbolo + (più). A tale scopo è necessario portarsi sul menu Dati, selezionare le celle da raggruppare, premere il tasto Raggruppa, scegliere Righe, Ok.

A questo punto se premiamo il tasto – (meno) che è apparso davanti alle intestazioni di riga, i valori che avevamo selezionato vengono compressi, se premiamo nuovamente il tasto, che ora ha assunto il simbolo +, torniamo ad espandere e vedere tali dati. Questa funzionalità di Excel è utile se vogliamo ad esempio nascondere i dati di un’operazione e lasciare visibile solo il totale, ma con la possibilità di poterli sempre visionare con la semplice pressione dell’apposito pulsante.

Il raggruppamento può essere effettuato non solo su un livello, ma su più sottolivelli. Espandendo un livello vedremo il relativo sottolivello. Se abbiamo la necessità di raggruppare dati disposti su più colonne e non su più righe, ci portiamo sul menu Dati, selezioniamo le celle disposte su più colonne, premiamo il tasto Raggruppa, poi Colonne, Ok. Il tasto meno che dovremo premere appare ora sulle intestazioni di colonna, e non di riga.

Se non abbiamo più bisogno di questa funzionalità, per separare definitivamente le righe o le colonne, basterà premere dal menu Dati il tasto Separa, dopo aver selezionato le righe o le colonne interessate, poi scegliere Righe o Colonne, infine tasto Ok.