/* $Id: tcpconnection.cpp,v 1.10 2005/12/10 15:54:36 chfreund Exp $ */ // #include #include #include #include #include #include #ifdef WIN32 #include #endif #include "global.hpp" #include "message.hpp" #include "tcpconnection.hpp" #include "servercommunicator.hpp" #define CHUNK_SIZE 10240 int TCPConnection::sendMessage( TCPsocket socket, Message* message, bool compressData, TCPProgressListener* listener ) { // buffer with actual data to send and its size Uint8* messageBuffer = 0; Uint32 messageSize = 0; if ( compressData ) { // set compression flag message->compressed = true; // calculate uncompressed data size header message->uncompressedDataSize = message->getDataSize(); // serialize data into temporary buffer Uint8* tmpBuffer = new Uint8[message->uncompressedDataSize]; ASSERT( tmpBuffer, "TCPConnection::sendMessage: memory allocation failed\n" ); Uint8* bufferPointer = tmpBuffer; message->serializeData( bufferPointer ); // calculate maximal size for buffer with compressed data // and allocate buffer Uint32 compressBufferSize = static_cast( 1.01 * ( message->uncompressedDataSize + 12.5 ) ); messageBuffer = new Uint8[Message::getStaticSerializeBufferSize() + compressBufferSize]; ASSERT( messageBuffer, "TCPConnection::sendMessage: memory allocation failed\n" ); // compress data LOG( 2 ) INFO( "TCPConnection::sendMessage: compressing data\n" ); uLongf zlibBufferSize = compressBufferSize; ASSERT( compress( messageBuffer + Message::getStaticSerializeBufferSize(), &zlibBufferSize, tmpBuffer, message->uncompressedDataSize ) == Z_OK, "TCPConnection::sendMessage: error compressing data\n" ); compressBufferSize = static_cast( zlibBufferSize ); // delete temporary buffer delete[] tmpBuffer; // set data size and serialize header into buffer message->dataSize = compressBufferSize; bufferPointer = messageBuffer; message->serializeHeader( bufferPointer ); // set overall message size messageSize = Message::getStaticSerializeBufferSize() + compressBufferSize; } else { // calculate data size header message->dataSize = message->getDataSize(); // create send buffer messageSize = message->getSerializeBufferSize(); messageBuffer = NEW Uint8[messageSize]; // serialize message Uint8* bufferPointer = messageBuffer; message->serialize( bufferPointer ); } // send packet Uint32 dataSent = 0; while ( dataSent < messageSize ) { int chunkSize = ( messageSize - dataSent ) < CHUNK_SIZE ? messageSize - dataSent : CHUNK_SIZE; int bytesSent = SDLNet_TCP_Send( socket, static_cast( messageBuffer + dataSent ), chunkSize ); if ( bytesSent < chunkSize && errno != EAGAIN ) { LOG( 2 ) INFO( "TCPConnection::sendMessage: error sending message (%s)\n", SDLNet_GetError() ); delete[] messageBuffer; return -1; } dataSent += bytesSent; // notify if listener is given if ( listener ) listener->sendProgress( messageSize, dataSent ); DBG( 5 ) INFO( "TCPConnection::sendMessage: %d of %d bytes sent\n", dataSent, messageSize ); } // free message buffer delete[] messageBuffer; // error check if ( dataSent < messageSize ) { LOG( 2 ) INFO( "TCPConnection::sendMessage: error sending message (%s)\n", SDLNet_GetError() ); return -1; } return 0; } int TCPConnection::recvMessage( TCPsocket socket, Message*& message, TCPProgressListener* listener ) { // initialize result with null pointer message = 0; // create packet for storing header data Message headerMessage; // create receive buffer Uint32 headerSize = Message::getStaticSerializeBufferSize(); Uint8* headerBuffer = NEW Uint8[headerSize]; // receive header data Uint32 dataReceived = 0; while ( dataReceived < headerSize ) { int bytesReceived = SDLNet_TCP_Recv( socket, headerBuffer + dataReceived, headerSize - dataReceived ); if ( bytesReceived <= 0 ) { if ( errno == EAGAIN ) { bytesReceived = 0; } else { LOG( 2 ) INFO( "TCPConnection::recvMessage: error receiving message header (%s)\n", SDLNet_GetError() ); delete[] headerBuffer; return -1; } } dataReceived += bytesReceived; DBG( 5 ) INFO( "TCPConnection::recvMessage: %d of %d bytes received\n", dataReceived, headerSize ); } // deserialize header data Uint8* bufferPointer = headerBuffer; headerMessage.deserialize( bufferPointer ); delete[] headerBuffer; // distinguish packet types switch ( headerMessage.type ) { case Message::UNKNOWN: // nothing to do here break; case Message::REQUEST: message = NEW RequestMessage( headerMessage ); break; case Message::INFO: message = NEW InfoMessage( headerMessage ); break; case Message::CONNECT: message = NEW ConnectMessage( headerMessage ); break; case Message::ADD_PLAYER: message = NEW AddPlayerMessage( headerMessage ); break; case Message::REMOVE_PLAYER: message = NEW RemovePlayerMessage( headerMessage ); break; case Message::WORLD: message = NEW WorldMessage( headerMessage ); break; case Message::EVENT: message = NEW EventMessage( headerMessage ); break; case Message::ECHO: message = NEW EchoMessage( headerMessage ); break; case Message::CHAT: message = NEW ChatMessage( headerMessage ); break; case Message::SYNC_PROGRESS: message = NEW SyncProgressMessage( headerMessage ); break; case Message::QUIT: message = NEW QuitMessage( headerMessage ); break; default: ASSERT( false, "TCPConnection::recvMessage: don't know how to receive message of type %d\n", headerMessage.type ); break; } // receive content data Uint8* dataBuffer = NEW Uint8[headerMessage.dataSize]; ASSERT( dataBuffer, "TCPConnection::recvMessage: memory allocation failed\n" ); dataReceived = 0; while ( dataReceived < headerMessage.dataSize ) { int bytesReceived = SDLNet_TCP_Recv( socket, dataBuffer + dataReceived, headerMessage.dataSize - dataReceived ); if ( bytesReceived <= 0 ) { if ( errno == EAGAIN ) { bytesReceived = 0; } else { LOG( 2 ) INFO( "TCPConnection::recvMessage: error receiving message data (%s)\n", SDLNet_GetError() ); delete[] dataBuffer; delete message; return -1; } } dataReceived += bytesReceived; // notify if listener is given if ( listener ) listener->recvProgress( headerMessage.dataSize, dataReceived ); DBG( 5 ) INFO( "TCPConnection::recvMessage: %d of %d bytes received\n", dataReceived, headerMessage.dataSize ); } if ( message->compressed ) { // create temporary buffer for the uncompressed data Uint8* tmpBuffer = new Uint8[message->uncompressedDataSize]; ASSERT( tmpBuffer, "TCPConnection::recvMessage: memory allocation failed\n" ); // uncompress data LOG( 2 ) INFO( "TCPConnection::recvMessage: uncompressing data\n" ); uLongf zlibDataSize = message->uncompressedDataSize; ASSERT( uncompress( tmpBuffer, &zlibDataSize, dataBuffer, message->dataSize ) == Z_OK, "TCPConnection::recvMessage: error uncompressing data\n" ); // deserialize uncompressed data bufferPointer = tmpBuffer; message->deserializeData( bufferPointer ); // delete temporary buffer delete[] tmpBuffer; } else { // deserialize content data bufferPointer = dataBuffer; message->deserializeData( bufferPointer ); } delete[] dataBuffer; return 0; }