Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: webhope 10. 12. 2013, 09:59:47

Název: Binární zápis do souboru
Přispěvatel: webhope 10. 12. 2013, 09:59:47
Zdravím,
už delší dobu si neumím poradit s jedním problémem a tak bych potřeboval poradit. Možná dokážete podle symptomů poznat kde by mohl být problém. PHP má lecco společného s C++ tak možná poradíte.

Tady mám třídu, jejiž úkolem je vytvořit binární soubor pomocí metody reset(). Ale všiml jsem si že v módu "x" nebo "xb" se na začátku vytvoří unicode BOM, což nevím jestli je normální a jestli je to v pořádku.
http://paste.ofcode.org/v8M4DwrH7hQzkJMEDjaRpt
Pro samotné spuštění stačí vytvořit instanci třídy. V konstruktoru se spouští metoda addIP, která testuje jestli je možné zapsat IP do souboru, aniž by se změnila jeho délka. Chci zapsat celkem tři čísla které patří jedné IP. Jenže se mi vede zapsat pouze první číslo, viz řádek 412 blok // TEST. Soubor by měl obsahovat samé nuly a po zápisu zapsanou hodnotu. Ale tady se na druhý zápis jakoby zkkrátí soubor, BOM zmizí, velikost souboru zůstává stejná. Nedovedu si vysvětlit toto chování. Mód r+b jsem už předtím zkoušel a fungovalo mi to, do té doby než jsem třídu tu trochu rozšířil. Poradíte?
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 10. 12. 2013, 10:58:40
Zapomněl jsem vložit obrázky

před chybou:
http://oi42.tinypic.com/2ngezbk.jpg

chyba při druhém zápisu
http://oi40.tinypic.com/242id81.jpg
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 10. 12. 2013, 14:09:49
Ještě posílám funkční zjednodušenou a zkrácenou verzi, která nepracuje se třídou.
http://paste.ofcode.org/8ipwC6eWGPxZpHGK5DQFmg (http://paste.ofcode.org/8ipwC6eWGPxZpHGK5DQFmg)

Stačí spustit a vytvoří se soubor test.txt
Zápisy do souboru se povedlo provést (na první a pátý bajt)

Proč to tedy v té třídě nejede, hm?
Název: Re:Binární zápis do souboru
Přispěvatel: petr 10. 12. 2013, 23:14:04
Citace
Ale všiml jsem si že v módu "x" nebo "xb" se na začátku vytvoří unicode BOM, což nevím jestli je normální a jestli je to v pořádku.

Normální ani v pořádku to není. A mám pochybnosti, zda ten BOM tam vkládá PHP, proč proboha? Nemohl ten soubor s tím BOM na začátku být již vytvořen před spuštěním skriptu? Nebo s ním manipuluje něco jiného?

Kód: [Vybrat]
$fh = & $this->FRWBHandler;co to?

Kód: [Vybrat]
$t .= sprintf("%c", 0);Proč ne
Kód: [Vybrat]
$t .= chr(0) ?

Proč v metodě reset() otevíráš soubor v režimu "xb" místo "wb"? Jednak se funkce nechová podle svého názvu, jednak před jejím voláním kontroluješ existenci souboru.

Doporučil bych ti pročistit ten skript a přidat ladící informace, aby jsi mohl sledovat postup od vytvoření souboru až do konce skriptu (zda byl soubor v metodě reset() vytvořen, jaký je jeho obsah v každém okamžiku atd.) A samozřejmě kontroluj návratové hodnoty fwrite, fseek apod. !
Název: Re:Binární zápis do souboru
Přispěvatel: petr 10. 12. 2013, 23:40:59
Zapomněl jsem říct, že mně tvůj kód funguje správně.

Jen mě ještě napadlo, vzhledem k tomu, že se v tom souboru na začátku objeví BOM a potom se ořeže před prvním NUL, že s ním nejspíš manipuluje něco z venku. Třeba nějaký spuštěný editor, co já vím.
Název: Re:Binární zápis do souboru
Přispěvatel: krystal 11. 12. 2013, 00:14:28
notepad ve windows daval nejaky bordel na zacatek souboru, nebo se pletu?
Název: Re:Binární zápis do souboru
Přispěvatel: Ondřej Novák 11. 12. 2013, 01:22:15
BOM je zpravidla před <?php
takže se přepíše do výstupu.

Některé verze PHP dokonce odmítají odeslat hlavičky, když je ten soubor s BOMem. Když se ale výstup bufferuje, tak se chyba hned neprojeví a hlavičky se přesto podaří nastavit navzdory tomu, že na výstup už byl odeslán znak.
Název: Re:Binární zápis do souboru
Přispěvatel: petr 11. 12. 2013, 06:17:43
Citace
BOM je zpravidla před &lt?php takže se přepíše do výstupu.

Do výstupu otevřeného pomocí fopen(), do kterého je následně zapisováno pomocí fwrite() ?
Název: Re:Binární zápis do souboru
Přispěvatel: monitor 11. 12. 2013, 08:06:57
hmm...
skus na zaciatok tej funkcie reset pridat:

var_dump(iconv_get_encoding('all'));

Moja teoria je, ze ak sa ti to sprava tak, ako pises, tak zrejme niektory subor, co pouziva tu triedu, nastavi pre PHP nejake ine kodovanie (nejake uCS-2), a to mozno nejako sposobi, ze sa ten vytvoreny string nejako zapise s BOMom...

inak, ak sa tomu chces vyhnut, a neriesit to, tak
namiesto 307 a 311 (pridavanie sprintf-ovych kuskov do jedneho velkeho stringu, a potom fwrite), nerob si ten velky string,
ale urob ten fopen skor, a potom rovno urob "$this->bufferSize" krat fprintf($fh,"%c", 0)) a to MUSI fungovat...
Název: Re:Binární zápis do souboru
Přispěvatel: petr 11. 12. 2013, 14:29:53
Vy mě vykláte :) Myslel jsem, že pro soubory otevřené v binárním režimu, je fwrite() vždy binary-safe, tj. že zapíše string (interně implementovaný jako pole bajtů) tak jak je, bez jakýchkoliv změn. Především pokud je zadána délka (třetí argument fwrite), což v tom kódu není, ale zase tam jsou samé nuly a nemá se co escapovat. Kde by se tam vzal BOM? To by to PHP bylo naprosto nepoužitelné.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 11. 12. 2013, 14:48:31
Za tu dobu co jsem tu nebyl se to trochu posunulo směrem k úspěchu. Ale na tu první otázku stále nedokážu odpovědět. To bych opravdu rád věděl jak tam ten BOM vzniká, protože nic jiného než fopen($fh, "xb") nepoužívám. Snad jediná možnost, že by to tak zobrazoval editor PSPadu. To se mi ale nezdá.

Na tu chybu proč se mi přepisoval soubor čili proč to smazalo ty nuly jsem nepřišel, čím to bylo, ale dělal jsem to znova z obyčejné funkce až po třídu a teď to jede. Nicméně, BOM tam stále je a chová se to tak, že když zapisuji např. dvě čísla v hex je to  78 a 64 tak hex editor to zobrazuje jako 7800 6400 ... jenže já kontroloval velikost toho stringu který vytváří ten soubor a jeho velikost přesně odpovídá správné velikosti. Použil jsem strlen($t) na zjištění velikosti proměnné pro vstup. Taktéž velikost souboru přesně odpovídá. Takže je to záhada, ale vlastně nevím jestli tam někde chyba je nebo to blbě zobrazuje PSPAD.

Petr:
člen FRWBHandler je File handler pro r+b zápis. Dal jsem to tam pro účel hromadného zápisu, tak aby se ten soubor pořád neotvíral a nezavíral. Prostě se to jednou otevře a hotovo. Metody addIP a checkIP používají r+b mód, i když checkIP by stačil rb, ale kvůli tomu zbytečnému otevírání pořád dookola jsem zvolil společný handler. $t .= chr(0) to nevím kde bylo, ale mělo to přidávat nuly, asi součást starého kódu, dnes nepotřebné. Na nové verzi ještě pracuji. wb by bylo pro zápis, kdežto já potřebuji číst i psát. čísl pro kontrolu jestli je daný bajt nula. Není tu snad nic co by mohlo zvenku ovlivnit soubor. Je to teď jen jeden skript (instanci vytvářím hned za třídou čímž spouštím konstruktor.

krystal:
notepad je nepoužitelný, lepší je ten PSPad

Ondřej Novák:
tag php s tím nemá co dělat, tenhle proces probíhá nezávisle. Nebo jsem vás nepochopil. nejde o php soubor, ale o binární soubor. Nemluvím o výstupu html ale o výstupu do souboru.

monitor:
to samé co Ondřeji Novákovi. Nejde o zpracování html, tohle funguje nezávisle.
Název: Re:Binární zápis do souboru
Přispěvatel: petr 11. 12. 2013, 15:27:59
V tom PSPadu zobrazuješ ten výstupní soubor? Tak to bych ho přece jen zkusil zavřít.

Citace
... chová se to tak, že když zapisuji např. dvě čísla v hex je to  78 a 64 tak hex editor to zobrazuje jako 7800 6400 .
Hex editor a ne viewer? Nemůže do toho zapisovat třeba ten? Jinak 7800 6400 je v pořádku. Seekuješ o bajt při zápisu dalšího čísla:
Kód: [Vybrat]
                fseek($fh, $i);
                fwrite($fh, chr($arr[1]), 1);
                $result = fseek($fh, $i + 2);
Jen takové hodnoty asi mohou skutečně zmást nějaký editor, který reaguje na změny v souboru. Už jen proto, že v metodě reset() ten deskriptor po vyplnění souboru nulama explicitně zavíráš. Jestli např. nějaký editor (pspad, hex editor) se nechává notifkovat o změnách souboru, který má otevřen, tak to může v ten okamžik přepsat, protože to na první pohled připomíná little endian UCS-2 nebo UTF16 a ten BOM by tomu odpovídal. Taky to samozřejmě mohou být regulární hodnoty z toho skriptu, ale ten příklad co jsi uváděl tomu neodpovídá. Navíc by to nevysvětlovalo to ořezání co jsi pozoroval.

Já to vidím takhle:

Mám prázdný řetězec, do kterého přidávám \x00, potom ho uložím pomocí fwrite do prázdného souboru vytvořeného pomocí fopen v binárním režimu. Jak by se tam mohl dostat BOM? Do toho řetězce? Nebo na začátek souboru při vytváření pomocí fopen(..., 'xb')? I kdyby měl ten skript na začátku BOM nebo byl v jakémkoliv kódování?

Já tam vidím čtyři možnosti: Buď ten soubor již existoval s BOM na začátku před voláním skriptu nebo ho něco externího upravilo mimo skript - třeba ten zmiňovaný PSPad nebo použitý hex editor (neznám ani jeden) nebo něco jiného. Nebo je něco shnilého v implementaci php pro windows (to jsi mohl zmínit v prvním postu :) nebo nevím jak funguje zápis binárních souborů na windows - to skutečně nevím, nedivil bych se ničemu. A nebo jsem v tom kódu přehlédl řádek:
Kód: [Vybrat]
if (!rand()) fwrite($fh, \xEF\xBB);
Mě spíš překvapuje že tam netestuješ úspěšnost fwrite, fseek, fclose.... pokud se ten soubor ořezal před prvním \x00 za BOM tak přece selhal následný fseek.

Pokud ten problém souvisí s PHP na windows, tak k tomu poradit neumím.

Citace
wb by bylo pro zápis, kdežto já potřebuji číst i psát.
Myslel jsem použít mód "wb" v metode reset() místo módu "xb", kde ten soubor stejně jen vyplníš \x00 a zavřeš. To "xb" předělává metodu reset, takže dělá něco jiného než je její název - existenci souboru kontroluješ před jejím voláním, alespoň v aktivní části toho kódu co jsi poslal. Ale to je jen nepodstatný detail.

Citace
$t .= chr(0)
To jsem napsal já, myslel jsem to jako náhradu za ten sprintf(), který bude možná zbytečně zdržovat. Opět jen bezvýznamný detail. Optimální by bylo
Kód: [Vybrat]
str_repeat(chr(0), $this->bufferSize), ale to prý nefunguje, pokud je string \x00, tak jsem to nepsal.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 11. 12. 2013, 15:58:52
teoreticky bych tam wb použít mohl pro reset.

Já měl za to že jeden byte je hex hodnota 00 až FF, 8bitů. Tudíž když posouvám fseek po jednom a pak zapisuji výsledek by měl být dva bajty např "FFFF" a ne "FF00 FF00". Jestli to nechápu tak to jsem už asi z toho jelen. Navíc je tu další věc, která se mi opět vynořila a přispívá k mé zmatenosti. Snažím se na pozici 500tého bajtu zapsat slovo "localhost;" (název domény). Prohlížím si kam jsem to zapsal v tom hexeditoru (v PSPadu). Pozice je 3E8 což je 1000 byte. Tak tohle už musí být hmatatelný důkaz že to funguje špatně.

Tohle je jeden příkaz který je součástí nové verze reset():
Kód: [Vybrat]
$t=substr_replace($t,$s,$this->h1+$this->h2-1,strlen($s));Zaměňuje v tom vygenerovaném stringu string $t za $s="localhost;" od pozice 100+400-1 = 499; což když vezmeme v úvahu pozici v bytech je pozice bajtu 500. No a v hexaeditoru to čtu že je to na pozici 1000 což znamená, že tam opravdu je jeden byte navíc. Jdu zkusit ten wb mód.. opět pozice 3E8, takže to nemá vliv.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 11. 12. 2013, 16:09:58
Taky by mě zajímalo jak to bylo v té první verzi (to je ta co si zkoušel) protože mám dojem že tam to ten bajt navíc nepsalo.
Název: Re:Binární zápis do souboru
Přispěvatel: jholas 11. 12. 2013, 16:59:55
možná by nebylo od věci napsat verzi PHP. Já zkusil toto sestavení pro windows php-5.3.27-nts-Win32-VC9-x86 a reset dal jeden zbytečně velkej soubor plnej nul a nejen PSPad to tak i zobrazil. Takže žádný BOM. (pouštěno z commadline: php.exe script.php což by nemělo mít na nic vliv)
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 11. 12. 2013, 17:05:55
Dík za pomoc. Už jsem to otevřel v Notepad++ (je třeba pluxin HEX EDITOR). a zjistil jsem, že tam opravdu žádný BOM není ani ty nuly tam nejsou (vedle těch zapsaných bajtů). Takže opravdu je to chyba zobrazení PSPadu, který asi předpokládá nějaké formátování nebo co. Jinak jestli tu někdo používáte NOtepad++ tak se chci zeptat jestli nevíte jak nastavit pozadí aby bylo černé a jak zjistit pozici aktuálního bajtu.  To nejde jinak než vypočítat? Mám tam 1f0 + 3 byty se rovná 499 bajtů takže mi to sedí.
Název: Re:Binární zápis do souboru
Přispěvatel: petr 11. 12. 2013, 22:29:53
Doufám tedy že ti to dělá, co chceš, protože

Citace
... chová se to tak, že když zapisuji např. dvě čísla v hex je to  78 a 64 tak hex editor to zobrazuje jako 7800 6400 .
Citace
... Tudíž když posouvám fseek po jednom a pak zapisuji výsledek by měl být dva bajty např "FFFF" a ne "FF00 FF00".

Jenže v tom kódu seekuješ o 2 bajty, ne o jeden...

Když např. přidáš 1.120.100.60 do prázdného souboru, prvních 8 bajtů bude
78 00 64 00 00 00 00 00
seekuješ to o 2 bajty, viz úryvek tvého kódu, co jsem vložil do minulé odpovědi.
Když potom přidáš třeba 1.14.205.60, tak to bude
78 0E 64 CD 00 00 00 00
protože první bajt testuješ na \x00 a přesuneš se v cyklu o bajt dál.
Když dál přidáš třeba opět 1.120.100.60, tak vyjde
78 0E 64 CD 78 00 64 00

Další věc je, že to takhle hledá konec dat po 1 bajtů (vždy fseek a fread), což asi moc efektivní nebude a další věc že se ti při různých příležitostech budou přepisovat data:
Nejvýznamnější bajt v IP ti určuje offset * 250. Ale do těch 250 bajtů zapisuješ druhý i třetí bajt IP adresy. Takže rozsah adres 2.x.x.x ti bude přepisovat druhou polovinu záznamů pro rozsah 1.x.x.x. Navíc ve smyčce hledáš konec data testováním na hodnotu \x00; co když bude IP adresa x.0.x.x nebo x.x.0.x ? Přijdeš o její záznam.

Já když jsem to viděl, tak jsem ihned vzdal snahu porozumět tomu, v sítích se nijak nevyznám, myslel jsem si kdovíjakou magii neprovádíš a že to je nad moje schopnosti / čas. Teď si nejsem jist, když tě mate to posunování o bajt.

Možná jsi to už úplně přepracoval.... tak snad ti to funguje jak očekáváš.

K té metodě reset() jsem chtěl celou dobu říct jen to, že je buď chybně pojmenovaná nebo nedělá co má. Na funkčnost nemá vliv, ale až zapomeneš na implementační detaily, můžeš nasekat chyby tím, že budeš předpokládat že metoda reset() "resetuje" výstupní soubor a ona přitom zatím jen vytvoří nový soubor, pokud dosud neexistuje. Pokud existuje, nijak se ho nedotkne, takže jakýpak reset? Buď bych ji přejmenoval třeba na init() nebo v ní vytvářej soubor vždy (mód "wb") - existenci souboru stejně kontroluješ mimo ní a voláš ji jen když neexistuje.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 11:35:32
Petře, co se týče toho fseek o dva bajty, tak to bylo myslím jen v té testovací části. Tu když odstraníš, tak pak se to dělá jinak.

No to je právě na zváženou. Mohl bych načíst celý řetězec po 750 bytech, respektive tři řetězce po 250 bytech (tohle ještě musím upravit, není to číslo dělitelné třema). A v tom pomocí smyčky hledat v RAM ce. Nebo hledat na disku po jednotlivých bajtech, což asi máš pravdu by bylo pomalejší, ale zase se nemusí načítat 750 bajtů, protože on s největší pravděpodobností načte pouze několik bajtů. Není to tak že by poslední tři čísla IP byly za sebou uložené. Ony jsou uložené tak, že všechny druhé čísla (n2x) z ip=n1x.n2x.n3x.n4x jsou uložené za sebou. Tzn n21,n22,n23 atd. tedy pokud v souboru jsou na stejném "řádku" zapsány tři IP:
124.5.100.1
124.8.10.14
124.9.18.254
tak fakticky načte pouze 5 bajtů. Ale pokaždé se při tom použije fseek. Jestli myslíš, že je to pomalé tak to budu načítat po celém řádku o velikosti 750 bajtů.

Data se přepisovat nemohou protože pokud je tam bajt který není \x00 tak se tam nic nepíše, jde se dál a zapíše se až narazí na \x00.
Proto považuji zbytečné odpovídat na zbytek tvých otázek, protože z logiky věci vyplývá, že k žádnému přepisu nemůže dojít.

Nakonec pokud by došlo místo tak se to celé vyresetuje a pojede se od nuly (nový soubor). Reset zde znamená smazání souboru a vygenerování nového.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 11:37:29
Čili ten testovací kód byl tento:
Kód: [Vybrat]
// TEST
// die(); test first
$result = fseek($fh, $i+2);
fwrite($fh, chr($arr[2]), 1);
die($arr[1]." ".$arr[2]." ".$arr[3]. " result $result");
// END TEST

To celé příjde vymazat. Mám pocit, že tě to zmátlo.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 12:14:07
Citace
Nejvýznamnější bajt v IP ti určuje offset * 250. Ale do těch 250 bajtů zapisuješ druhý i třetí bajt IP adresy.
Ne není to tak jak píšeš.
První číslo v IP určuje pozici, tedy "číslo řádku".
Kód: [Vybrat]
$arr[0]="120";
$this->segmentLength = 250; // toto nutno opravit na 249
$this->rowLength = 3*$this->segmentLength; // máme 3*250;
$segmentBegin = ($arr[0]-1)*$this->rowLength;
$segmentBegin = (120-1)*750;
$segmentBegin = 119*750;
Zápis tedy bude probíhat do řádku číslo 120. Tak ve starém kódu. V novém skriptu jsem ale vytvořil hlavičku o délce 750 bajtů, takže tam to nebude posunuté.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 13:25:07
Ještě si dovolím požádat o radu. V nové verzi, jak jsem již zmínil přibyla hlavička. Ve třetí části je uložený název serveru, který chci blokovat. pro začátek jsem tam uložil slovo localhost;

Nyní kód, který by měl sestavit slovo $word obsahující "localhost".

Kód: [Vybrat]
for($n=$this->h1+$this->h2; $n<$this->header_size; $n++):
$word=$byte="";
fseek( $fh  , $n );echo "n is $n;";
                         // && $byte!=";"
while ( $byte=fread($fh,1) ) //  && ord($byte)!=0
     {
echo $byte;
$word.=$byte;
echo ":$word==".$_SERVER["HTTP_HOST"]."<br>".ord($byte);
if ( $word==$_SERVER["HTTP_HOST"] )
 return false; // this domain is blocked
endfor;

Tohle není dodělané. Vypíše to slovo localhost; a následující znaky které jsou nula. Dalo by se říct, že toto funguje, ale chci vypsat jen slovo localhost. Tak na konec podmínky while přidám:
 && $byte!=";"

a výsledek je ten, že to vypíše pouze písmeno "l", tedy jeden znak, protože cyklus je hned přerušen.
Zkoušel jsem tedy i  && $byte!=59
a && bindec($byte)!=59
nic z toho ale nefunguje :-( vždycky se cyklus hned přeruší  :o
Název: Re:Binární zápis do souboru
Přispěvatel: and 12. 12. 2013, 15:14:31
Proc to vlastne davas do binaniho souboru? Kdyby to byl text ci csv, uz bys to mel hotovy a asi by to bylo i rychlejsi na zpracovani...
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 15:53:54
and: to si nemyslím, že by to bylo rychlejší. Pokud vezmeš v úvahu, že v souboru je zaznamenáno min. 120 IP, ale prakticky tak 200-500 IP (postupně jak se plní kontejner a podíváš se kolik kroků potřebuješ na zjištění jestli tam ta IP je nebo není, tak já myslím, že toto je rychlejší. Jinak totiž porovnání každé IP představuje porovnat tak kolem 14-15 znaků. Tady se ti za tu dobu porovnají min. 3 IP, ale je to samozřejmě různé, bude to záviset od toho jaká IP tam v tom kontejneru bude. Uvedu příklad. Když tam budu ukládat čínské proxy servery, tak ty mohou existovat pod např. 182.165.14 bude třeba 20 IP. Ale to nevadí. Protože uživatel co má IP 124. na tuto adresu vlastně vůbec neskočí. Ten skočí na řádek 124, kde zjistí, že žádná IP není a tudíž ho není třeba blokovat. Kdežto pokud to bude někdo s adresou 182.165.14 tak je blokovaný v nové verzi hned (protože 182.165.14 by se uložilo do hlavičky takže to není třeba ani zapisovat do řádku, když se to opakuje). Ale kdyby tam ta hlavička nebyla tak jako podle staré verze, tak přečte dejme tomu 60 bajtů a ví že je blokovaný (proxy je blokované).

Dal by se stejný proncip aplikovat i na seznam IP blokovaných uživatelů (ne proxy), ale jelikož tam by to bylo neomezené, musel by ten řádek mít dynamickou délku (to už mám taky vymyšlené takže asi to zavedu). Zkrátka mě to baví to vymýšlet. Užitečnost by byla samozřejmě až od většího počtu adres, jinak rychlost při krátkém seznamu zanedbatelná. To s tou dynamickou délkou bych řešil pomocí hlavičky. Do hlavičky se dá zapsat, kolikátý řádek má jakou délku. Délka by se dala zvětšit podle potřeby. Je fakt, je to zase o něco složitější, ale jen trošičku. Zase platí, čím více adres tam je (a pokud mu dojde vymezené místo) tím více musí přečíst bajtů z hlavičky, aby mohl rozšířit daný řádek. Zní to složitě? Vlastně je to jen o tom načíst správný počet bajtů před aktuálním řádkem a od následujícího řádku až do konce souboru, vygenerovat správnou délku aktuálního řádku, zaměnit řetězec (dosadit staré data), a nakonec poslučovat tyto tři části a zapsat to do nového souboru (nebo je zapsat do nového souboru v módu append, to by asi bylo lepší).
Název: Re:Binární zápis do souboru
Přispěvatel: and 12. 12. 2013, 16:27:16
Nez PHP nacte, vyhodnoti, preskoci atd. radky v binarnim souboru, tak o trva.
Nacteni cele struktury do pameti a dotazani se bude radove rychlejsi diky tomu, ze operace se budou dit na urovni C/C++ kodu a ne na urovni PHP.
Nedavno jsem udelal zkusenost, ze pouziti sqlite3 jako storage je rychlejsi, nez uchovavat pointery do souboru na nacitani z nich a jejich cteni. Rozdil zhruba 2x.

Dvakrat mer a jednou rez - vyzkousej oba pristupy a posli vysledky, co je rychlejsi!

Nicmene na hrani je to urcite zajimavy material  ;)
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 17:07:55
and:
Jistě že to vyhodnocení kódu trvá. Vem si, že dnešní webové stránky většinou využívají třídy hlavně RS apod. na to, abys tam mohl mít přihlášené uživatele nebo vkládat články, dávat komentáře, atd. Kolik tříd se musí načíst a o jak složité objekty se jedná? V porovnání s nimi je moje třída opravdu banální, že je zbytečné to měřit. Ale taky si uvědom, že když použiješ například funkci file() nebo explode() tak tyto funkce fakticky parsují soubor a vytvářejí objekt (i když v php se tomu říká pole), což je podle mého názoru podobné (ne-li stejné?) jako vyhodnocení vlastní třídy a sestavení objektu (instance). Na sestavení vlastní třídy mě příjde lákavé to že si můžeš vytvořit vlastní metody, které si pojmenuješ přesně podle toho co potřebuješ. Tedy kdyby chtěl někdo například ukládat nějaké záznamy do mysql tabulky, ale ty by ses to rozhodl udělat binárně takže si ušetříš čas s připojováním k mysql a komplikace typu (nevím proč mi to hází mysql chybu, nevím proč se mi nezobrazují správně znaky, přišel jsem o data kvůli převodu z jedné tabulky do druhé, atd.) tak by to svůj smysl mohlo mít, pokud nemáš dva miliony uživatelů. Přístup máš zajištěný jedinečný, protože nikdo jiný než daný uživatel dané místo v souboru přepisovat nebude, zálohování velice snadno zkopíruješ soubor s tabulkou přes ftp manažer. To by mělo své výhody.
Název: Re:Binární zápis do souboru
Přispěvatel: and 12. 12. 2013, 21:40:39
and:
Jistě že to vyhodnocení kódu trvá. Vem si, že dnešní webové stránky většinou využívají třídy hlavně RS apod. na to, abys tam mohl mít přihlášené uživatele nebo vkládat články, dávat komentáře, atd. Kolik tříd se musí načíst a o jak složité objekty se jedná? V porovnání s nimi je moje třída opravdu banální, že je zbytečné to měřit. Ale taky si uvědom, že když použiješ například funkci file() nebo explode() tak tyto funkce fakticky parsují soubor a vytvářejí objekt (i když v php se tomu říká pole), což je podle mého názoru podobné (ne-li stejné?) jako vyhodnocení vlastní třídy a sestavení objektu (instance). Na sestavení vlastní třídy mě příjde lákavé to že si můžeš vytvořit vlastní metody, které si pojmenuješ přesně podle toho co potřebuješ. Tedy kdyby chtěl někdo například ukládat nějaké záznamy do mysql tabulky, ale ty by ses to rozhodl udělat binárně takže si ušetříš čas s připojováním k mysql a komplikace typu (nevím proč mi to hází mysql chybu, nevím proč se mi nezobrazují správně znaky, přišel jsem o data kvůli převodu z jedné tabulky do druhé, atd.) tak by to svůj smysl mohlo mít, pokud nemáš dva miliony uživatelů. Přístup máš zajištěný jedinečný, protože nikdo jiný než daný uživatel dané místo v souboru přepisovat nebude, zálohování velice snadno zkopíruješ soubor s tabulkou přes ftp manažer. To by mělo své výhody.

Nikde v tvem textu nevidim to rychlostni srovnani, jen kupu dohadu...
Neni to tim, ze si fakt jen hrajes a opravdu efektivni reseni nehledas?
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 22:20:33
and

Citace
Nacteni cele struktury do pameti a dotazani se bude radove rychlejsi diky tomu, ze operace se budou dit na urovni C/C++ kodu a ne na urovni PHP.
Nedavno jsem udelal zkusenost, ze pouziti sqlite3 jako storage je rychlejsi, nez uchovavat pointery do souboru na nacitani z nich a jejich cteni. Rozdil zhruba 2x.

Ok, tedy nebudu to číst po jednom bajtu, ale po celém řádku/segmentu. Tím pádem se ten rozdíl smaže. Během toho co jsem vypisoval smyčku hlavičky o délce 449 bajtů to jelo děsně pomalu, ale bylo to nejspíš tím, že tam bylo echo $bajt (echovat jednotlivé stringy v cyklu vůbec není efektivní). Já to srovnání zatím nemám, ale mohu si to udělat později až dodělám třídu.

Když máš zkoušenost sqlite, zkoušel si jak velký je rozdíl v porovnání s mysql při provozování stránek? A je těžké předělat RS dělaný na mysql do sqlite? Nebo je to skoro stejné? Těma pointerama si myslel jako že uchováváš id jednotlivých záznamů? To já id právě neukládám, ale vypočítávám pozici což by mělo být rychlejší. Ale těžko říct jak by celá stránka reagovala, to bych musel celé předělat z RS pro mysql na RS pro sqlite.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 23:01:23
Hele tak dělal jsem test, jo, a teda nevím jestli to měřím špatně nebo co, ale mě to vrací čas nula. Pokud to chceš změřit, tak první bajt nastavuju $i=449; poslední bajt $i=749;

Kód: [Vybrat]
public function checkDomain()
{
if ( !$this->FRWBHandler )
    $this->FRWBHandler = @fopen($this->file,"r+b");
    $fh =& $this->FRWBHandler;

    // read header block 3 - domains;
$t2=time();
$sentence="";
for($n=$this->h1+$this->h2; $n<$this->header_size; $n++):
$word=$byte="";
fseek( $fh  , $n );echo "n is $n;";
while($byte=fread($fh,1) && $byte<>";" ):
$word.=$byte;
if ($word=="localhost")
{
       die();
}
endwhile;
$sentence.=$word;
$word="";
    endfor;
    $t1=time();
    echo "sentence:".strlen($sentence).";";
    die("time:".($t1-$t2));
}

Je sice pravda, že v té hlavičce není nic jiného než nuly, ale podle mě to nemá žádný vliv na rychlost.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 12. 12. 2013, 23:16:12
Mám tam chybu, má tam být
Kód: [Vybrat]
while( ($byte=fread($fh,1)) && ... podmínky )ale ono to stejně nejde otestovat, protože se to zacykluje. Nejlepší je na test cyklus while vypustit a dostaneš rozdíl v čase nula milisekund.
Název: Re:Binární zápis do souboru
Přispěvatel: petr 13. 12. 2013, 04:56:33
Petře, co se týče toho fseek o dva bajty, tak to bylo myslím jen v té testovací části. Tu když odstraníš, tak pak se to dělá jinak.
Moje chyba, přehlédl jsem ten testovací kód, který tam nepatří. Já komentáře smazal, když jsem reformátoval kód :) Ale problém s hledáním konce dat testováním na hodnotu \0 tam pořád vidím. Adresy a.0.x.x, pro které je nejvyšší bajt "a" stejný, se ti budou přepisovat, ne?

Problém s rychlostí snad ani není ve čtení ze souboru a seekování, stejně bude v paměti, ale ve smyčkách. Pomocí smyčky hledáš konec dat (navíc chybně testováním na hodnotu \0, která může být součástí záznamu) a smyčkou také testuješ zda je IP blokována. Data nemáš na úrovni stejně významných bajtů IP adresy nijak tříděna. To přece mohou být stovky průchodů, když bude tabulka více zaplněna.

Přelétl jsem další komentáře a prošel podrobněji ten (původní) kód a teprve teď mi došlo, k čemu ten kód má vlastně sloužit: k jednoduchém ukládání a vyhledávání JEDNOTLIVÝCH IP adres, tedy žádná magie, žádné intervaly, žádné podsítě. Proč tedy ty IP adresy jednoduše neukládáš do databáze třeba jako 32bit unsigned integer do jednoho indexovaného sloupce? Neřešilo by to vše, co potřebuješ?
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 09:50:09
Není to možný aby se přepisovali. Zapisuji tři bajty, každý má své místo. Pro zjednodušení si to představ jako kontejner, který má kapacitu jednoho záznamu a jedné konkrétní IP, začínající na \1:
\2\3\4
kde číslo bajtu současně znamená číslo v pořadí v IP. A teď si to představ, že tam bude záznamů více
V tom případě by si měl jen jeden řádek, který má tři bajty. V případě, že chceš vytvořit kontejner pro dvě IP (začínající na 1 a na 2) zapsal bych tam:
\2\3\4\2\3\4
Když chceš mít kontejner pro 254 záznamů, ale každý začíná na jiné číslo, bude to 254xsekvence \2\3\4 . Když bys tam chtěl mít max. dvě stejné IP adresy tak budeš 254x opakovat sekvenci \2\3\4\2\3\4 ... z toho vyplývá, že každá IP a každé číslo má svoje místo a nemůže se přepsat.

Citace
Ale problém s hledáním konce dat testováním na hodnotu \0 tam pořád vidím.
Ve skutečnosti nehledám konec dat, ale začátek dat kam mohu psát a pak vracím true;
Kód: [Vybrat]
if ( $byte === "\x00" ){
...
return true;
break;
}
Při pokusu vložit IP, když najde nulu, zapíše 3 bajty a ukončí vkládání. Dále nepokračuje.

Hodnota \0 nemůže být součástí záznamu, protože ascii tabulka začíná od jedničky :-)
Citace
a smyčkou také testuješ zda je IP blokována.
Zkontroloval jsem. Je tam return false; ale nedal jsem tam break; takže to opravuju. I když teoreticky by se program měl ukončit hned při return, php není Javascript, nemohou jet dva procesy současně! Asi to zas smažu ! :-)
Tabulka má kapacitu 249 IP (nyní) ale stejně podle mého názoru je to zbytečně moc. Stačilo by 200, s tím, že se to každou hodinu aktualizuje (soubor se znova založí).
Kód: [Vybrat]
Data nemáš na úrovni stejně významných bajtů IP adresy nijak tříděna. To přece mohou být stovky průchodů, když bude tabulka více zaplněna.Jestli myslíš ten počátek, tak tam není třídění třeba. V jednom řádku jsou všechny IP začínající např. na 124. Máš, pravdu, že bych to mohl setřídit čísla druhé v pořadí, jestli myslíš toto, ale to prakticky nejhraje roli, protože to ničemu nepomůže.

Citace
Přelétl jsem další komentáře a prošel podrobněji ten (původní) kód a teprve teď mi došlo, k čemu ten kód má vlastně sloužit: k jednoduchém ukládání a vyhledávání JEDNOTLIVÝCH IP adres, tedy žádná magie, žádné intervaly, žádné podsítě. Proč tedy ty IP adresy jednoduše neukládáš do databáze třeba jako 32bit unsigned integer do jednoho indexovaného sloupce? Neřešilo by to vše, co potřebuješ?
Jistě, možností existuje více. Jak už tu padlo, mohl bych použít sqllite a bylo by to taky efektivní. Tohle je v podstatě jen na trénning. Myslím, ale že to splní svůj účel stejně jako by to splnilo načítání ze souboru. Obojí je rychlé. Ale tímhle jsem se něco naučil, hlavně co se týče té binární logiky. Chci zkusit ještě udělat ten dynamický kontejner pro blockované IP, kde bude neomezený počet IP, ale rychlost vyhledávání prakticky taky neomezená (vyhledávací čas v ms očekávám nula - podle výsledků včerejšího měření ... jestli jsem to dělal dobře).
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 11:39:28
Jak jsem psal, že by se tento binární princip dal použít i na sestrojení vlastní databáze, tak mě napadl jeden dobrý důvod proč ano. Představte si, že si odněkud z netu stáhnete webové stránky používající mysql, případně sqlite, zdrojový kód moc nezkoumáte, ale rozhodnete se to nakonfigurovat a spustit. Časem se může stát, že na databází bude proveden útok pomocí některých metod jako
javascript injection, sql injection nebo SQL Injection Host (http://shiflett.org/blog/2006/mar/server-name-versus-http-host (http://shiflett.org/blog/2006/mar/server-name-versus-http-host)) (když útočník změní hlavičku odesílaného požadavku tak, aby obsahoval útočný skript a vy tu hlavičku ukládáte třeba jen do session tabulky, nebo do tabulky se statistikama, tak se ukládaný kód změní ve smrtelnou past). Tohle by se ale nikdy nemohlo stát v případě vlastnoručně napsané třídy, kde místo queries voláte přímo metody k jednotlivým třídám (příklad: users.insert("pepa@seznam.cz","mojeheslovmd5");. Takový přístup by byl nejen jednodušší, ale i bezpečnější.
Název: Re:Binární zápis do souboru
Přispěvatel: none_ 13. 12. 2013, 13:53:53
Relační databáze mají dobrý důvod, proč existují a jsou používány. Prostě určitý typ úloh řeší dobře.

Jinak to, co popisuješ je základ každého ORM. Jenom se tam vetsinou nedela users.insert(jmeno, prijmeni), ale users.insert(user).

Každopádně takovýmto zaobalením bezpečnost nezvýšíš. Pouze můžeš odstranit rizika spojená s SQL databázemi. Ať už tak, že ta tvoje třída bude důsledně ošetřovat všechny vstupy, nebo tak, že na pozadí budeš používat něco jiného než SQL databázi. Ale třeba ten javascript injection neodstraníš. Když ti někdo do DB uloží do políčka jméno uživatele javascript a ty ho potom někde zobrazíš v neescapované podobě, tak máš aplikaci stejně v háji, až používáš relační databázi, objektovou nebo key-value...
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 14:05:09
none_: to je fakt, je javascriptový útok na to nemá vliv.

petr: ještě mě napadlo jestli si nemyslel to, že když překročím oněch 250 IP ček, tak že se to bude zapisovat do dalšího segmentu. To jsem sice původně plánoval očetřit, ale vlastně jsem se k tomu ještě nedostal, takže to udělám dnes. Jde jen o to si hlídat počet načtených bajtů.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 14:07:02
vlastně zase kecám. vždyť je tam ta smyčka for která končí po 250 bajtech... takže se to přepsat nemůže
Název: Re:Binární zápis do souboru
Přispěvatel: none_ 13. 12. 2013, 14:10:18
SQL Injection Host (http://shiflett.org/blog/2006/mar/server-name-versus-http-host (http://shiflett.org/blog/2006/mar/server-name-versus-http-host))

Btw dík za zajímavý článek...
Název: Re:Binární zápis do souboru
Přispěvatel: petr 13. 12. 2013, 18:46:03
Neztrácej čas vracením se k tomu, že jsem v předminulém (asi) komentáři psal, že se ti bajty IP adres ukládají objednu pozici vedle sebe a dojde k přepisování. To mě zmátl ten testovací kód, který jsem bral jako součást funkce a považoval jsem za testovací naopak ten die(); smazal jsem si z toho komentáře. Myslel jsem, že je to jasné z mého posledním komentáře. Takže se omlouvám za čeření vody.

Ale v tom posledním komentáři jsem psal už o jiných věcech a po přečtení reakce mi nezbývá než tě na něj znovu odkázat. Zkusím to napsat znovu trochu jinak a podrobněji:

Mluvil jsem o přepisování záznamů IP adres, které mají jako druhý nejvýznamnější bajt nulu. Takovou IP adresu (s druhým bajtem 0) při ukládání další IP adresy, která má stejný první bajt, přepíšeš, protože v souboru testuješ na \0 jako konec dat a pokud tam je, zapíšeš novou IP adresu a tedy přepíšeš předchozí (která měla druhý bajt 0). ASCII s tím přece nijak nesouvisí, chr(0) == \0 == \x00

O smyčkách jsem mluvil kvůli tomu, že tu datovou strukturu právě moc efektivní nemáš. Při zápisu hledáš smyčkou konec dat (\0) při čtení zase musíš prohledávat smyčkou záznamy dokud nenajdeš shodu nebo konec (\0).  Píšu musíš, protože metoda checkIP v tom kódu, je nefunkční - smyčka se přeruší při prvním bajtu, který nedopovídá druhému bajtu IP adresy, a tedy téměř vždy skončí bez vrácení hodnoty.

Pokud to opravíš, tak abys při plné tabulce zjistil, že IP adresa NENÍ v blacklistu, což bude asi naprostá většina, projdeš smyčkou segmentLength krát, vždy jeden seek a jeden fread,  tj. 250x (dle původního kódu),  v nejhorším případě ale provedeš seek a read 3x za průchod, což je 750x. Možná je to zanedbatelné, ale jeden SQL dotaz do indexovaného pole nebo projití binárního stromu (což může být to stejné) bude
IMHO mnohem rychlejší. Ten nejhorší případ je pro tabulku zaplněnou ip adresama které se liší jen v posledním bajtu.

Co se týče dynamického kontaineru (teď to máš omezené na 250 IP adres), vždyť to řeší databáze. SQL injection přece vůbec nemusíš řešit, vždyť potřebuješ uložit/vyhledat jedno číslo, které bude vždy číslem. Pokud navíc předpokládáš podvržené IP adresy na vstupu nebo nějaký balast tak je celá tvoje aplikace k ničemu.

Pokud se jen učíš, dobře, praktický význam to ale podle mě nemá - řešení pomocí databáze (nepotřebuješ relační) je triviální, efektivnější, neomezené počtem ip adres a rozšiřitelné. Pokud například použiješ B-Tree index, což bude stejně nejefektivnější, tak je triviální třeba kontroloval i rozsahy IP adres (jediným dotazem), tj. zda třeba daná IP není součásti blokované sítě. Pokud chceš udělat něco co bude efektivnější než databáze, tak ty adresy budeš muset stejně ukládat do binárního stromu.

Musím se přiznat, že mnohdy nevím, na co reaguješ, např:
Citace
Zkontroloval jsem. Je tam return false; ale nedal jsem tam break; takže to opravuju. I když teoreticky by se program měl ukončit hned při return, php není Javascript, nemohou jet dva procesy současně! Asi to zas smažu ! :-)

Javascript není Javascript, nemohou jet dva procesy současně! :) ?

I když nevím jak to souvisí s tím, co jsem ti psal.

Specifikace ECMAScriptu (javascriptu) nedefinuje žádné mechanismy pro multithreading. Nicméně pro Javascript se dají použít web workers (součást specifikace HTML5), pro PHP zase pthreads.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 20:30:58
Tak zase z mého úhlu pohledu. IP adresa sice může obsahovat nuly, ale mám pocit, že toto platí jen v místní síti. Na veřejné síti se nuly neobjevují. Takže máš pravdu, že adresa vnitřní sítě např. 127.0.0.1 má jako první bajt 127, ale posuzuju podle druhého bajtu, který je 0 a tudíž by opravdu došlo k chybě. Nicméně, počítám s tím že tyto adresy tam prostě nebudou. Počítám, že zapisuji proxy adresy, ale připomínka je to dobrá. Tzn. co bych tam měl zapsat místo nuly, 255 do celého souboru?

Pokud jde o konec metody checkIP tak tam je to takto:
Kód: [Vybrat]
if(...){...}
else
{ return true; // no more ips to check
}
endfor;
echo  " end of check method  ";
return true;
}

Tobě jde o to, že fread($hf,1) volám ze smyčky místo fread($hf,250) bez smyčky? ale fread dělá v podstatě to samé protože data uvnitř fread se čtou pomocí smyčky, ne? Já každopádně žádné zpoždění nenaměřil, možná to neumím měřit, ale prostě to nějak vidím jako zanedbatelné. SQL dotaz by sice nevyžadoval smyčku, ale máš zdržení způsobené databází, které je větší než když to čteš ze souboru který máš na disku kde jsou webovky. Přece mysql taky používá nějaké vyhledávací mechanismy, které i kdyby byly seberychlejší, musí řešit spoustu věcí okolo vrácení těch správných dat. To jsou operace, které nevidíš, ale dějí se na straně mysql serveru, takže nějaké zpoždění tam bude + doba přenosu + čas na to aby php vyhodnotilo vrácenou odpověď ....

Podvržené IP adresy? To myslíš co a jak by to útočník udělal?

Jak říkám, řešit tenhle proxy kontainer přes db (mysql) je podle mě totální mrhání prostředky. Vždyť se to bude neustále měnit a přepisovat, nepotřebuju zdržení kvůli připojení a odeslání a vybavení mysql požadavku. Takže omezení adres mi vůbec nevadí a není to žádný problém. Ohledně binárního stromu už nevím o čem mluvíš.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 20:48:00
A nezapomel si na to, že když unikátní IP vkládáš do db, tak ta taky musí kontrolovat jedinečnost, tj. porovnat všechny IP? Což bude logicky mnohem náročnější počet. Vem si tedy ten extrémní případ, že je tabulka skoro plná: 249*254 záznamů. Tak při vkládání musí porovnat 63246 záznamů (IP adres) tedy 252984 bajtů. Já porovnávám 249 ajpin konkrétně 747 bajtů.

Z relační db znám jen to mysql to ten druhý typ jako uložiště znám jen mysql lite. Tam by se ušetřil ten čas na přenost, ale podle mě by to bylo nastejno. Pro mě je hlavní si to zkusit, to co pro tebe nemá praktický význam pro mě může mít význam i praktický.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 13. 12. 2013, 20:51:14
Chtěl jsem říct ne mysql lite ale sqlite. Ale z databázemi jsem moc nepracoval takže je nemám v malíčku, takže ty jak to třeba popisuješ pro tebe by to nebyl problém. Mě každopádně láká ta možnost napsat vlastní třídu a vyzkoušet něco jak by to bylo nebo nebylo efektivní, i když o neefektivnosti nebo neužitečnosti nejsem přesvědčen (spíš si myslím, že je to řešení psané na míru).
Název: Re:Binární zápis do souboru
Přispěvatel: petr 14. 12. 2013, 22:17:43
Chtěl jsem říct ne mysql lite ale sqlite. Ale z databázemi jsem moc nepracoval takže je nemám v malíčku, takže ty jak to třeba popisuješ pro tebe by to nebyl problém. ...
Ten problém je skutečně triviální, jeden sloupec, jeden index. To by jsi určitě zvládl.

Mě každopádně láká ta možnost napsat vlastní třídu a vyzkoušet něco jak by to bylo nebo nebylo efektivní...
A já to tak taky vnímám...

... (spíš si myslím, že je to řešení psané na míru).
Na míru čeho? Bude mít ta množina IP adres nějakou charakteristiku nebo nějaká vyplývá z toho, že to budou proxy servery? Já jsem tam nic speciálního neviděl, ten problém se mi jeví jako zcela obecné vyhledávání v množině čísel.

Tak zase z mého úhlu pohledu. IP adresa sice může obsahovat nuly, ale mám pocit, že toto platí jen v místní síti. Na veřejné síti se nuly neobjevují. Takže máš pravdu, že adresa vnitřní sítě např. 127.0.0.1 má jako první bajt 127, ale posuzuju podle druhého bajtu, který je 0 a tudíž by opravdu došlo k chybě. Nicméně, počítám s tím že tyto adresy tam prostě nebudou. Počítám, že zapisuji proxy adresy, ale připomínka je to dobrá. Tzn. co bych tam měl zapsat místo nuly, 255 do celého souboru?

Ne, když se pro jednoduchost budeme zabývat jen druhým nejvýznamnějším bajtem, tak jakákoliv hodnota včetně 0 či 255 může být součástí veřejné IP adresy koncového zařízení.

Co se týče adres, které mají v druhém bajtu 0 a se kterými nemusíš počítat, tak jsou IMHO následující:
- 127.0.x.x - část rozsahu 127.x.x.x pro loopback
- 10.0.x.x - lokální síť třídy A
- [>=224].0.x.x - multicast a část rezervovaného rozsahu [240-255].x.x.x
- broadcast pro každou síť, který má v bitech pro hosta samé jedničky (nejvyšší adresa v síti), např. x.0.0.255 (broadcast sítí třídy A)
- samotné adresy sítí, tj. ne koncových zařízení, které mají v bitech pro hosta samé nuly (nejnižší adresa v síti), např. x.0.0.0 - adresy všech sítí třídy A
Co ale s tím?

Takže tabulku klidně inicializovat nulama můžeš, ale aby jsi našel konec dat (první prázdný záznam), musíš kontroloval, zda jsou v tabulce nuly na pozicích všech bajtů záznamu IP adresy, protože máš záznam rozdělen na 3 bajty. Potom by mohly záznamy kolidovat jen s adresami sítí třídy A (x.0.0.0), protože hodnota 1. bajtu ti data rozděluje na segmenty, ale adresy sítí tam nebudou. Nejlepší je stejně ukládat si počet záznamů, a potom nemusíš vůbec hledat konec, budeš ho vždy znát: začátek segmentu + počet záznamů * (1 bajt).

Další věc je ta, že po zaplnění tabulky ji vyprázdníš, tudíž ztratíš informace o všech blokovaných IP adresách a začneš znovu. To asi není, to co bys chtěl. Co tam bude ty blokované IP adresy přidávat? Ideální je uchovávat si časy posledního "zásahu" pro každou blokovanou adresu a pokud ti dojde místo, nahradit ten záznam, který měl zásah před nejdelší dobou. Případně to kombinovat s četnosti záznamů apod. Triviální pro databázi (druhé pole, druhý index) nebo vytvořit druhý binární strom.

Pokud jde o konec metody checkIP tak tam je to takto:
Kód: [Vybrat]
if(...){...}
else
{ return true; // no more ips to check
}
endfor;
echo  " end of check method  ";
return true;
}

Tobě jde o to, že fread($hf,1) volám ze smyčky místo fread($hf,250) bez smyčky? ...

Psal jsem "smyčka se přeruší při prvním bajtu, který nedopovídá druhému bajtu IP adresy  a tedy téměř vždy skončí bez vrácení hodnoty".
Kód: [Vybrat]
    public function checkIP ($ip)
    {
        $arr = $this->splitIp($ip);
        $segmentBegin = ($arr[0] - 1) * $this->segmentLength;
        if (! $this->FRWBHandler)
            $this->FRWBHandler = @fopen($this->file, "r+b");
        $fh = & $this->FRWBHandler;
        for ($i = $segmentBegin; $i <= $segmentBegin + $this->segmentLength; $i ++) :
            fseek($fh, $i);
            $byte = fread($fh, 1);
            if ($byte !== "\x00") {
                if ($byte === "\x" . $arr[1]) {
                    fseek($fh, $i + $this->segmentLength);
                    $byte = fread($fh, 2);
                    if ($byte === "\x" . $arr[2]) {
                        fseek($fh, $i + 2 * $this->segmentLength);
                        $byte = fread($fh, 1);
                        if ($byte === "\x" . $arr[3])
                            return false; // this ip is blocked
                    }
                }
                break;
            } else
                return true; // no more ips to check
        endfor
        ;
    }
Špatně je ten "break", místo něho musí být "continue" a také to, že za smyčkou nevracíš true (to už jsi tam doplnil).
Ale abys ošetřil ty případné nuly v IP adresách, tak by jsi musel v každém průchodu kontrolovat všechny 3 bajty IP adresy.

Dále píšeš o režii databáze, jasně to se musí brát v úvahu, proto jsem psal "Pokud chceš udělat něco co bude efektivnější než databáze". Ale ta režie není závislá na velikosti souboru, algoritmu vyhledávání, ale spíš jak jsi psal na komunikaci mezi skriptem a databázovým serverem. Určitě to lze implementovat s menší režií bez databáze. Ta sqlite vypadá pro tenhle účel zajímavě. Defakto ti ale stačí implementovat binární strom.

Mám pocit, že jsi jednou psal, že zkoušíš udělat optimální algoritmus, jindy že ti rychlost postačuje. Kdyby jsi do jednoho souboru ukládal 250 x 32bit číslo jako IP adresu a při každém testu procházel vše, nemohlo by to být pomalejší než to máš teď. To rozdělování IP adresy na jednotlivé bajty a jejich ukládání do zvláštních segmentů nemá ve tvé implementaci téměř žádný smysl, musíš stejně otestovat všech 250 záznamů. Výjimkou je pouze ten první bajt, který neukládáš, ale určuje ti segment, ale zase ti kvůli tomu narostla velikost dat 256x.

Kdyby jsi byl důsledný, a udělal to i pro ostatní komponenty IP adresy, tak máš vlastně pole o 4 rozměrech, bitmapu o 222(?)*256*256*254 záznamech pro kompletní rozsah IP adres, tj. soubor o velikost 440MiB, tam by jsi skutečně nemusel nic hledat, a vždy by jsi přečetl nebo zapsal pouze 4 bity. Nevýhoda - veliký soubor zabírající místo na disku, nic efektivnějšího nelze ale vymyslet. Buď tedy tohle, nebo chceš mít uložen jen omezený počet IP adres a potom musíš mít seznam setříděn nebo uložen v binárním stromu. Záleží na tom kolik těch IP adres bude, a jak často se budou zapisovat, protože při zápisu musíš záznamy setřídit nebo rebalancovat strom. Budeš-li mít záznamy setříděné nebo v úplném binárním stromu, potřebuješ ke zjištění, zda IP adresa NENÍ přítomna v 250 záznamech MAXIMÁLNĚ 8 testů. Ty jich potřebuješ VŽDY 250 * 3, takže 750. Použití prvního bajtu IP adresy jako čísla segmentu ti to zredukovalo na 750 z 1000 testů ale 256x ti narostla velikost tabulky. Obě možností můžeš kombinovat a hledat kompromis mezi rychlostí a potřebnou velikostí tabulky, ale pokud se bude jednat víceméně o náhodné IP adresy, tak nemáš podle čeho optimalizovat.

Navíc kromě té kompletní bitmapy má sotva smysl ukládat komponenty IP adresy zvlášt - převeď ji na 32bit číslo.
Název: Re:Binární zápis do souboru
Přispěvatel: petr 14. 12. 2013, 23:05:05
Zase v obou metodách addIP a checkIP vidím na začátku
Kód: [Vybrat]
$segmentBegin = ($arr[0] - 1) * $this->segmentLength;, tzn. že addIP() ti přepisuje data, a ani checkIP() nemůže fungovat. Musí tam přece být
Kód: [Vybrat]
$segmentBegin = ($arr[0] - 1) * $this->segmentLength * 3;.

A jestli tedy uhádám, jak ten kód má vypadat, nebo vypadá v současné době, tak není pravda co jsem psal, že pojme 250 IP adres ale pojme 250 - 250*254 adres v závislosti na hodnotách 1. komponenty IP adresy. A také není pravda, že po každé musíš  otestovat 750 čísel ale 3 - 750 čísel.

Končím. Asi jsem blbej, protože nechápu, jak ti to může fungovat.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 14. 12. 2013, 23:57:28
Dneska to už nedávám odpovědět na všechny věci, ale tak snad aspoň částečně:

rozlišuj mezi třemi věcmi o kterých jsem tu mluvil. Původní řešení se týká seznamu proxy serverů, ten je třeba aktualizovat a stačí mi na to malý soubor. Já se rozhodl tento soubor pravidelně doplňovat o informace, takže se mohou ukládat dejme tomu i ajpiny několik staré dejme tomu 24 hodin nebo více. Zakládání databáze kvůli tomu mi příjde zbytečné.

Něco jiného je blokování uživatelů - psal jsem, že mi stačí udělat dynamicky se rozšiřující soubor (rozšíří se v místě druhého bajtu ajpiny když to bude potřebovat). Do toho souboru bude stejně tak rychlý přístup jako v tom řešení výše.

Zatřetí možnost udělat na podobném principu i databázi uživatelů. Samozžejmě výhoda je v tom, že nemusím se obtěžovat se zprávou externí databáze.

S těma ajpinama s nulama jsem se teda ještě nesetkal, ale mám pocit, že je velmi malá pravděpodobnost že na netu na ně někdy narazím. Jednoduché řešení: ajpina, která obsahuje na druhém bajtu nulu nebude moci být zapsána. Blokaci takového uživatele lbych možná mohl provést pomocí zablokování domény? Nebo zablokování celé sítě? Totiž k tomu slouží ta hlavička, kterou jsem tam přidal v nové verzi. Blokování prvních dvou bajtů a prvních třech bajtů a blokování domén.

Dneska končím.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 15. 12. 2013, 10:04:54
Ano ve staré verzi byla chyba, nemá tam být
Kód: [Vybrat]
$segmentBegin = ($arr[0] - 1) * $this->segmentLength;
ale
Kód: [Vybrat]
$segmentBegin = ($arr[0] - 1) * $this->rowLength; // což je trojnásobek délky segmentu :-)
Citace
Nejlepší je stejně ukládat si počet záznamů, a potom nemusíš vůbec hledat konec, budeš ho vždy znát: začátek segmentu + počet záznamů * (1 bajt).

No jo, jenže já musím před přidáním ajpiny nejdříve zkontrolovat jestli ta IP již není v seznamu. A právě během této kontroly v nové verzi zjistím, kde pro daný řádek je konec dat. Takže vlastně začínám číst rovnou tam kde skončila kontrola :-) . Ale postřeh to byl dobrý, to tě chválím.

Citace
Špatně je ten "break", místo něho musí být "continue" a také to, že za smyčkou nevracíš true (to už jsi tam doplnil).
Ale abys ošetřil ty případné nuly v IP adresách, tak by jsi musel v každém průchodu kontrolovat všechny 3 bajty IP adresy.
V nové verzi je return true; Ale proč continue? Pro ten blok kde je break; platí
Kód: [Vybrat]
$byte == \x00 // první bajt je nulaCož znamená: pokud jsem našel konec dat, aniž bych našel hledanou IP adresu v seznamu, pak vracím true, protože je vše v pořádku. Funkce addIP pak může pokračovat v přidání ajpiny.

Co se týče databáze tak už chápu, vyjadřoval ses k tomu mému třetímu návrhu. Na zbytek tvé reakce bych odpověděl někdy později (binární strom). Dík za pomoc a diskusi.
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 15. 12. 2013, 12:43:55
Citace
Mám pocit, že jsi jednou psal, že zkoušíš udělat optimální algoritmus, jindy že ti rychlost postačuje. Kdyby jsi do jednoho souboru ukládal 250 x 32bit číslo jako IP adresu a při každém testu procházel vše, nemohlo by to být pomalejší než to máš teď.
jestli myslíš soubor txt, tak pomalejší by to bylo v tom, že by se vytvářelo pole o velikosti cca 250 prvků. Nějaké pole sice v nové třídě používám taky, to se ale prakticky vztahuje jen k těm vkládaným číslům, takže pokud vkládám 10 čísel, bude mít pole nakonec velikost 10 prvků. Ale rychlost u txt asi celkově malá, taky zanedbatelná.

Ještě jsem uvažoval nad tím jestli tam je možnost přepsání souboru txt, ale v módu append by to myslím nebylo možné.

Citace
To rozdělování IP adresy na jednotlivé bajty a jejich ukládání do zvláštních segmentů nemá ve tvé implementaci téměř žádný smysl, musíš stejně otestovat všech 250 záznamů.
Ne, testuji jen ten řádek, do kterého chci vkládat data. Tzn. pokud chci doplnit 10 ajpin, testuju jen deset řádků, ve kterých  prohledávám jen několik bajtů. V tom je právě rozdíl oproti txt souboru. Myslím, že budu doplňovat ajpiny každých 2 a půl minuty a za tu dobu by mohlo přibýt tak 3-10 ajpin odhadem. Možná jen 2-5 teď si nejsem jistý jak rychle se to objevuje.
Ještě ale dodatek: během iniciačního vkládání mohu vložit těch ajpin třeba 100 a v nich bude třeba čínská proxy, kde se opakuje bajt na třetím řádu, takže bloknu tuhle ajpinu obecně. Tím pádem ten počet ajpin co budu přidávat ještě ubude. Prostě do hlavičky zapíšu např. 124.50.64 a tyto ajpiny budou blokovány ještě před tím než začnu hloubkově porovnávat...

To mi připomíná, že musím ještě dodělat funkci na detekci podobných adres když vkládám adresy.
 
Citace
Výjimkou je pouze ten první bajt, který neukládáš, ale určuje ti segment, ale zase ti kvůli tomu narostla velikost dat 256x.
Ale velikost souboru tu nehraje roli. Ten soubor má 186 kBajt, ale je jedno jak je velký, i kdyby měl jeden 100 mega, tak by to nemělo mít vliv na rychlost.

Citace
Kdyby jsi byl důsledný, a udělal to i pro ostatní komponenty IP adresy, tak máš vlastně pole o 4 rozměrech, bitmapu o 222(?)*256*256*254 záznamech pro kompletní rozsah IP adres, tj. soubor o velikost 440MiB,
Jenže já dělám třídu a soubor na míru. Nepotřebuju tam vkládat něco s čím se v praxi nesetkám... Je to psáno tak aby to nylo na 3 bajty. Tvůj výpočet ale stejně nechápu, ale tím se nezatěžuj, to není nutné rozebírat. Jestli si ovšem nemyslel jeden řádek pro jednu adresu :-) lol - to by byl snad soubor o velikosti 4096 megabajt což je rovných 4 giga :-).

Citace
tam by jsi skutečně nemusel nic hledat, a vždy by jsi přečetl nebo zapsal pouze 4 bity. Nevýhoda - veliký soubor zabírající místo na disku, nic efektivnějšího nelze ale vymyslet.

Ten 4 gigabajtový soubor by byl super effektivní lol :-)

Citace
Budeš-li mít záznamy setříděné nebo v úplném binárním stromu, potřebuješ ke zjištění, zda IP adresa NENÍ přítomna v 250 záznamech MAXIMÁLNĚ 8 testů. Ty jich potřebuješ VŽDY 250 * 3, takže 750. Použití prvního bajtu IP adresy jako čísla segmentu ti to zredukovalo na 750 z 1000 testů ale 256x ti narostla velikost tabulky. Obě možností můžeš kombinovat a hledat kompromis mezi rychlostí a potřebnou velikostí tabulky, ale pokud se bude jednat víceméně o náhodné IP adresy, tak nemáš podle čeho optimalizovat.
Stále nevím co je to binární strom. Ale asi tuším co myslíš, ale nevím jak takovou věc zrealizovat. Ale nechtělo by se mi řešit třídu na binární strom. Myslím že to tak nechám :-)
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 15. 12. 2013, 19:14:28
Ještě mě napadlo, že až budu dělat to blokování ajpin uživatelů (tedy nikoliv blokování proxy), tak budu nakonec muset ten design změnit protože budu chtít mít propojení na tabulku s uživateli, abych věděl s kterým účtem je blokace propojená a jak dlouho bude trvat. Zrušením blokace účtu bych odblokoval i danou ajpinu. Takže je vlastně nevyhovující mít to proveení jen v jednom bajtu. Možná si o tom už psal, já ani nevím, protože jsem tě možná nepochopil. Zkrátka první tři bajty jdoucí v řadě za sebou mohou popisovat druhý třetí a čtvrtý bajt ajpiny. Další tři-čtyři bajty mohou být propojeny s id uživatelem. Při načítání a hledání ajpin to pak budu číst po šesti nebo po sedmi bajtech.
Název: Re:Binární zápis do souboru
Přispěvatel: none_ 16. 12. 2013, 12:26:30
Myslím, že bys měl vážně zvážit využití nějaké DB. Jakmile začneš vymýšlet relace přes různé souboru, tak ti komplexita poroste, spolu s ní chybovost a myslím si, že i výkonově to nebudeš mít o moc (jestli vůbec) lepší. Jako šuplíková úloha hezký, ale do produkce bych asi takovýhle řešení radši nedával...
Název: Re:Binární zápis do souboru
Přispěvatel: webhope 16. 12. 2013, 13:33:03
Tak nejdříve je třeba to vyzkoušet a až pak to případně zavrhnout. Databázi se chci ale vyhnout každopádně, tam mi prostě vadí ta závislost a komplikovanost. I když jak petr říkal že je to banalita, tak možná pro někoho kdo dělá s databázemi každý den. Chybovost (bugy) se musí ošetřit. Pochopitelně, že ten skript asi nebude fugovat hned na poprvé, nejdříve se musí ošetřit bugy, které se ukážou až v praxi.
Název: Re:Binární zápis do souboru
Přispěvatel: none_ 16. 12. 2013, 15:38:59
No jestli to delas pro sebe, tak OK. Jinak na tom propalis kalhoty...