napló
továbbra sem vagyok kibékülve a selecttel. az ember a socketeket nem csak úgy szanaszét tartja, hanem valamiféle wrapper objektumokban, azok meg listá(k)ban. a selectnek egy socket listát kell adni, amit aztán kislankít, csak az marad benne, amivel dolgozni kell. csakhogy, nekem aztán vissza kell keresni a socket-hez a wrappert, vagy fordítva, minden wrapperre megnézni, hogy a socketje benne van-e a maradékban.
ez nem olyan nagy áldozat, de minek? sokkal szebb lett volna, ha adhatok minden socket mellé még egy dword paramétert, amit a select érintetlenül hagy. én abba tuszkoltam volna a wrappert, vagy sorszámot.
azt is lehetett volna, hogy ne töröljön a listából, hanem minden socket mellé legyen egy longbool, amit true-ra állít, ha lehet dolgozni a sockettel. ekkor a pozíció alapján tudtam volna könnyen azonosítani őket. ez a megoldás arra is jó lett volna, hogy ne is legyen három lista, hanem csak egy, amiben minden socket mellé van egy 16 bit word, egy bitmask, amiben megadom, hogy milyen tevékenységeket akarok végezni (bit0 - read/accept, bit1 - write/connected, bit2 - error, bit3 - disconnected ...), és egy másik 16 bites bitfield, amiben meg a visszatéréskor 1-re állítja, amit lehet, és 0-ra, amit nem lehet vagy nem is kértem. ez arra is jó lett volna, hogy ne is kelljen a socket-setet minden alkalommal előállítani, csak karbantartani kell, új klienst beletenni, disconnectedeket törölni.
na de felesleges keseregni, azt kell használni, ami van.
a másik érdekesség, hogy a windows-os select az első paramétert nem használja. ugyanakkor a linuxos változat megköveteli, hogy a legnagyobb értékű socket handle + 1 legyen az értéke. ezt kicsit furának tartom, de annyi baj legyen. én aztán átadom a socketek összegének négyzetgyökét osztva szerdával, ha a linux ettől boldog.
újabb akadály: a socket set típus delphiben 64 darab socketre van korlátozva. windowsban valami define tréfával ez kövéríthető, de delphiben nem, mivel ez sima konstans. utánanézve kiderül, hogy a TFDSet az egy record, aminek van egy integer tagja, az elemszám, és egy 64 elemű integer tömbje, ezek az elemek. na ilyet én is tudok csinálni, mégpedig első körben lustadisznózunk, és egy sima dinamikus tömb lesz az, aminek az első elemébe betesszük a hosszt, a többibe meg a socketeket. nagy öröm a lustáknak, hogy a dinamikus tömb valójában egy pointer, tehát hideg fejjel átcastoljuk PFDSet-nek, és mehet a select-be. mivel később ez bele lesz rejtve egy saját select eljárásba, és csak egy helyen lesz megírva, lehet, hogy így is marad. pedig elég randa.
mindezekkel a multisocket select megy, mint a golyó, és most már egyetlen select végzi az összes várakozást, írásra, olvasásra, új kliensre.
tanulságképpen egy buta bug: első sikeres megvalósítás után kicsit kiegészítettem a progit, hogy miután a select és az utána jövő accept/read/write lement, hívja meg még egyszer a selectet, és ezt ismételje addig, amig nincs teendő. ez elég feleslegesnek tűnik, csak azért volt rá szükség, mert most ez a cucc egy gomb eventjében figyel, valamint amikor lusta vagyok nyomkodni, egy timer hivogatja. később ez persze nem így lesz, de a tesztprogramban nem árt némi kézi verzérlés, és a lustaság is megengedett. viszont emiatt ha bejön egy új kérés miközben feldolgozom a többit, az csak a következő timer eventben fog feldolgozódni, és ez lerontja a tervezett sebességteszteket. ezért hát addig ismétlem, amig tényleg el nem fogy a meló. na de a bugról kell itt beszélni. kioptimalizáltam, hogy az ismétlésnél már nem kell SetLength a dinamikus tömbökre, mert ugyanannyi socket van, mint az előbb. csak újra be kell őket pakolni, mivel a select rendet vágott köztük. ennek jutalma egy rémséges invalid pointer operation, és aztán teljes programelhülyülés lett a legelső disconnect próbálkozáskor, mégpedig akkor, amikor fel akartam szabadítani a socket-wrapperemet. az invalid pointer operation az csúnya. az azt jelenti, hogy szétromboltam a heap-et valami vigyázatlansággal. persze az olvasó tudja, hogy mi a baj: a connect során egyel több kliens lesz, tehát egyel több socket, amiket én a második menetben a régi tömbbe zúdítottam bele, ezáltal túlindexeltem azt, és valamit felülírtam, ami éppen utána volt. így kavicsot tettem a memory manager cipőjébe, ami őtet folyamatosan bosszantotta, végül a free művelet volt az, amikor kiborult, nem bírta tovább. a tanulság sok, de például az is, hogy az invalid pointer operation egyáltalán nem biztos, hogy akkor jön, amikor a bajt csináljuk, hanem lehet, hogy sokkal később.