Üdv mindenkinek!
Egy kis chat programot kezdtem el írni C++ nyelven azonban most elakadtam benne, azt hiszem valamit rosszul csinálok a hálózati programozás résznél.
Ezért írtam egy kis példa szervert meg klienst(kiszedtem belőle a szálkezelést meg a többi felesleges dolgot), hogy rájöjjek mi a gond de így se sikerült.
Feldobtam a kódot pastebinre:
szerver
kliens
tcpSocket osztály
(A tcpSocket osztályt az egyszerűség kedvéért egyben töltöttem fel a pastebinre, amúgy természetesen két külön fájlban van :)
Az az érdekes hogy ha elindítom a szervert és utána a klienst, akkor a kliens csatlakozik a szerverhez de az üdvözlő üzenetet már nem kapja meg(próbáltam a szervert egy "sleep 5" paranccsal késleltetni, hátha ez a gond, de nem), viszont ha a szerver elindítása után
telnet localhost 11111
paranccsal csatlakozok a szerverhez akkor tökéletesen működik. Bármit begépelek a kliensen, a szerver visszaküldi azt nekem.
Valaki meg tudja mondani mit csinálok rosszul? :)
Köszi!
- 2081 megtekintés
Hozzászólások
A szerver az üdvözlőszövegben nem küld entert, a kliens viszont addig olvas, amíg nem jön egy enter.
- A hozzászóláshoz be kell jelentkezni
ÓÓÓÓ a fene egye meg. Agyon kerestem mi lehet a hiba a hálózatkezelésben....
Köszönöm a segítségedet, ezt a kis hibát fél napja nem sikerült észrevennem, pedig debuggoltam is meg minden... :)
- A hozzászóláshoz be kell jelentkezni
Az baj, ha tovabbfejlesztesi celzattal lenyulnam a tcpSocket kodjat? Mar reg keresek valami egyszeru, jol ertheto kodot...
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
Ezt is nézd meg, én elég sokféle dologra használom,
saját kód, használd egészséggel (-::
protokol: ftp
domain: meditor.hu
user: public
cd ./c_examples
és itt: Mediag.tar.gz
> Sol omnibus lucet.
- A hozzászóláshoz be kell jelentkezni
Halas koszonet.
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
Használd egészséggel ha tetszik ;)
- A hozzászóláshoz be kell jelentkezni
Egész letisztult a tcpSocket osztály, de talán elfogadsz pár építő jellegű kritikát...
Ha jól látom van saját Exception osztályod. Ez felesleges, létezik szabványos exception osztály, sőt több is:
http://www.cppreference.com/wiki/exception/start
Exception helyett nyugodtan használhatnád a runtime_error-t, Network_err helyett meg csinálhatsz network_error-t, ami lehet runtime_error leszármazott...
A Send, és Recv osztályok protokollja nem túl szerencsés. Úgy szokták ezt csinálni, hogy átküldöd az üzenet méretét (mondjuk unsigned int) majd az üzenetet.
Így fogadó oldalon sem kell byte-onként olvasnod, a fórum indító hiba sosem lett volna gond, illetve nem csak string-ekkel fog később működni.
A kódod lefagy, ha Recv vagy Send közben megszakad a kapcsolat. Sőt, sehol nem látok ilyen jellegű ellenőrzést, úgyhogy valószínűleg akkor is lefagy vagy elszáll, ha bármikor megszakad a kapcsolat.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Az utolsot hogy lehet ellenorizni?
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
Szerintem csak recv és send híváskor.
Winen ha bármilyen error van, akkor SOCKET_ERROR-ral térnek vissza. Illetve a recv 0-val, ha nem megszakadt, hanem rendesen lezárták a kapcsolatot:
http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
Gondolom Linuxon is ugyanez a helyzet maximum mások a hibakódok...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Kosz.
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
Építő jellegű kritikákat mindig szivesen fogadok! :)
Köszi a tanácsokat, épp most kezdtem el átírni a Send() és a Recv() függvényeket ahogy mondtad, aztán következik a kivételkezelés.
Kérdésem is lenne akkor már: egy ilyen jellegű program esetén hogy a legcélszerűbb ellenőrizni, hogy egy kliens továbbra is kapcsolódik-e a szerverhez? (pl. ha elhalálozik az egyik kliens akkor a szerver addig nem fogja megtudni, hogy kapcsolódik-e még, amíg nem próbál neki üzenetet küldeni)
Gondoltam olyasmire, hogy mondjuk félpercenként küld a szerver minden kliensnek egy üzenetet amire mondjuk egy "OK" választ vár, amelyik klienstől meg nem kap, azt a socketet bezárja, csak nem tudom ez mennyire "szép" megoldás.
- A hozzászóláshoz be kell jelentkezni
Szerintem ezt így szokták, de nem vagyok egy hálózat guru...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
- A hozzászóláshoz be kell jelentkezni
Az egyik megoldás az, amit te is felvázoltál. Előnye az, hogy tetszőlegesre állíthatod a timeout-ot.
A másik megoldás pedig:
setsockopt(..., SO_KEEPALIVE, ...)
man 7 socket
man 2 setsockopt
- A hozzászóláshoz be kell jelentkezni
A poll(); jelzi a szakadást. A SIG_PIPE-ot fel kell húzni.
> Sol omnibus lucet.
- A hozzászóláshoz be kell jelentkezni
Köszi mindkettőtök válaszát, de közben sikerült egyszerűen a recv() == 0 figyelésével megoldanom a dolgot, ahogy azt fentebb is írták.
- A hozzászóláshoz be kell jelentkezni
A recv() az akkor tér vissza 0-val, ha a túloldal rendesen bezárta a kapcsolatot. Ha egy TCP kapcsolaton keresztül nem forgalmazol semmilyen adatot, akkor szerintem egy socketről nem tudod eldönteni, hogy "él"-e, vagy sem.
SIGPIPE szignált akkor kapsz, amikor írni probálsz egy socketre, de nem sikerül, mert az közben megszakadt, és az írás pillanatában derül ki, hogy megszakadt.
Szóval ha csak a recv() visszatérési értékét nézed, akkor adott esetben csak jóval később fogod tudni érzékelni, hogy a kapcsolat megszakadt-e. Ennek az elkerülésére kell bevezetni mondjuk a ping/pong üzeneteket, mint ahogy az irc-ben csinálják, vagy az általam korábban felvázolt SO_KEEPALIVE opciót kell megadni a socketnek.
- A hozzászóláshoz be kell jelentkezni
Nos, az én esetemben az van, hogy a kliens egy külön szálon folyamatosan recv()-el és ha 0 a recv() visszatérési értéke akkor kivételt dob és kilép.
A szerver esetében hasonló a helyzet, minden kliensnek indít egy külön szálat ami folyamatosan recv()-el és ha 0 akkor kilépteti az adott klienst.
Ha kipróbálom és elindítok egy szervert meg pár klienst és az egy klienst bezárom CTRL-C lenyomásával, a szerver azonnal küldi az üzenetet a többi kliensnek hogy XY kijelentkezett. Nekem úgytűnik ez így teljesen jól működik.
Egy olyan program esetében ami csak időnként küld/vár üzenetet érthető, hogy miért nem jó ez a megoldás, az én esetemben viszont szerintem nem lenne értelme átírni a kódot úgy ahogy mondod. Vagy igen?
- A hozzászóláshoz be kell jelentkezni
A Ctrl-C ha úgy vesszük egy rendes bezárás, hiszen a háttérben az operációs rendszer bezárja a kapcsolatot, amit a túloldali szerver érzékelni fog.
De mondjuk próbáld ki azt, hogy két gépet használsz, az egyikről mész a másikra, és kihúzod a köztük lévő kábelt (vagy lehúzod az interface-t). Akkor hány perc múlva fogod érzékelni, hogy bezáródott a kapcsolat? Azt te döntsd el, hogy megéri-e neked erre az esetre is felkészíteni a programot, vagy sem.
- A hozzászóláshoz be kell jelentkezni
Értem, köszi, erre a lehetőségre nem gondoltam.
- A hozzászóláshoz be kell jelentkezni
Igen, a ctrl-c -nek megvan ez a kellemetes/kellemetlen tulajdonsaga, hogy rendes bezarasnak minosul. Meg a kill -9 sem eletbiztositas a megszakadas tesztelesere. A kabelkitepkedes a tuti fix.
--
()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.
- A hozzászóláshoz be kell jelentkezni
hogy átküldöd az üzenet méretét (mondjuk unsigned int) majd az üzenetet.
+ Network byte orderben, ahogy illik.
---
pontscho / fresh!mindworkz
- A hozzászóláshoz be kell jelentkezni
Ezt kifejtenéd kicsit bővebben?
Minden egyes üzenetet network byte orderben kellene elküldeni?
És ha igen, miért? Tapasztalatom szerint működik anélkül is.
- A hozzászóláshoz be kell jelentkezni
Mert meg nem programoztal multiplatform kornyezetben.
---
pontscho / fresh!mindworkz
- A hozzászóláshoz be kell jelentkezni
A lényeg a következő: TCP kapcsolatot használsz, aminek az a jellemzője, hogy nem tudod, hogy az üzenetnek hol vannak a határai, tehát ha írsz két 10 bájtos üzenetet, akkor azt a túloldal kiolvashatja 20bájtként is, de kiolvashatja 12+8bájtként is, stb...
Tehát meg kell oldanod, hogy minden üzenethez egy fejlécet csatolsz, amibe beírod azt is, hogy milyen hosszú az üzenet. Ezt nyilván megteheted többféleképpen is: 10-es számrendszerben szövegesen, vagy ennél az elterjettebb megoldás, hogy csinálsz egy fejlécet, amibe felveszel egy egész számot, ami az üzenet hosszát hordozza:
typedef struct {
int length;
int type; // esetleg az uzenet tipusa
} header_t;
És ekkor minden üzenet elé berakod ezt a kis fejlécet. Ennek a hátrányai:
- azt int típusa nem fix méretű, van ahol 32 bit, van ahol 16 bit. -> megoldás: uint32_t length; típus használata (stdint.h), ami minden gépen ugyannyi.
- az int bájt sorrendje nem rögzített. Pl length = 0x01, akkor egy 32 bites little-endian gépen (i386) ez 0x01, 0x00, 0x00, 0x00 formában megy át, egy big-endian gépen (sparc asszem) pedig 0x00, 0x00, 0x00, 0x01 formában. Ezért van a socket programozásnál a htons (16bit), htonl(32bit)-es függvények, de a /usr/include/endian.h-ban találsz egy rakatot.
Szóval egy platform független alkalmazás esetében az egész számok méretét nem árt rögzíteni, továbbá a bájt sorrendet. A network bájt order big-endian, a mérete nincs rögzítve, a korrektség kedvéért azt sem árt rögzíteni.
Persze ilyenkor nem árt az alábbiakra figyelni:
- ha olvasol a socketról, meg kell nézned, hogy a fejléc teljes egészében megérkezett-e, mielőtt feldolgozod a benne lévő adatot,
- ha megérkezett a fejléc, akkor meg kell nézni, hogy a utána megjött-e a hosszában közölt adat.
- A hozzászóláshoz be kell jelentkezni
Es ez nem csak a halozati adat atvitelre igaz, hanem mindenhol ahol feltetelezheto, h heterogen rendszerek kozott kell adatot mozgatni.
---
pontscho / fresh!mindworkz
- A hozzászóláshoz be kell jelentkezni
Így már érthető, köszi! :)
- A hozzászóláshoz be kell jelentkezni
Egyébként ha valakinek esetleg vannak jó doksiai hálózati programozással kapcsolatban, azt megköszönném ha linkelné :)
Eddig az egyetlen jó amit találtam az a Beej's Guide to Network Programming. Ez az alapokat jól leírja szerintem, de komolyabb dolgokat annyira nem részletez.
- A hozzászóláshoz be kell jelentkezni
Ebből érdemesebb a mindig friss angol eredetit olvasni. A magyar fordítás régi, Beej átdolgozta az anyagot rendesen már.
- A hozzászóláshoz be kell jelentkezni
Köszönet az infóért, ezt észre se vettem :P
- A hozzászóláshoz be kell jelentkezni