[WINSOCKET] Rozlaczanie servera i brak zwr. 0 przez recv

0

Witam.

Gdy klient się połączy z serverem to gdy server się rozłączy funkcja recv zwroci 0.

Jednak gdy server "połączy sie i rozlaczy sie" kilka razy to za ktoryms razem funkcja recv nie zwraca 0, i klient NIE WIE nawet ze server sie rozlaczyl

void Client::Thread(Client* thisClient)
{
	BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

	// Dopóki klient jest połączony z serverem
	while(thisClient->clientSocket)
	{
		int recvBytes;

		// Dopóki otrzymuje się pakiety > 0 i klient jest połączony z serverem
		while(((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0) && thisClient->clientSocket)
		{	
			// Długość pakietu
			int packetLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) + 4;
			int dataLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);

			while(recvBytes != packetLength)
			{
				recvBytes += recv(thisClient->clientSocket,(char*)(buffer+recvBytes),packetLength-recvBytes,0);
			}

			ONRECV onRecvFunction = (ONRECV)thisClient->onRecvFunction;
			onRecvFunction(buffer);

			thisClient->packetReceived++;
			recvBytes = 0;
			
			Sleep(1);
		}	

		// Klient jest rozłączony z serverem to można samemu rozłączyć
		thisClient->Close();

		ONCLOSE onCloseFunction = (ONCLOSE)thisClient->onCloseFunction;
		onCloseFunction();
	}

	if(buffer)
	delete[] buffer;
}

dlaczego tak jest?

z gory dziekuje za pomoc

0

Może dlatego, że masz tak skonstruowaną pętlę odbierającą?

while(((recvBytes = recv(...)) > 0) && ...)
{       
	...
	while(recvBytes != packetLength)
	{
		recvBytes += recv(...); //<--- a co jeśli będzie <=0, przerwie się pętla?
	}
	
	...
}       
BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

...

if(buffer) delete[] buffer;

To sprawdzenie, czy buffer nie jest NULL'em, jest bez sensu, bo new w tej formie nie zwróci NULL'a przy nieudanej alokacji, tylko rzuci wyjątek bad_alloc. Nawet jakby zwracało NULL'a, to i tak aplikacja wyłożyłaby się przy wykonywaniu pętli, nie doszłoby do wykonania tej linii kodu. Rozsądniej byłoby użyć vector'a.

0

Dzięki wielkie, teraz wszystko działa : P

0

Ops, jednak nie jest dobrze.


void Client::Thread(Client* thisClient)
{
	BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

	// Dopóki klient jest połączony z serverem
	while(thisClient->clientSocket)
	{
		int recvBytes;

		// Dopóki otrzymuje się pakiety > 0 i klient jest połączony z serverem
		while(((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0) && thisClient->clientSocket)
		{	
			bool breakLoop = false;

			// Długość pakietu
			int packetLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) + 4;
			int dataLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);

			while(recvBytes != packetLength)
			{
				int recvNow = recv(thisClient->clientSocket,(char*)(buffer+recvBytes),packetLength-recvBytes,0);

				if(recvNow <= 0)
				{
					breakLoop = true;
					break;
				}

				recvBytes += recvNow;				
			}
			
			if(breakLoop)
				break;

			ONRECV onRecvFunction = (ONRECV)thisClient->onRecvFunction;
			onRecvFunction(buffer);

			thisClient->packetReceived++;
			recvBytes = 0;
			
			Sleep(1);
		}	

		// Klient jest rozłączony z serverem to można samemu rozłączyć
		thisClient->Close();

		ONCLOSE onCloseFunction = (ONCLOSE)thisClient->onCloseFunction;
		onCloseFunction();
	}

	delete[] buffer;
}

Teraz to wyglada tak, i jak sie server rozlaczy to jest CPU 100 %, ale nadal czasami nie dochodzi do thisClient->Close();

0

CPU 100%

dosc prosto taki efekt osiagnac, jesli przy petlach:

while(thisClient->clientSocket)
    while(((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0) && thisClient->clientSocket)

okazaloby sie, ze socket jest != 0, i np. rowny INVALID_SOCKET (-1) -- recv zwraca blad, inner while sie nie wykonuje, outer while zas kreci ile wlezie. sprawdz to

0

Czy wywołanie Client::Close zeruje zmienną Client::clientSocket, a co za tym idzie, przerywa główną pętlę?


PS. jeśli odpowiedź brzmi tak, to nie wiem po co w ogóle ta pętla.

0

Tak, ta funkcja zeruje clientSocket.

0

Zmieniłem teraz na coś takiego:

void Client::Thread(Client* thisClient)
{
	BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

	while(!thisClient->clientSocket)
		Sleep(1);

	int recvBytes = 0;

	// Dopóki otrzymuje się pakiety > 0 i klient jest połączony z serverem
	while(((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0) && thisClient->clientSocket)
	{	
		bool breakLoop = false;

		// Długość pakietu
		UINT32 packetLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) + 4;
		UINT32 dataLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);

		while(recvBytes != packetLength)
		{
			int recvNow = recv(thisClient->clientSocket,(char*)(buffer+recvBytes),packetLength-recvBytes,0);

			if(recvNow <= 0)
			{
				breakLoop = true;
				break;
			}

			recvBytes += recvNow;				
		}
		
		if(breakLoop)
			break;

		ONRECV onRecvFunction = (ONRECV)thisClient->onRecvFunction;
		onRecvFunction(buffer);

		thisClient->packetReceived++;
		
		Sleep(1);
	}	

	MessageBox(NULL,"close","d",MB_OK);

	// Klient jest rozłączony z serverem to można samemu rozłączyć
	thisClient->Close();

	ONCLOSE onCloseFunction = (ONCLOSE)thisClient->onCloseFunction;
	onCloseFunction();

	delete[] buffer;
}

Jak server się rozłączy to powinien sie wysiwetlic message box z napisem "close", jednak sie nie wyswietla i na dodatek klient zuzywa 100 % CPU,

btw, moze to cos ma do rzeczy:

mam jeszcze jeden wątek, ktory odnawia połączenie z serverem, gdyby server się wyłączył i za iles tam czasu włączył.


void Gad::Thread(Gad* gad)
{
        // Wyslanie pakietu powitalnego
	protocol->sendWelcome();

	while(true)
	{
		if(!protocol->client->IsConnected())
		{
                        // Odnowienie winsocka (jakby bylo wywolane WSACleanup() )
			protocol->StartWinsock();

                        // Proba polaczenia z serverem
			protocol->client->Renew();

                        // Wyslanie pakietu powitalnego
			protocol->sendWelcome();
		}

		Sleep(2000);
	}
}

Tak wyglada konstruktor obiektu Client


Client::Client(Protocol* protocol, void* onRecvFunction, void* onCloseFunction, const char* ip, int port)
{
	this->packetSent = 0;
	this->packetReceived = 0;
	this->clientSocket = 0;
	this->onRecvFunction = onRecvFunction;
	this->onCloseFunction = onCloseFunction;

	this->ip = new char[30];
	strcpy(this->ip,ip);

	this->port = port;

	clientSocket = socket(AF_INET,SOCK_STREAM,0);

	sockaddr_in sin;
	hostent* host;

	host = gethostbyname(ip);

	if(!host)
	{
		Close();
		WSACleanup();

		return;
	}

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = ((in_addr*)(host->h_addr))->s_addr;
	sin.sin_port = htons(port);

	if(connect(clientSocket,(sockaddr*)&sin,sizeof(sin)) == SOCKET_ERROR)
	{
		Close();
		WSACleanup();

		return;
	}

	// Tworzenie wątku
	hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Client::Thread,(DWORD*)this,0,NULL);
}

A tutaj niektore funkcje


bool Protocol::StartWinsock()
{
	WORD wVersion;
	WSADATA wsaData;

	wVersion = MAKEWORD(2,0);
	if(WSAStartup(wVersion,&wsaData) != ERROR_SUCCESS)
	{
		WSACleanup(); 
		return false;
	}
	if((LOBYTE(wsaData.wVersion)!=2) || (HIBYTE(wsaData.wVersion)!=0))
	{
		WSACleanup();
		return false;
	}

	return true;
}

void Client::Close()
{
	if(clientSocket)
	{
		closesocket(clientSocket);
		clientSocket = 0;
	}
}

void Client::Renew()
{
	if(hThread)
	{
		TerminateThread(hThread,0);
		hThread = 0;
	}

	Close();

	clientSocket = socket(AF_INET,SOCK_STREAM,0);

	sockaddr_in sin;
	hostent* host;

	host = gethostbyname(ip);

	if(!host)
	{
		Close();
		WSACleanup();

		return;
	}

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = ((in_addr*)(host->h_addr))->s_addr;
	sin.sin_port = htons(port);

	if(connect(clientSocket,(sockaddr*)&sin,sizeof(sin)) == SOCKET_ERROR)
	{
		Close();
		WSACleanup();

		return;
	}

	hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Client::Thread,(DWORD*)this,0,NULL);
}


0

pierwotnie sie dopisalem na koniec posta 0x666, ale potem stwierdzilem ze napisze nowy:

0x666 napisał(a)

PS. jeśli odpowiedź brzmi tak, to nie wiem po co w ogóle ta pętla.

tez sie dziwie, zwlaszcza ze autor ponizej twierdzi ze zeruje, i tym dziwniejsze jest wiec '&& thisClient->clientSocket' w drugim while :/

bo.. while(recvBytes != packetLength)
a co jesli serwer wysle w sumie pare bajtow wiecej niz packetLength zaklada? zmien na <, obecnie teoretycznie ten fragment tez moze Ci zrobic semi-infinite loop, choc standardowe zachowanie recv przy zerwaniu polaczenia polaczone z if(recvNow <= 0) powinno przed tym chronic..

edit:

mam jeszcze jeden wątek, ktory odnawia połączenie z serverem, gdyby server się wyłączył i za iles tam czasu włączył.

oczywiscie ze to moze miec znaczenie.. przeciez watek odnawiajacy polaczenie moze sie np. natychmiast obudzic po zerwaniu, zanim jeszcze Twoj poprzedni watek przejdzie pomiedzy dwoma recvami (udanym i tym ktory mialby sie nieudac), i przy odrobinie pecha taki np. packetLength moze sie ustawic na dosc duza losowa wartosc..

0

Zmienilem na cos takiego:


void Client::Thread(Client* thisClient)
{
	BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

	while(!thisClient->clientSocket)
		Sleep(1);

	int recvBytes = 0;

	// Dopóki otrzymuje się pakiety > 0
	while((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0)
	{	
		bool breakLoop = false;

		// Długość pakietu
		int packetLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) + 4;
		int dataLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);

		while(recvBytes < packetLength)
		{
			int recvNow = recv(thisClient->clientSocket,(char*)(buffer+recvBytes),packetLength-recvBytes,0);

			std::stringstream ss;

			ss << recvNow << " " << recvBytes << " " << packetLength;

			MessageBox(NULL,ss.str().c_str(),"recvNow, recvBytes, packetLength",MB_OK);

			if(recvNow <= 0)
			{
				breakLoop = true;
				break;
			}

			recvBytes += recvNow;				
		}
		
		if(breakLoop)
			break;

		ONRECV onRecvFunction = (ONRECV)thisClient->onRecvFunction;
		onRecvFunction(buffer);

		thisClient->packetReceived++;
		
		Sleep(1);
	}	

	MessageBox(NULL,"Close","Close",MB_OK);

	// Klient jest rozłączony z serverem to można samemu rozłączyć
	thisClient->Close();

	ONCLOSE onCloseFunction = (ONCLOSE)thisClient->onCloseFunction;
	onCloseFunction();

	delete[] buffer;
}

I nadal nie działa : /

po prostu messagebox wyswietla pakiety, a jak sie rozlacze to czasami sie wyswietla messagebox z close, a czasami nie, i jak sie nie wyswietli to 100 % CPU sie robi.

0

Hmm, dziwne. Wewnętrzna pętla w tej formie nie powinna "kręcić" bez przerwy. A to jest jedyna rzecz w tej funkcji, która mogłaby powodować 100% zajęcie procesora. Podejrzewam, że jak zwykle błąd leży gdzieś indziej... szczególnie, jeśli mamy do czynienia z aplikacją wielowątkową.

A tak poza tym:

while(true)
{
	if(...)
	{
		protocol->StartWinsock(); //<--- wywołanie WSAStartup
		...
	}

	...
}

Nie za często wywołujesz WSAStartup, zważywszy, że nie idzie to w parze z WSACleanup?

void Client::Renew()
{
	if(hThread)
	{
		TerminateThread(hThread,0); //<--- ZUO
		hThread = 0;
	}

	...
}

To zły sposób kończenia wątku.

0

Z WSAStartup juz sprawdzam,

a co do TerminateThread to jak inaczej zamknac wątek?

0

Tradycyjnie: zmienna volatile, która sygnalizuje, że powinniśmy wyskoczyć z funkcji wątka, plus funkcja WaitForSingleObject. TerminateThread stosujesz tylko wtedy, gdy zupełnie nie masz kontroli nad wątkiem.

0

Zauwazylem jedna bardzo wazna rzecz:

To czy prawidlowo sie rozlaczy zalezy w ktorym momencie server sie rozlaczy:


void Client::Thread(Client* thisClient)
{
        BYTE* buffer = new BYTE[1 * 1024 * 1024 * 6];

        while(!thisClient->clientSocket)
                Sleep(1);

        int recvBytes = 0;

        // Dopóki otrzymuje się pakiety > 0
        while((recvBytes = recv(thisClient->clientSocket,(char*)buffer,4,0)) > 0)
        {       
                bool breakLoop = false;

                // Długość pakietu
                int packetLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) + 4;
                int dataLength = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);

                MessageBox(NULL,"message box pierwszy","1",MB_OK);

                while(recvBytes < packetLength)
                {
                        int recvNow = recv(thisClient->clientSocket,(char*)(buffer+recvBytes),packetLength-recvBytes,0);

                        std::stringstream ss;

                        ss << recvNow << " " << recvBytes << " " << packetLength;

                        MessageBox(NULL,ss.str().c_str(),"message box drugi",MB_OK);

                        if(recvNow <= 0)
                        {
                                breakLoop = true;
                                break;
                        }

                        recvBytes += recvNow;                               
                }
               
                if(breakLoop)
                        break;

                ONRECV onRecvFunction = (ONRECV)thisClient->onRecvFunction;
                onRecvFunction(buffer);

                thisClient->packetReceived++;
               
                Sleep(1);
        }       

        MessageBox(NULL,"Close","Close",MB_OK);

        // Klient jest rozłączony z serverem to można samemu rozłączyć
        thisClient->Close();

        ONCLOSE onCloseFunction = (ONCLOSE)thisClient->onCloseFunction;
        onCloseFunction();

        delete[] buffer;
}

jezeli server sie rozlaczy w momencie message box 1, to wtedy wszystko jest ok, dalszy recv zwraca -1 i klient sie prawidlowo rozlacza.

ale jezeli server sie rozlaczy w momencie message box 2, to po prostu wszystko sie przerywa... hmm tak jakby stanelo wtedy na funkcji recv, zaraz to sprawdze

0

yy nie recv, tylko onRecv

0

Już chyba wszystko wiem.

w onRecv, klient parsuje wiadomosci od servera, na niektore z nich mu odpowiada wysylajac pakiet, jednak wysyla go gdy server jest juz rozlaczony - jednak o tym nie wie, bo jeszcze nie odebralo wyniku 0 od recv

void Protocol::SendToServer(Packet* packet)
{
	THREAD_LOCK(lock);

	if(client->clientSocket)
	{
		UINT32 sentBytes = 0;

		while(sentBytes != packet->getLength())
		{
			sentBytes += send(client->clientSocket,(char*)(packet->buffer+sentBytes),packet->getLength()-sentBytes,0);
		}
	}

	THREAD_UNLOCK(lock);
}

niby jest sprawdzane czy clientSocket > 0, tylko ze co z tego jak jeszcze nie zostalo clientSocket wyzerowane.

wiec chyba wszystko juz wiadomo.

tylko jak to naprawic? <ort>z kad</ort> ta funkcja ma wiedziec ze server jest juz rozlaczony jak jeszcze nie recv nie zwrocilo 0.

0

No przecież send zwraca SOCKET_ERROR, jeśli coś pójdzie nie tak.

0

Juz wszystko jasne,

zmienilem troche funkcje wysyłającą, ze jak send zwroci -1 to przerywa pętle wysyłania.

Teraz juz WSZYSTKO DZIAŁA.

dzięki wielkie za pomoc i poświęcony czas dla 0x666 i quetzalcoatl

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