Problemi Socket

Ciao a tutti :)

Scusate se sono nuovo e subito parto con le richieste, ma ho un problema tanto strano quanto bizzarro.

Ho creato in php un motore socket per chat. In pratica con socket_create, bind, listen, accept, read e un opportuno ciclo infinito apro una porta del server e mi metto in ascolto.

Lo script viene avviato a riga di comando, e il Client l'ho invece costruito in Flash.

Funziona una meraviglia... sono quasi commosso... ;D  MA...

Ho due problemi che ho la sensazione siano essere collegati.

Il primo è che a volte (molto raramente in verità) le stringhe inviate vengono troncate. Per esempio invio "Ciao a tutti" e il server libera "utti". Non sono sicuro del dove e quando vengono persi i dati, ma temo al momento del socket_read.

Il secondo è il più fastidioso. Sta capitando spesso (un paio di volte al giorno) che il server si blocca. Gli utenti connessi rimangono connessi ma se inviano stringhe il server non risponde. Chi non è connesso e cerca di connettersi, ci riesce ma lo stesso non invia né riceve dati: il flash dà solo l'"ok connesso".

Una volta è successo che è stato in questa condizione un giorno intero tanto che sono stato costretto a killare il processo e riavviarlo. Ultimamente invece dopo 5 o 6 minuti si sblocca da solo liberando tutti insieme gli input avuti quando era bloccato (quindi riceveva tutto ma non liberava il flusso).

Mi sono fatto una idea... Credo che a causa del primo problema si perda il byte di fine invio (\r o \n o \0) per qualche utente e il server rimanga in ascolto in attesa appunto di quel byte da quell'utente. Però a questo punto basterebbe un altro invio per averlo e si dovrebbe sbloccare subito... invece no.

Qualcuno ha avuto esperienze in merito o ha una idea di come possa risolvere? :)

Grazie e scusate la prolissità.

inviato 10 anni fa
chumkiu
X 0 X

ehm... non pretendo una soluzione... vanno bene anche delle idee :D

risposto 10 anni fa
chumkiu
X 0 X

Per prima cosa verificherei se il problema è casuale oppure deterministico (si verifica solo con determinate stringhe)

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Del tutto casuale....

risposto 10 anni fa
chumkiu
X 0 X

prova a realizzare, sia per il client che per il server, un sistema di logging degli eventi molto dettagliato. Magari potresti far scrivere su file di testo tutto ciò che avviene ed in quale istante. Potresti anche arrivare a registrare l'esecuzione di ogni istruzione o il valore delle diverse variabili durante l'esecuzione dei programmi.

In questo modo postresti effettuare un debugging "in differita".

 :bye:

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

provo... Avrei voluto tenermela come ultima chance visto che impegnerebbe un pò troppo il server (la chat è un gioco che va a comandi e si scrive parecchio), ma vedo che ormai è proprio l'ultima chance.

Una sola domanda:

che voi sappiate gli output (quelli di print e echo) che il server manda, nel momento in cui avvio lo script a riga di comando e metto il processo in background e gli dico espressamente che non voglio output (con dev/null), che fine fa? Rimane in memoria? Mi sono accorto di aver lasciato qualche echo qua e là e non vorrei centrassero.

grazie in ogni caso :)

risposto 10 anni fa
chumkiu
X 0 X

L'output viene comunque generato e passato al sistema operativo, come accade per tutte le istruzioni di I/O. Il sistema operativo poi lo scarta.

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Mi è venuta in mente una cosa.

Capita ogni tanto, per pagine e siti normali, che una volta che si richede una pagina se il server è particolarmente impegnato, tale richiesta viene persa in attesa ed è necessario fare un refresh.

E' plausibile pensare che succede lo stesso al mio script quando per esempio deve eseguire una query sql? Il mio scritp attende, prima di andare avanti, il risultato di Mysql. Magari per una ragione questa query si perde non producendo risultati. Il mio script attende pazientemente risposta ma questa arriverà solo molto dopo.

Nel caso fosse questo il problema... come posso risolvere?

Far eseguire le query aldifuori del mio script, o dare una specie di timeout?

Che ne pensi?

risposto 10 anni fa
chumkiu
X 0 X

Per verificare questa ipotesi ti conviene procedre loggando le varie fasi di una query, con il relativo esito ed il timestamp.

Potresti anche usare il log delle query lunghe presente in MySQL ma il sono più per la prima soluzione.

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Niente...

ho messo dei log su file prima e dopo ogni query e quando si è bloccato nessuna query risultava aperta! :(

risposto 10 anni fa
chumkiu
X 0 X

ma hai un'evidenza che lo script aveva intercettato la richiesta? E' importante per capire se è un problema di rete o di programma.

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Ho fatto così:

FileLog("inizio query");

mysql_query($query);

FileLog("fine query");

Al momento del blocco non c'era nessun "inizio query" rimasto senza fine query.

risposto 10 anni fa
chumkiu
X 0 X

Quindi le ipotesi sono 2:

o lo script non viene proprio lanciato oppure viene eseguito completamente. Giusto?

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Comunque non è la causa del blocco. Ovviamente segno anche la data a inizio e fine query e non ha mai un distacco di almeno un secondo tra l'inizio e la fine della richiesta al database.

risposto 10 anni fa
chumkiu
X 0 X

Un problema di rete quindi? Lo script non gira con apache, vero?

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

No direttamente php.

Volendo lo posso anche far eseguire tramite browser (e quindi apache) ma si sconnette dopo un'oretta. (mi và in timeout la pagina, rimane un pò e poi cade).

risposto 10 anni fa
chumkiu
X 0 X

A questo punto inizio a pensare che sia un problema di rete, forse il server non accetta più connessioni oltre una determinata soglia.

Le connessioni tra client flash e server PHP sono persistenti? Durano per tutta la chattata?

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

No perché altrimenti poi non si sbloccherebbe. o quanto meno ritornerebbe un errore. Invece poi riprende come se nulla fosse successo. Non penso sia problemi di rete perché le stringhe le riesci ad inviare al server, il server le riceve ma non le elabora! Le elabora solo quando si sblocca.

Faccio un esempio: sei dentro e si blocca. Se tu scrivi 20 volte "Ci sono?" non vedrai nulla, ma quando poi si sblocca (e si sblocca da solo) li vedrai tutte e 20. Ergo i dati arrivano ma non li elabora. Il tutto fa pensare che si blocca ad una riga per poi sbloccarsi. una specie di sleep. Ma non c'è causa.

Le connessioni sono persistenti.

risposto 10 anni fa
chumkiu
X 0 X

Allora... ho un pò di materiale.

Ho messo dei flag, in mezzo al codice che mi scrive su un file di testo la riga corrente dello script.

Ovviamente quando la chat funzionava mi dava sempre la riga in cui è in ascolto di dati (difficile beccarlo nel mezzo di un processo).

Quando si è bloccato ho controllato:

In tempi diversi mi ha dato righe diverse, sintomo che lo script cammina, lento ma cammina.

Si è bloccato in momenti in cui riassegnavo degli Array. Ha impiegato parecchi secondi per farlo (sempre che alla fine l'abbia fatto). Il che mi fa pensare a un problema di memoria e mi chiedo:

in una cosa del genere

while(true) {
$mioArray= Array()
// poi riempio questo array man mano 
}

$mioArray può raggiungere dimensioni grosse, ma nel momento in cui la svuoto con $mioArray=Array(), libero anche tutta la memoria? Se uso unset è più sicuro? Nel frattempo cerco di trovare info.

grazie scusate ma alla fine devo venirne a capo :)

risposto 10 anni fa
chumkiu
X 0 X

i due approcci per svuotare l'array dovrebbero essere equivalenti. Se il tuo PHP è stato compilato con l'opzione --enable-memory-limit potrsti usare la funzione memory_get_usage() per valurare il consumo di memoria del PHP.

Oppure potresti realizzare uno script bash per monitorare il consumo di memoria del processo, tipo ogni secondo e scriverlo in un file, in modo da poter verificare se i rallentamenti sono associati a dei picchi di consumo.

 :bye:

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

i due approcci per svuotare l'array dovrebbero essere equivalenti. Se il tuo PHP è stato compilato con l'opzione --enable-memory-limit potrsti usare la funzione memory_get_usage() per valurare il consumo di memoria del PHP.

Oppure potresti realizzare uno script bash per monitorare il consumo di memoria del processo, tipo ogni secondo e scriverlo in un file, in modo da poter verificare se i rallentamenti sono associati a dei picchi di consumo.

 :bye:

fatto...

Non è un problema di memoria.  :tichedoff:

risposto 10 anni fa
chumkiu
X 0 X

Uhm ho trovato un'altra impurezza  (o meglio erroraccio).

dentro un froeach su un array, richiamo una funzione (anzi più di una) che fa un foreach sullo stesso array (lo chiamo dentro con global). QUesto ho visto creare non pochi problemi, visto che sballa il puntatore di tale array. La cosa che non mi spiego è che non avrebbe mai dovuto funzionare visto che tale funzione la chiama ad ogni ciclo.

Comunque ora nelle funzioni faccio una copia dell'Array e faccio il foreach su di essa. Non mi piace molto in termini di prestazioni, visto che tale Array contiene gli utenti online con le loro caratteristiche (e se fossero molti...) Vediamo se è questa la causa.

Vedete qualche alternativa al creare una copia dell'array?

Inoltre, una volta che la funzione è stata chiamata e fa il suo bravo ritorno, tutte le variabili dentro dichiarate non occupano più memoria vero?

Dai che ne vengo a capo :)

risposto 10 anni fa
chumkiu
X 0 X

Quando passi una variabile ad una funzione quest'ultima riceve automaticamente una copia della variabile in questione, non l'originale.

Ecco perché funzionava loscript.

In gergo si diche che si tratta di un passaggio per valore (il contrario invece si chiama passaggio per indirizzo)

 :bye:

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

E infatti non la passavo alla funzione...

la trattavo come variabile globale.

es:

function miafunzione($p) {
    global $mioarr;
     foreach($mioarr as $kiave=>$valore) {
        //ecc.
     }
}
$mioarr=Array(); // lo riempio
foreach($mioarr as $k=>$v) {
  // varie righe
  miafunzione($mioparametro);
}
risposto 10 anni fa
chumkiu
X 0 X

Non avevo capito  :o

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Ovviamente ignoravo il funzionamento e l'esistenza dei puntatori ad array.

Ma ora che li conosco non capisco come abbia potuto funzionare finora...

Ora sto invece facendo lavorare una copia dell'Array nelle funzioni. Ma come posso evitare di copiarlo per intero, visto che può raggiungere notevoli dimensioni?

ho provato con i riferimenti

$copia= &$mioarr;

ma prevedibilmente il puntatore è lo stesso.

risposto 10 anni fa
chumkiu
X 0 X

perché non riporti tutto il codice che gestisce l'array? Magari posso consigliarti una eventuale gestione più efficiente.

 :bye:

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Non posso riportare tutto il codice perché sono 980 righe!

Prova a vedere un pò questo riassunto:

<?php

$arr= Array("ciao","mamma","guarda","come","mi","diverto");
function prova() {
     global $arr;
     foreach($arr as $k=>$v) {
          echo "prof(2):$k <br>";
          riprova();
     }
}
function riprova() {
     global $arr;
     foreach($arr as $k=>$v) {
     echo "<br>prof(3):$k <br>";
     }
}
foreach($arr as $k=>$v) {
     echo "<br><br>********<br>prof(1):$k <br>******<br>";
     prova();
}
?>

E guarda il suo comportamento! prof sta per "profondità"! nell'esempio la profondità 2 non viene gestita come dovrebbe: quando finisce di gestire la prof3 non completa il foreach credendo di aver finito.

Invece se cancelliamo la funzione riprova() e decommentiamo il richiamo, tutto funziona.

E' come se non riuscisse a gestire tre foreach sullo stesso array, mentre due si.

risposto 10 anni fa
chumkiu
X 0 X

Dall'array che hai riportato non si capisce perché tu voglia ciclare3 volte sugli stessi elementi. Spiegami come è strutturato il vero array.

risposto 10 anni fa
Gianni Tomasicchio
X 0 X

Allora...

L'array consiste nel contenitore di tutti gli utenti online con i loro parametri e valori (resource socket, nome, stanza e vari parametri di gioco).

Il primo foreach lo metto nel ciclo principale, quando il socket mi dice quali resource sono cambiati, li controllo con questo array (lo chiamo $clients) e elaboro l'input.

Quando elaboro l'input posso richiamare altre funzioni. Come per esempio il parla!

Se l'input è una stringa di testo da dire nella stanza, viene chiamata la funzione che scrive su tutti i socket, e qui c'è il secondo foreach per controllare tutti i nick e la loro posizione.

Fino a due livelli il php non produce errori. Ora avevo fatto un'altra implementazione in cui c'è un'altro foreach e qui non riesce a gestire.

Ristrutturare il tutto potrebbe essere una soluzione: per esempio anziché fare un foreach per trovare le persone presenti in stanza, potrei fare un array di quella stanza inserendo lì tutti gli utenti (o le risorse o l'id) di quella stanza, e aggiornare tali array ogni volta che c'è un cambiamento. Ma all'inizio inconsapevole di questo problema, volevo evitarlo perché avrei dovuto gestire simultaneamente due array (col cambio stanza per esempio, devo cambiare due array, mentre cosi' mi basta cambiare un campo), e se una delle due modifiche va storta mi trovo errori difficilmente scovabili. Ma ormai cambiare tutto sarebbe quasi un suicidio.

risposto 10 anni fa
chumkiu
X 0 X
Effettua l'accesso o registrati per rispondere a questa domanda