/* 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 "tcp_socket.h" /* header for debug output */ #include "debug.h" ObjectListDefinition(TcpSocket); /** * Default constructor */ TcpSocket::TcpSocket() { this->init(); } /** * Constructor to connect directly */ TcpSocket::TcpSocket( std::string host, int port ) { this->init(); connectToServer( host, port); } TcpSocket::TcpSocket( TCPsocket sock ) { this->init(); this->tcpSocket = sock; readThread = SDL_CreateThread(thread_read, (void*)this); writeThread = SDL_CreateThread(thread_write, (void*)this); } void TcpSocket::init() { /* set the class id for the base object */ this->registerObject(this, TcpSocket::_objectList); 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)("TcpSocket created\n"); } /** * Default destructor * dont use this from outside: use destroy() instead!! */ TcpSocket::~TcpSocket( ) { this->terminateThread = true; /* Quit SDL_net */ // NOTE: what if other instances of TcpSocket 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 TcpSocket::connectToServer( std::string host, int port) { IPaddress ip; bOk = true; if ( SDLNet_ResolveHost( &ip, host.c_str(), port ) != 0 ) { PRINTF(1)("could not resolve host %s\n", host.c_str() ); bOk = false; return; } //check if not already connected or listening if (tcpSocket) { PRINTF(1)("TcpSocket::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 TcpSocket::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 TcpSocket::writeBytes(byte * data, int length) { PRINTF(5)("TcpSocket::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 TcpSocket */ int TcpSocket::thread_read( void * data ) { int nbytesread = 0; int nbytestoread = 0; char buffer[_LOCAL_BUFFER_SIZE]; TcpSocket * self = (TcpSocket*)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 TcpSocket::thread_write( void * data ) { int nbyteswrite = 0; int nbytestowrite = 0; char buffer[_LOCAL_BUFFER_SIZE]; TcpSocket * self = (TcpSocket*)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 TcpSocket::writePacket( byte * data, int length ) { PRINTF(5)("TcpSocket::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 TcpSocket::readPacket( byte * data, int maxLength ) { PRINTF(5)("TcpSocket::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; }