/** ****************************************************************************** @file /net/client.cpp @brief Trida klienta @author Vta @version 1.0 ******************************************************************************/ #include "net/client.h" #include "common/utils.h" #include "common/xml/strda.h" #include "common/compatibility.h" #include namespace net { #define DEBUG_CONNECTION_DELAY 0 ///< Testovani prodlevy pri pripojeni int TClient::sendMessage(TXMLdata * data) { this->lock(); xml_in_da->reset(); data->toXML(xml_in_da); char * content=NULL; content=xml_in_da->getData(); // int contentlen=datostr(&content, xml_in_da,0); // nejdriv to hodim do sveho charu // int to = data->getDataInt("to_",0,NULL); int i=-1; if ((this->localGame==1) || (this->connected==1)) { i=this->queue->addMessage(content,this->fd); // vsechny data od klienta miri jen na server, ten je pak smeruje dal } else { GLOBALLOGID(PRIORITY_SENDING_DATA, "Can not send any data untill the client is connected"); // printf("Cant send a messeage while disconnected"); } this->unlock(); return i; } int TClient::findServer(char * ip) { this->he = gethostbyname(ip); if (!he) { GLOBALLOGID(PRIORITY_FATAL, "Klient - can not find the server"); // KSendMessage(RQUEUE,MSG_NET_CONNECTIONFAILED,MOD_NET,BROADCAST,NULL); // informace o neuskutecnenem pripojeni // THROW(E_8K_NET,"Klient - can not find the server"); return -1; } else return 0; } int TClient::connectToServer() { if (this->fd!=0) { if(MyConnect(this->fd,(struct sockaddr *)&(this->server),sizeof(struct sockaddr)) < 0) { // printf("Connect se nezdaril"); GLOBALLOGID(PRIORITY_FATAL, "Client - connect error"); // KSendMessage(RQUEUE,MSG_NET_CONNECTIONFAILED,MOD_NET,BROADCAST,NULL); // informace o neuskutecnenem pripojeni THROW(E_8K_NET,"Client - connect error"); // printf("connect error"); // printf("%i",WSAGetLastError()); // return -1; } else { GLOBALLOGID(PRIORITY_NEW_CONNECTION, "Client - the client has connected to the server"); // printf("connected"); this->connected=1; return 0; } } return -1; } void TClient::getMyProfileName(char profile_name[K8_MAX_NAME_LEN]) { strncpy(profile_name,this->profile_name,K8_MAX_NAME_LEN); } void TClient::socketWasClosed() { this->lock(); this->connected=0; if (this->fd!=0) MyCloseSocket(this->fd); this->fd=0; this->unlock(); } /// Funkce bezici ve vlastnim vlakne pripojici klienta k serveru int connectToServer_thread(void * data) { TClient * that=(TClient *) data; SDL_SemPost(that->sem); try { if (that->findServer(that->address)==-1) { // nenaslo to pozadovanou adresu if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_NET_CONNECTIONFAILED,MOD_NET,BROADCAST,NULL); return 0; } that->createMySocket(); that->server.sin_family = AF_INET; that->server.sin_port = htons(that->port); that->server.sin_addr = *((struct in_addr *)that->he->h_addr); // if (DEBUG_CONNECTION_DELAY>0) // SDL_Delay(DEBUG_CONNECTION_DELAY); that->connectToServer(); that->sem2=SDL_CreateSemaphore(0); that->recvThread=SDL_CreateThread(recvClient_thread, (void*)that); SDL_SemWait(that->sem2); SDL_DestroySemaphore(that->sem2); // while (that->status!=STATUS_THREAD_IS_RUNNING) { } that->status=STATUS_RUNNING; if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_NET_CONNECTIONSUCCESSFUL,MOD_NET,BROADCAST,(void*)that->profile_name); } catch (E_8K_NET) { if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_NET_CONNECTIONFAILED,MOD_NET,BROADCAST,NULL); } return 0; } int recvClient_thread(void * data) { TClient * that=(TClient *) data; try { SDL_SemPost(that->sem2); fd_set readfds; timeval timeout; timeout.tv_sec=0; timeout.tv_usec=0; // mysocket sock; int s; int len=0; mysocket maxDescriptor=that->fd; while (1) { if (that->status==STATUS_ENDING) // pokud server konci { GLOBALLOGID(PRIORITY_THREAD, "Client - recvClient_thread is ending"); // printf("Ending recv thread"); return 0; } if (that->fd!=0) { FD_ZERO(&(readfds)); FD_SET(that->fd, &(readfds)); // hlida to jen tento fd #ifdef WIN32 s=MySelect (0, &(readfds), NULL,NULL, &timeout); #else s=MySelect (maxDescriptor+1, &(readfds), NULL,NULL, &timeout); #endif if (that->status==STATUS_ENDING) // pokud server konci { GLOBALLOGID(PRIORITY_THREAD, "Client - recvClient_thread is ending"); // printf("Ending recv thread"); return 0; } if (s>0) { if (FD_ISSET(that->fd,&(readfds))) // nic jineho ani nastat nemuze { // printf("%i\n",that->size_of_next_message); if (that->status!=STATUS_ENDING) len=that->recvFrom(that->fd,that->recvbuff,that->decompressbuff,0,that->size_of_next_message); else return 0; /* // prvni zprava musi byt korektni if ((that->my_clientid==-1) && (that->size_of_next_message>MAX_LENGHT_UNAUTHORIZED_MESSAGE)) { GLOBALLOGID(PRIORITY_NEW_CONNECTION, "Client - connection to server was closed due to strange message received"); that->socketWasClosed(); KSendMessage(RQUEUE,MSG_NET_CONNECTIONFAILED,MOD_NET,BROADCAST,NULL); return 0; } else */ { if (len<=0) // nic neprijal - server uzavrel spojeni { // sock=readfds.fd_array[s-1]; GLOBALLOGID(PRIORITY_NEW_CONNECTION, "Client - connection to server was closed"); // printf("Socket fd se zavrel len: %i, size_o_m: %i\n",len,that->size_of_next_message); that->lock(); that->setMyClientId(-1); that->socketWasClosed(); if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_NET_LOST_CONNECTION,MOD_NET,BROADCAST,NULL); that->unlock(); return 0; } else // neco prijal - zije - zvednu ping { that->setPing(); } } } } } SDL_Delay(COMM_WAIT_AFTER_SELECT); // close sock() } } catch (E_8K &e) { GLOBALLOGID(PRIORITY_EXCETION_IN_THREAD, "Exception in recvClient_thread"); char err[1000]; snprintf(err,999,"%s:%i:%s",e.getFileName(),e.getLineNumber(),e.getDescription()); err[999]=0; GLOBALLOGID(PRIORITY_EXCETION_IN_THREAD, err); if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_EXCEPTION_IN_THREAD,MOD_NET,BROADCAST,&e); } return 0; } void TClient::startPing() { #if (PING_ENABLED == 1) this->lock(); this->last_ping_received=SDL_GetTicks(); this->sem3=SDL_CreateSemaphore(0); this->pingThread=SDL_CreateThread(pingClient_thread, (void*)this); SDL_SemWait(this->sem3); SDL_DestroySemaphore(this->sem3); this->unlock(); #endif } int TClient::prepare(char * connectto) { // toto vlakno musi bezet vzdy this->sem=SDL_CreateSemaphore(0); this->sendThread=SDL_CreateThread(sendInThread, (void*)this); SDL_SemWait(this->sem); SDL_DestroySemaphore(this->sem); // while (this->status!=STATUS_THREAD_IS_RUNNING) { } this->status=STATUS_PREPARE; if (this->localGame==0) { strncpy(this->address,connectto,128); this->address[127]=0; KSendMessage(RQUEUE,MSG_NET_START_NETWORK,MOD_NET,MOD_NET,NULL); // this->start(); this->sem=SDL_CreateSemaphore(0); SDL_CreateThread(connectToServer_thread, (void*)this); SDL_SemWait(this->sem); SDL_DestroySemaphore(this->sem); // while (this->status!=STATUS_THREAD_IS_RUNNING) { } } else { this->status=STATUS_RUNNING; } return 1; } void TClient::copyClientsInfo(TClientsInfo * ci) { int i; int j=0; for (i=this->clients_info->getNext(-1);i!=-1;i=this->clients_info->getNext(i)) { int ii = (*this->clients_info)[i]->clientid; ci->cinfo[j].clientid=ii; strncpy(ci->cinfo[j].profileName,(*this->clients_info)[i]->profileName,K8_MAX_NAME_LEN); ci->cinfo[j].profileName[K8_MAX_NAME_LEN-1]=0; ci->cinfo[j].local=(*this->clients_info)[i]->local; j++; } ci->num=j; } void TClient::removeClientInfo(int clientid) { if ((*this->clients_info)[clientid]!=NULL) { KMemFree((*this->clients_info)[clientid]); (*this->clients_info)[clientid]=NULL; } } void TClient::addClientInfo(TNodeInfo * client) { if ((*this->clients_info)[client->clientid]==NULL) { (*this->clients_info)[client->clientid]=(TClientInfo*)KMemAlloc(sizeof(TClientInfo)); strcpy((*this->clients_info)[client->clientid]->profileName,client->profileName); (*this->clients_info)[client->clientid]->clientid=client->clientid; // bud je to ten klient bezici v apliaci po siti, nebo je to lokalni klient if ((this->my_clientid==client->clientid) || (client->localConnection==1) ) (*this->clients_info)[client->clientid]->local=1; else (*this->clients_info)[client->clientid]->local=0; } } void TClient::setMyClientId(int clientid) { // if ((clientid>-1) && (this->localGame!=1)) // KSendMessage(RQUEUE,MSG_NET_CONNECTIONSUCCESSFUL,MOD_NET,BROADCAST,(void*)this->profile_name); this->my_clientid=clientid; } int TClient::getMyClientId() { return this->my_clientid; } void TClient::setPing() { #if (PING_ENABLED==1) this->lock(); this->last_ping_received=SDL_GetTicks(); this->unlock(); #endif } int pingClient_thread(void * data) { TClient * that=(TClient *) data; try { int i; timeval timeout; timeout.tv_sec=0; timeout.tv_usec=0; // that->lock(); SDL_SemPost(that->sem3); // that->unlock(); TPackage ping(MSG_NET_PING); while (1) { for (i=0;i<2;i++) { if ((that->status==STATUS_ENDING) || (that->fd==0)) // pokud server konci { GLOBALLOGID(PRIORITY_THREAD, "Client - recvClient_thread is ending"); // printf("Ending recv thread"); return 0; } SDL_Delay(that->ping_time*1000); } if (SDL_GetTicks()>(that->last_ping_received+that->max_timeout*1000)) { GLOBALLOGID(PRIORITY_NEW_CONNECTION, "Client - connection to server was closed due to expired timeout"); that->lock(); that->setMyClientId(-1); that->socketWasClosed(); // printf("Ping - Timeout"); that->unlock(); if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_NET_LOST_CONNECTION,MOD_NET,BROADCAST,NULL); return 0; } ping.send(that->my_clientid,TO_SERVER,MOD_NET); } } catch (E_8K &e) { GLOBALLOGID(PRIORITY_EXCETION_IN_THREAD, "Exception in pingClient_thread"); char err[1000]; snprintf(err,999,"%s:%i:%s",e.getFileName(),e.getLineNumber(),e.getDescription()); err[999]=0; GLOBALLOGID(PRIORITY_EXCETION_IN_THREAD, err); if (that->status!=STATUS_ENDING) KSendMessage(RQUEUE,MSG_EXCEPTION_IN_THREAD,MOD_NET,BROADCAST,&e); } return 0; } TClient::TClient(int localGame,int port,char * profile_name) { this->pingThread=NULL; this->size_of_next_message=0; this->fd=0; this->my_clientid=-1; this->port=port; this->localGame=localGame; this->decompressbuff=new(DA)(1000); this->connected=0; strncpy(this->profile_name,profile_name,K8_MAX_NAME_LEN); this->recvbuff=new DA(MAX_SIZE_OF_MESSAGE); // buffer na prichozi zpravy this->clients_info=new(DA)(MAX_CLIENTS); this->last_ping_received=0; } TClient::~TClient() { int i; this->queue->shutdownQueue(); if ( (this->localGame==0) && (this->status==STATUS_RUNNING) ) // pokud je to lokalni hra, tak se to vlakno nevytvarelo a pokud neni STATUS_RUNNING, // nestihlo se to vlakno vubec pustit ve vlakne connectToServer_thread() { this->status=STATUS_ENDING; SDL_WaitThread(this->recvThread,NULL); if (this->pingThread!=NULL) SDL_WaitThread(this->pingThread,NULL); } this->status=STATUS_ENDING; delete(this->decompressbuff); SDL_WaitThread(this->sendThread,NULL); delete(this->recvbuff); for (i=this->clients_info->getNext(-1);i!=-1;i=this->clients_info->getNext(i)) { KMemFree((*this->clients_info)[i]); } delete (this->clients_info); if (this->fd!=0) MyCloseSocket(this->fd); // if (this->localGame==0) // this->clean(); } }