/* 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 your own header */ #include "network_socket.h" /* header for debug output */ #include "debug.h" /** * Default constructor */ NetworkSocket::NetworkSocket() { /* set the class id for the base object */ this->setClassID(CL_NETWORK_SOCKET, "NetworkSocket"); tcpSocket = NULL; incomingBufferLength = 0; outgoingBufferLength = 0; 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"); } /** * Constructor to connect directly */ NetworkSocket::NetworkSocket(IPaddress ip) { NetworkSocket(); connectToServer(ip); } /** * Default destructor */ NetworkSocket::~ NetworkSocket( ) { /* Quit SDL_net */ // NOTE: what if other instances of NetworkSocket running? SDLNet_Quit(); PRINTF(5)("SDL_net shutdown\n"); _isListening = false; SDL_DestroyMutex(incomingBufferMutex); SDL_DestroyMutex(outgoingBufferMutex); SDL_DestroyMutex(socketMutex); } /** * 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; } _isListening = false; SDL_CreateThread(thread_read, (void*)this); SDL_CreateThread(thread_write, (void*)this); } /** * Tells the NetworkSocket to listen on a specific port for incoming connections. * NetworkSocket::writeBytes(...) will have no effect until there is a valuable connection. * @param port */ void NetworkSocket::listen(unsigned int port) { _isListening = true; //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()!\n"); _isListening = false; return; } IPaddress ip; if (SDLNet_ResolveHost(&ip, NULL, port)==-1) { PRINTF(1)("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); _isListening = false; return; } tcpSocket = SDLNet_TCP_Open(&ip); if (!tcpSocket) { PRINTF(1)("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); _isListening = false; return; } SDL_CreateThread(thread_listen, (void*)this); 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(0)("NetworkSocket::writeBytes()\n"); #ifdef _USE_OUTGOING_BUFFER //printf("length=%d, bufsize=%d\n", length, _OUTGOING_BUFFER_SIZE); // if (length>_OUTGOING_BUFFER_SIZE) // { // int res = 0; // int n = length / _OUTGOING_BUFFER_SIZE; // if (length % _OUTGOING_BUFFER_SIZE != 0) // n++; // // printf("n=%d\n", n); // SDL_Delay(500); // for (int i = 0; iincomingBufferLength, length); if (incomingBufferLength >= length) return readBytes(data, length); else return 0; } /** * used to create a thread to listen * will call thrad_read when established connection * @param data: pointer to NetwortSocket */ int NetworkSocket::thread_listen( void * data ) { NetworkSocket * self = (NetworkSocket*)data; self->_isListening = true; TCPsocket tempsocket; tempsocket = SDLNet_TCP_Accept(self->tcpSocket); while (!tempsocket && !self->terminateThread) tempsocket = SDLNet_TCP_Accept(self->tcpSocket); SDL_mutexP(self->socketMutex); SDLNet_TCP_Close(self->tcpSocket); self->tcpSocket = NULL; if (!tempsocket) { printf("SDLNet_TCP_Accept: %s\n", SDLNet_GetError()); //printf("SDLNet_TCP_Accept: %s\n", SDLNet_GetError()); SDL_mutexV(self->socketMutex); self->_isListening = false; return -1; } self->tcpSocket = tempsocket; SDL_mutexV(self->socketMutex); self->_isListening = false; return thread_read(data); } /** * 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; while (!self->terminateThread) { #define min(a,b) (aincomingBufferLength, _LOCAL_BUFFER_SIZE); #undef min //if buffer is full if (nbytestoread<=0 || self->_isListening) { SDL_Delay(_MSECONDS_SLEEP_FULL_BUFFER); continue; } nbytesread = SDLNet_TCP_Recv(self->tcpSocket, buffer, nbytestoread); SDL_mutexP(self->incomingBufferMutex); 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); return -1; } //printf("thread_read: nbytesread=%d\n", nbytesread); memcpy(self->incomingBuffer+self->incomingBufferLength, buffer, nbytesread); self->incomingBufferLength += nbytesread; SDL_mutexV(self->incomingBufferMutex); } return 0; } int NetworkSocket::thread_write( void * data ) { int nbyteswrite = 0; int nbytestowrite = 0; char buffer[_LOCAL_BUFFER_SIZE]; NetworkSocket * self = (NetworkSocket*)data; 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->_isListening) { 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); return -1; } } return 0; }