/* 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 co-programmer: */ #include "udp_socket.h" #include "udp_server_socket.h" #include "debug.h" void UdpSocket::init( ) { //TODO setClassId this->serverSocket = NULL; this->socket = NULL; this->packet = NULL; this->randomByte = 0; } /** * constructor - connects to give host * @param host host * @param port port */ UdpSocket::UdpSocket( std::string host, int port ) { init(); this->packet = SDLNet_AllocPacket( UDP_PACKET_SIZE ); assert( this->packet ); memset( packet->data, 0, UDP_PACKET_SIZE ); PRINTF(0)("PACKET DATA: %x\n", packet->data); this->connectToServer( host, port ); } /** * default constructor. use this one if you want to call connecttoServer */ UdpSocket::UdpSocket( ) { this->init(); this->packet = SDLNet_AllocPacket( UDP_PACKET_SIZE ); if ( !packet ) { PRINTF(1)("SDLNet_AllocPacket: %s\n", SDLNet_GetError()); assert( false ); bOk = false; } } /** * constructor. used by UdpServerSocket * @param serverSocket pointer to serverSocket * @param ip client's ip address * @param userId userid used by serverSocket */ UdpSocket::UdpSocket( UdpServerSocket * serverSocket, IPaddress ip, int userId, byte randomByte ) { this->init(); this->ip = ip; this->serverSocket = serverSocket; this->userId = userId; this->randomByte = randomByte; } /** * destructor */ UdpSocket::~UdpSocket( ) { this->disconnectServer(); if ( serverSocket ) serverSocket->removeUser( userId ); if ( this->packet ) SDLNet_FreePacket( this->packet ); if ( socket ) SDLNet_UDP_Close( socket ); } /** * connect to server * @param host host name * @param port port number */ void UdpSocket::connectToServer( std::string host, int port ) { assert( serverSocket == NULL ); this->randomByte = generateNewRandomByte(); PRINTF(0)("connect to server %s on port %d\n", host.c_str(), port); if ( SDLNet_ResolveHost( &this->ip, host.c_str(), port ) != 0 ) { PRINTF(1)("SDLNet_ResolveHost: %s\n", SDLNet_GetError() ); bOk = false; return; } socket = SDLNet_UDP_Open(0); if ( !socket ) { PRINTF(1)("SDLNet_UDP_Open: %s\n", SDLNet_GetError() ); bOk = false; return; } int channel = SDLNet_UDP_Bind(socket, 1, &this->ip); if ( channel == -1 ) { PRINTF(1)("SDLNet_UDP_Bind: %s\n", SDLNet_GetError()); bOk = false; return; } } /** * disconnect from server */ void UdpSocket::disconnectServer( ) { PRINTF(0)("disconnect\n"); byte cmd = this->randomByte | UDPCMD_DISCONNECT; writeRawPacket( &cmd, 1 ); SDLNet_UDP_Unbind( socket, -1 ); SDLNet_UDP_Close( socket ); bOk = false; socket = NULL; this->ip.host = 0; this->ip.port = 0; } /** * reconnects to * @param host new server address * @param port new port number * * this terminates the current connection and starts a new connection to the new server */ void UdpSocket::reconnectToServer( std::string host, int port) { // first disconnect the old server this->disconnectServer(); // now connect to the new this->connectToServer( host, port); } /** * reconnects to * @param host new server address * @param port new port number * * this terminates the current connection and starts a new connection to the new server */ void UdpSocket::reconnectToServerSoft( std::string host, int port) {} /** * send one packet to other host * @param data pointer to data which will be sent * @param length length of data * @return true on success */ bool UdpSocket::writePacket( byte * data, int length ) { byte * buf = new byte[length+1]; if ( length > 0 ) memcpy( buf+1, data, length ); buf[0] = this->randomByte; return writeRawPacket( buf, length+1 ); } /** * recieve one packet from another host * @param data pointer to buffer to copy data into * @param maxLength maximal length of buffer * @return less than 0 on error, number bytes read else */ int UdpSocket::readPacket( byte * data, int maxLength ) { assert( maxLength <= UDP_PACKET_SIZE ); if ( serverSocket ) { NetworkPacket networkPacket = serverSocket->getPacket( this->userId ); byte udpCmd = 0; if ( networkPacket.length > 0 ) { assert( maxLength > networkPacket.length ); memcpy( data, networkPacket.data+1, networkPacket.length-1 ); udpCmd = networkPacket.data[0]; } else return 0; if ( !checkRandomByte( networkPacket.data[0] ) ) return 0; if ( networkPacket.data ) { free( networkPacket.data ); networkPacket.data = NULL; } if ( !checkUdpCmd( udpCmd ) ) return 0; return networkPacket.length-1; } else { int numrecv = SDLNet_UDP_Recv( socket, packet); byte udpCmd = 0; if ( numrecv > 0) { assert( packet->len <= maxLength ); if ( packet->len > 0 ) memcpy( data, packet->data+1, packet->len-1 ); else return 0; if ( !checkRandomByte( packet->data[0] ) ) return 0; if ( !checkUdpCmd( udpCmd ) ) return 0; return packet->len-1; } else if ( numrecv < 0 ) { PRINTF(1)("SDLNet_UDP_Recv: %s\n", SDLNet_GetError()); bOk = false; return -1; } else { return 0; } } return 0; } bool UdpSocket::writeRawPacket( byte * data, int length ) { if ( serverSocket ) { NetworkPacket networkPacket; networkPacket.length = length; networkPacket.data = data; if ( !serverSocket->sendPacket( networkPacket, this->userId ) ) { bOk = false; return false; } else return true; } else { assert( length <= packet->maxlen ); memcpy( packet->data, data, length ); packet->len = length; if ( socket && SDLNet_UDP_Send( socket, 1, packet) == 0 ) { PRINTF(1)("SDLNet_UDP_Send: %s\n", SDLNet_GetError()); bOk = false; return false; } return true; } } bool UdpSocket::checkUdpCmd( byte udpCmd ) { if ( udpCmd & UDPCMD_DISCONNECT ) { this->disconnectServer(); PRINTF(0)("received disconnect byte\n"); return false; } if ( !this->serverSocket && ( udpCmd & UDPCMD_INVALIDRNDBYTE ) ) { PRINTF(0)("received invlid random number byte\n"); byte cmd = this->randomByte | UDPCMD_DISCONNECT; writeRawPacket( &cmd, 1 ); this->randomByte = generateNewRandomByte(); return false; } return true; } byte UdpSocket::generateNewRandomByte( ) { srand( SDL_GetTicks() ); byte res = ( rand() & 0xFC ); PRINTF(0)("generated random byte: %x\n", res); return res; } bool UdpSocket::checkRandomByte( byte rndByte ) { if ( ( rndByte & 0xFC ) == this->randomByte ) { return true; } else { PRINTF(2)("wrong random byte: %x\n", ( rndByte & 0xFC )); return false; } }