/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Christoph Renner, David Hasenfratz co-programmer: */ /* this is for debug output. It just says, that all calls to PRINT() belong to the DEBUG_MODULE_NETWORK module For more information refere to https://www.orxonox.net/cgi-bin/trac.cgi/wiki/DebugOutput */ #define DEBUG_MODULE_NETWORK #include "converter.h" /* include your own header */ #include "network_socket.h" /* header for debug output */ #include "debug.h" /** * Default constructor */ NetworkSocket::NetworkSocket() { this->init(); } /** * Constructor to connect directly */ NetworkSocket::NetworkSocket(IPaddress ip) { this->init(); connectToServer(ip); } NetworkSocket::NetworkSocket( TCPsocket sock ) { this->init(); this->tcpSocket = sock; readThread = SDL_CreateThread(thread_read, (void*)this); writeThread = SDL_CreateThread(thread_write, (void*)this); } void NetworkSocket::init() { /* set the class id for the base object */ this->setClassID(CL_NETWORK_SOCKET, "NetworkSocket"); tcpSocket = NULL; incomingBufferLength = 0; outgoingBufferLength = 0; readThread = NULL; writeThread = NULL; thread_write_running = false; thread_read_running = false; incomingBufferMutex = SDL_CreateMutex(); outgoingBufferMutex = SDL_CreateMutex(); socketMutex = SDL_CreateMutex(); terminateThread = false; /* Init SDL_net */ //NOTE: do we need to call SDLNet_Init for all instances? if(SDLNet_Init()==-1) { PRINTF(1)("SDLNet_Init: %s\n", SDLNet_GetError()); return; } else PRINTF(5)("SDL_net initialized\n"); PRINTF(0)("NetworkSocket created\n"); } /** * Default destructor * dont use this from outside: use destroy() instead!! */ NetworkSocket::~NetworkSocket( ) { this->terminateThread = true; /* Quit SDL_net */ // NOTE: what if other instances of NetworkSocket running? SDLNet_Quit(); PRINTF(5)("SDL_net shutdown\n"); SDL_DestroyMutex(incomingBufferMutex); SDL_DestroyMutex(outgoingBufferMutex); SDL_DestroyMutex(socketMutex); SDL_DestroyMutex(threadTerminationMutex); } /** * This function establishes a TCP/UDP connection to a given server (function argument). * It is called by the NetworkStream. It creates a TCP/UDP socket for the connection. * @param ip */ void NetworkSocket::connectToServer(IPaddress ip) { //check if not already connected or listening if (tcpSocket) { PRINTF(1)("NetworkSocket::listen: tcpSocket!=NULL! maybe you already called listen or connectToServer or did not call disconnectServer()!"); } /* Connect to the host and port contained in ip using a TCP connection. */ tcpSocket = SDLNet_TCP_Open(&ip); if(!tcpSocket) { PRINTF(1)("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); return; } readThread = SDL_CreateThread(thread_read, (void*)this); writeThread = SDL_CreateThread(thread_write, (void*)this); } /** * DTears down a TCP/UDP connection. */ void NetworkSocket::disconnectServer( ) { terminateThread = true; /* Close the connection */ SDL_mutexP(socketMutex); SDLNet_TCP_Close(tcpSocket); tcpSocket = NULL; SDL_mutexV(socketMutex); } /** * This function writes some bytes (data) to the network connection (if the connection is already * estabilhed) otherwise it just does nothing (silently discarding the data). And writes some * warnings * @param data: pointer to the data to send * @param length: n bytes to send * @return the number successfully written bytes */ int NetworkSocket::writeBytes(byte * data, int length) { PRINTF(5)("NetworkSocket::writeBytes()\n"); #ifdef _USE_OUTGOING_BUFFER #define min(a,b) (a 0); return 0; } SDL_mutexP(outgoingBufferMutex); memcpy(outgoingBuffer + outgoingBufferLength, data, nbytes); outgoingBufferLength += nbytes; SDL_mutexV(outgoingBufferMutex); return nbytes; #else SDL_mutexP(socketMutex); if (!tcpSocket || data==NULL) return 0; int res = SDLNet_TCP_Send(tcpSocket, data, length); SDL_mutexV(socketMutex); if (resincomingBufferLength, length); if (incomingBufferLength >= length) return readBytes(data, length); else return 0; } /** * used to create a thread to read from socket * @param data: pointer to NetworkSocket */ int NetworkSocket::thread_read( void * data ) { int nbytesread = 0; int nbytestoread = 0; char buffer[_LOCAL_BUFFER_SIZE]; NetworkSocket * self = (NetworkSocket*)data; self->thread_read_running = true; while (!self->terminateThread) { #define min(a,b) (aincomingBufferLength, _LOCAL_BUFFER_SIZE); #undef min //if buffer is full if (nbytestoread<=0 || !self->tcpSocket) { SDL_Delay(_MSECONDS_SLEEP_FULL_BUFFER); continue; } nbytesread = SDLNet_TCP_Recv(self->tcpSocket, buffer, nbytestoread); SDL_mutexP(self->incomingBufferMutex); if (nbytesread<=0) { if (nbytesread<0) printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); SDL_mutexP(self->socketMutex); SDLNet_TCP_Close(self->tcpSocket); self->tcpSocket = NULL; SDL_mutexV(self->socketMutex); SDL_mutexV(self->incomingBufferMutex); continue; } //printf("thread_read: nbytesread=%d\n", nbytesread); memcpy(self->incomingBuffer+self->incomingBufferLength, buffer, nbytesread); self->incomingBufferLength += nbytesread; SDL_mutexV(self->incomingBufferMutex); } SDL_mutexP(self->threadTerminationMutex); self->thread_read_running = false; if ( !self->thread_write_running ) { //delete self; SDL_mutexV(self->threadTerminationMutex); } else { SDL_mutexV(self->threadTerminationMutex); } #ifdef DONTEXITTHREADS while ( true ) { SDL_Delay(1000); } #endif PRINTF(0)("QUIT READ THREAD\n"); return 0; } int NetworkSocket::thread_write( void * data ) { int nbyteswrite = 0; int nbytestowrite = 0; char buffer[_LOCAL_BUFFER_SIZE]; NetworkSocket * self = (NetworkSocket*)data; self->thread_write_running = true; while (!self->terminateThread) { #define min(a,b) (aoutgoingBufferLength, _LOCAL_BUFFER_SIZE); #undef min // printf("thread_write nbytes=%d listening=%d\n", nbytestowrite, (int)self->_isListening); //if buffer is full if (nbytestowrite<=0 || !self->tcpSocket) { SDL_Delay(_MSECONDS_SLEEP_EMPTY_BUFFER); continue; } SDL_mutexP(self->outgoingBufferMutex); //printf("a\n"); memcpy(buffer, self->outgoingBuffer, nbytestowrite); self->outgoingBufferLength -= nbytestowrite; memmove(self->outgoingBuffer, self->outgoingBuffer+nbytestowrite, self->outgoingBufferLength); SDL_mutexV(self->outgoingBufferMutex); nbyteswrite = SDLNet_TCP_Send(self->tcpSocket, buffer, nbytestowrite); if (nbyteswrite<=0) { printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); SDL_mutexP(self->socketMutex); SDLNet_TCP_Close(self->tcpSocket); self->tcpSocket = NULL; SDL_mutexV(self->socketMutex); continue; } } SDL_mutexP(self->threadTerminationMutex); self->thread_write_running = false; if ( !self->thread_read_running ) { //delete self; SDL_mutexV(self->threadTerminationMutex); } else { SDL_mutexV(self->threadTerminationMutex); } #ifdef DONTEXITTHREADS while ( true ) { SDL_Delay(1000); } #endif PRINTF(0)("QUIT WRITE THREAD\n"); return 0; } bool NetworkSocket::writePacket( byte * data, int length ) { PRINTF(5)("NetworkSocket::writePacket() size=%d\n", length); if ( length > 1024 ) PRINTF(2)("WARNING SENDING BIG PACKET SIZE = %d\n", length); byte blen[INTSIZE]; Converter::intToByteArray( length, blen, INTSIZE ); writeBytes(blen, INTSIZE); writeBytes(data, length); } int NetworkSocket::readPacket( byte * data, int maxLength ) { PRINTF(5)("NetworkSocket::readPacket()\n"); if (incomingBufferLengthmaxLength) { PRINTF(1)("Buffersize is too small (%d) for packet (%d)\n", maxLength, blen); assert(false); return 0; } if (blen>incomingBufferLength) { return 0; } byte t[INTSIZE]; readBytes(t, INTSIZE); int res = readBytes(data, blen); if (res!=blen) return -1; else return blen; }