serwer/klient problem z wylogowaniem

0

no wiec jest tak, mam programik (w zasadzie 2 klient i serwer), calosc ma byc prostym komunikatorem konsolowym.
Wszystko w miare dobrze dziala, ale mam problem z wylogowaniem klienta. Schemat jest mniej wiecej taki: uzytkownik wpisuje 'quit', wysylana jest informacja do serwera, proces obslugujacy danego klienta przesyla informacje do glownego procesu i konczy sie, glowny proces po odebraniu informacji usowa uzytkownika z listy dostepnych i rozsyla do pozostalych uzytkownikow informacje o tym. W praktyce to nie dziala i nie wiem dlaczego :\ kod (fragmetny w ktorych jest to obslugiwane):

KLIENT:
else if(!strcmp(kom,"quit")){ // zakonczenie programu
if (!IsLogged)
	exit(0);
else {
	sprintf(buf,"%c %s",CMD_QUIT,data.login);
	send(sockfd,buf,strlen(buf),0);
	exit(0);
}
}
SERWER:
(proces obslugujacy kllienta):
else if(buf[0]==CMD_QUIT){
	forward(pkom,sock);  // funckja forward dziala, pkom gniazdo do komunikacji z glownym procesem, sock gniazdo klienta
	exit(0);
}

(glowny proces) - tu gdzies sie to wszystko wysypuje

else if(buf[0]==CMD_QUIT){			//CMD_QUIT  
// Cli - lista dostepnych klientow, tCli - obecnie obslugiwany klient
	while(recv(tCli->sock,buf,sizeof(buf),MSG_DONTWAIT)>0) ;
	sprintf(buf,"%c#%s",CMD_USRQUIT,tCli->nick);
	FD_CLR(tCli->sock,&readset);
	close(tCli->sock);
	delcli(&Cli,tCli->nick);
	SendToAll(Cli,buf);
}

Jesli jest zalogowany tylko jeden klient to go elegancko wylogowuje, jesli wiecej to juz nie..
Jesli by sie komus chcialo to moge przeslac caly kod do sprawdzenia..

0

opisz co sie dzieje w przypadku tych nastepnych logoutow,z kodu to trudno wywnioskowac tutaj cos

0

Po wylogowaniu sie jednego klienta pozostali nie moga sie komunikowac, nowi nie moga sie logowac(serwer nie odpowiada), wyglada to tak jakby glowny proces serwera sie zapetlal :, jesli byl zalogowany tylko jeden i sie wyloguje to pozniej inni (on sam tez) moga logowac sie i rozmawiac..

0

hm.. to czyste zgadywanie z mojej strony, ale moze jak procesie obslugi klietna robisz:

forward(pkom,sock);exit(0);

to socket niezostaje zamkniety prawidlowo wyslany i zamkniety i serwer z drugiej strony sobie czeka i czeka na nim? sprobuj dac shutdown na sockecie tuz przed exit

0

No wlasnie sek w tym ze to jest moj pierwszy raz z socketami i nie mam pojecia gdzie moze byc blad a nie wiem nawet w ktorym kierunku isc ze zgadywaniem :
z shutdown dalej niestety to samo..

0

um.. przepraszam, mialem na mysli parke:
shutdown(sock);
close(sock);
:) poza tym zarzuc troche wiecej kodu albo powiedz co i jak robisz.. szczerze mowiac sredni wyobrazam sobie przypadek w ktorym blad na jednym polaczeniu powodowalby zablokowanie pozostalych.. pewnie masz gdzies indziej jakas wpadke. poza tym czemu do wewnatrzserwerowej komunikacji uzywasz socketow? tylko zycie utrudniasz sobie.. na pipe'ach chyba duzo latwiej mimo ze semantyka podobna.

0

sockety bo wystarczy mi jeden do kazdego procesu, potokow musialbym miec dwa i pamietac do ktorego pisac a z ktorego czytac.
a co do kodu i wyjasniania to nie ma sensu tu rzucac calego (wczesniejsze fragmenty to wszystko co dotyczy samego wylogowywania sie, a wszystko inne dziala). jak chcesz moge ci wyslac caly kod na maila i mniej wiecej wybadasz, bo kurcze ja nie widze co tu moze byc zle :\

0

ok, wyslij mailem. szczerze od razu uprzedze ze nie wiem ile mi to dni zajmie, bo ostatnio kiepsko z czasem stoje, ale postaram sie w miare szybko.
z socketami a pipe'ami - sockety maja tylko ten jeden minus, ze jest ich systemowo ograniczona ilosc, ale przy sensownej ilosci procesow to jeden pies

0

ok, poszlo juz..
a co do powyzszego posta, to nie jest tak ze system ogolnie ogranicza liczbe otwartych deskryptorow niezaleznie gniazd/plikow czy potokow?

0

oczywiscie ze ogranicza. z tym ze tych ostatnich po zsumowaniu po wszystkich aplikacjach moze byc duzo wiecej, a socketow nie da sie wiecej niz 65k z racji natury pojecia 'port'..

mmmmmmmmmmmm.... pomyslawszy dluzej.. to tcp/ip narzuca ze port ma 16 bit.. inny protokol moze miec wiecej, wiec na dobra sprawe i gniazd moze byc wiecej.. ok, odwoluje :)

0

masz pare ladnych problemow.. przede wszystkim to ze klienci pozniej sie nie moga podlaczyc jest efektem tego, ze socket na ktorym serve_main slucha zostaje zamkniety. dosc znaczacy fakt, ze Ty nigdzie w kodzie go nie zamykasz ;) tak wiec proces serve_main musial zdechnac. za ktoryms odpaleniem serwer wyplul z siebie informacje informacje z glibc ze free() chcialo zdealokowac nieprawidlowy wskaznik - masz problem.. obawiam sie ze tego akurat nie znajde. z cala pewnoscia masz tez blad w owym sighandlerze czyszczacym zombie po procesach klientow - przeciez ta funkcja zawiesza wykonanie na tak dlugo jak istnieja dzieci! i to watek systemowy zawiesza.. takze send i recv sa wykonywane w sposob budzacy watpliwosci.. ale to Ci komentarz w kodzie zostawilem

co do crash'a serve_main: rzecz dzieje sie w if(buf[0]==CMD_QUIT). po odpaleniu serwera i jednego klienta na raz - wszystko jest ok. po odpaleniu serwera i dwoch klientow: jak pierwszy klient sie wyloguje, to jakis watek zawisa na sighandlerze i ten ifik w ogóle sie nie wykonuje. po wylogowaniu sie drugiego klienta, inny watek wchodzi w sighandler, zwalnia ostatnie dziecko, przez co oba watki wychodza i if sie wykonuje. wskazuje to na fakt, ze ten pierwszy watek byl glownym watkiem procesu serve_main ktory siedzial sobie w jakiejs funkcji systemowej i zostal uzyty do wywolania sygnalu.. a ten drugi watek (najprawdopodobniej byl to caly czas ten sam, zablokowany w wait()). tak wiec jak 'drugi' watek odblokowal wszystko, to ow 'pierwszy' sie zwolnil, powrocil do wykonywania kodu tego ifika i dotarl do SendToAll() i ... juz z niego nie wyszedl. dalsze sledzenie pokazuje, ze proces wchodzi do SendToAll, przechodzi przez if'a sprawdzajacego liste (btw: a to tlumaczy czemu dla jednego usera dziala: lista jest pusta i nie wchodzi w tego if'a), dochodzi do send i sie na nim wywala. wniosek: albo send sie wewnetrzenie wywala z jakeigos powodu dziwnego, albo tmp, albo buf jest walniety. niestety, oba sa ustawione na nie-zero i maja prawidlowa zawartosc. stad wniosek ze musial sie wywalic send jakos wewnetrznie. strzelilem ze chodzi o to blokowanie watkow w sighandlerze - "poprawilem" go i ot niespodzianka, juz mozna sie logowac i wylogowywac dowoli.. przynajmniej na moim kompie :) kod Ci wysylam mailem, tylko serwer.c bo tylko on sie zmienil. generalnie jako dobra regule pamietaj, aby nigdy nie blokowac watkow jesli nie musisz. a juz przenigdy - jesli nie wiesz co to jest za watek, a tak to wlasnie jest z sygnalami ktore przylaza w losowych momentach i aktualnie wykonywany watek je odbiera.. ogolnie rzecz biorac musisz duuuzooo poprawic.. klient tez mi segfaulty wywalal wedlug swojego widzimisie..

btw: musisz to pisac w C? przejdz na C++, uporzadkuj.. zreszta, mozesz byc w C, ale uporzadkuj koniecznie, funkcje jakies powydzielaj i powywalaj do innych plikow, zeby bloki kodu byly krotsze.. w tej chwili kod ma przejrzystosc moze nie makaronu ale porzadnie zabielonej pomidorowej, a powinien byc klarowny bulion :)

Wspomne jeszcze tylko nachalnie o tym recv/send:
One zwracaja ilosc bajtow ktore rzeczywiscie zostaja wyslane/odebrane. To ze dasz recv(sock, buf, 30, 0); wcale-a-wcale nie oznacza ze musi sie wyslac te 30 bajtow. Recv moze Ci zwrocic np. 5 i to bedzie oznaczac ze pozostalych 25 bajtow NIE WYSLAL i powinies odpalic: recv(sock, buf+5, 30-5, 0);. Specjalnie tak pisze zeby bylo jasne jak wokol tego petle zrobic :) Send dziala w identyczny sposob. Poza tym obie moga zwrocic wartosc -1 oznaczajaca blad - np. zerwanie polaczenia. To tez nalezy sprawdzac, zwlazcza jak sobie zapetlisz jak wspomnialem przed chwila, bo dodawanie -1 do aktualnej pozycji w buforze to nie dobry pomysl, a i jak bedzie -1 nalezaloby przerwac operacje i sie jakos wydostac, a przynajmniej logmsga zapisac..

a propoS logow.. zapomnialem dodac ze to bardzo dobrze ze je robisz. niestety dzialaja tylko i wylacznie te w procesie glownym, tzn serve_main. logowanie w procesach obslugujacych klientow nie dziala. Najprawdopodobniej dlatego, ze aby zakonczyc owe proces uzywasz radosnie exit(0) ani nie flushujac, ani nie zamykajac pliku. I caly outbuffer leci w hiperprzestrzen zamiast na dysk.

0

ok, dzieki wielkie :)
Pisze w C bo sie chcialem cos w tym napisac, a akurat byla okazja :P(co do uporzadkowania kodu to wiadomo, poki co pisalem na pale i mialem pozniej to powykanczac (wiem,wiem powinno sie od razu tak jak trzeba no ale coz.. :P ))
sprawdzanie bledow przy send/recv tez jest w TODO, jak i duzo wiecej rzeczy.. :D.
W kazdym razie dzieki taz jeszcze, przegladne to jutro bo dzisiaj sie nie czuje na silach ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1