474 lines
12 KiB
C++
474 lines
12 KiB
C++
#include "Connection.h"
|
|
|
|
#include "minorGems/util/SettingsManager.h"
|
|
#include "minorGems/util/TranslationManager.h"
|
|
#include "minorGems/network/SocketClient.h"
|
|
|
|
#include "minorGems/util/log/AppLog.h"
|
|
|
|
|
|
extern int autoJoinPort;
|
|
|
|
|
|
int Connection::getPort() {
|
|
|
|
if( autoJoinPort != -1 ) {
|
|
return autoJoinPort;
|
|
}
|
|
|
|
char portFound;
|
|
int port = SettingsManager::getIntSetting( "port",
|
|
&portFound );
|
|
if( !portFound ) {
|
|
port = 7778;
|
|
}
|
|
return port;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connection::Connection()
|
|
: mSock( NULL ),
|
|
mError( false ),
|
|
mErrorString( NULL ) {
|
|
|
|
message empty = {NULL, -1, 0, 0, 0};
|
|
mNextOutgoingMessage = empty;
|
|
mNextIncomingMessage = empty;
|
|
|
|
|
|
|
|
mServer = new SocketServer( getPort(), 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make a connection to an established server
|
|
Connection::Connection( char *inAddress )
|
|
: mServer( NULL ),
|
|
mError( false ),
|
|
mErrorString( NULL ) {
|
|
|
|
message empty = {NULL, -1, 0, 0, 0};
|
|
mNextOutgoingMessage = empty;
|
|
mNextIncomingMessage = empty;
|
|
|
|
|
|
HostAddress h( stringDuplicate( inAddress ), getPort() );
|
|
|
|
// non-blocking
|
|
char timedOut;
|
|
mSock = SocketClient::connectToServer( &h, 0, &timedOut );
|
|
|
|
if( mSock == NULL ) {
|
|
setError( "err_failedConnect" );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Connection::~Connection() {
|
|
if( mSock != NULL ) {
|
|
delete mSock;
|
|
}
|
|
if( mServer != NULL ) {
|
|
delete mServer;
|
|
}
|
|
if( mErrorString != NULL ) {
|
|
delete [] mErrorString;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
unsigned char computeBitChecksum( unsigned char inMessage[5] ) {
|
|
int sum = 0;
|
|
|
|
for( int i=0; i<5; i++ ) {
|
|
for( int b=0; b<8; b++ ) {
|
|
sum += (int)( (inMessage[i] >> b) & 0x01 );
|
|
}
|
|
}
|
|
return (unsigned char)sum;
|
|
}
|
|
|
|
|
|
|
|
|
|
char Connection::isConnected() {
|
|
if( mSock != NULL ) {
|
|
return mSock->isConnected() == 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
char Connection::step() {
|
|
|
|
if( mSock == NULL && mServer != NULL ) {
|
|
char timeout;
|
|
mSock = mServer->acceptConnection( 0, &timeout );
|
|
|
|
if( mSock == NULL && !timeout ) {
|
|
setError( "err_failedAccept" );
|
|
}
|
|
return false;
|
|
}
|
|
else if( mSock != NULL && mServer == NULL ) {
|
|
if( mSock->isConnected() == 0 ) {
|
|
// working
|
|
return true;
|
|
}
|
|
else if( mSock->isConnected() == -1 ) {
|
|
setError( "err_failedConnect" );
|
|
return false;
|
|
}
|
|
}
|
|
else if( mSock == NULL ) {
|
|
// maybe failed connect right in constructor
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// got past here, we have a connected socket
|
|
|
|
|
|
|
|
if( mNextOutgoingMessage.length != -1 ) {
|
|
// try sending more of it
|
|
|
|
|
|
int i = mNextOutgoingMessage.nextToProcessIndex;
|
|
|
|
/*
|
|
// header packed right into send body
|
|
|
|
if( i == 0 && mNextOutgoingMessage.headerBytesProcessed != 5 ) {
|
|
// compose and send 5 header bytes
|
|
|
|
int numBytes = mNextOutgoingMessage.length;
|
|
|
|
unsigned char sendData[5];
|
|
|
|
sendData[0] = mNextOutgoingMessage.channel;
|
|
sendData[1] = ( numBytes >> 24 ) & 0xFF;
|
|
sendData[2] = ( numBytes >> 16 ) & 0xFF;
|
|
sendData[3] = ( numBytes >> 8 ) & 0xFF;
|
|
sendData[4] = ( numBytes ) & 0xFF;
|
|
|
|
int j = mNextOutgoingMessage.headerBytesProcessed;
|
|
|
|
// non-blocking, send only the remaining bytes of header
|
|
int numSent = mSock->send( &( sendData[j] ), 5 - j, false );
|
|
|
|
if( numSent == -1 ) {
|
|
setError( "err_failedSend" );
|
|
}
|
|
else if( numSent > 0 ) {
|
|
mNextOutgoingMessage.headerBytesProcessed += numSent;
|
|
}
|
|
}
|
|
*/
|
|
//else {
|
|
if( true ) {
|
|
|
|
// send data
|
|
|
|
// non-blocking
|
|
int numSent = mSock->send( &( mNextOutgoingMessage.data[ i ] ),
|
|
mNextOutgoingMessage.length - i,
|
|
false,
|
|
false ); // realtime, no buffering
|
|
if( numSent < 0 ) {
|
|
if( numSent == -1 ) {
|
|
setError( "err_failedSend" );
|
|
}
|
|
else {
|
|
// would block
|
|
// no more work to do right now
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
i += numSent;
|
|
AppLog::getLog()->logPrintf( Log::DETAIL_LEVEL,
|
|
"Socket sent %d/%d bytes",
|
|
numSent,
|
|
mNextOutgoingMessage.length );
|
|
|
|
if( i == mNextOutgoingMessage.length ) {
|
|
// done with it!
|
|
delete [] mNextOutgoingMessage.data;
|
|
mNextOutgoingMessage.data = NULL;
|
|
mNextOutgoingMessage.length = -1;
|
|
mNextOutgoingMessage.nextToProcessIndex = 0;
|
|
mNextOutgoingMessage.headerBytesProcessed = 0;
|
|
}
|
|
else {
|
|
// more to go
|
|
mNextOutgoingMessage.nextToProcessIndex = i;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
// is there another in our queue that we can start sending?
|
|
if( mQueuedOutgoing.size() > 0 ) {
|
|
// grab next
|
|
message *m = mQueuedOutgoing.getElement( 0 );
|
|
|
|
mNextOutgoingMessage = *m;
|
|
|
|
mQueuedOutgoing.deleteElement( 0 );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if( mNextIncomingMessage.length != -1 &&
|
|
mNextIncomingMessage.headerBytesProcessed == 6 ) {
|
|
// try receiving more of it
|
|
|
|
|
|
int i = mNextIncomingMessage.nextToProcessIndex;
|
|
|
|
// non-blocking
|
|
int numReceived = mSock->receive( &( mNextIncomingMessage.data[ i ] ),
|
|
mNextIncomingMessage.length - i,
|
|
0 );
|
|
|
|
if( numReceived < 0 ) {
|
|
if( numReceived == -1 ) {
|
|
setError( "err_failedReceive" );
|
|
}
|
|
else {
|
|
// timeout
|
|
// no more work right now
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
i += numReceived;
|
|
|
|
if( i == mNextIncomingMessage.length ) {
|
|
|
|
// push it onto queue
|
|
mQueuedIncoming.push_back( mNextIncomingMessage );
|
|
|
|
// clear it to make room for next one
|
|
mNextIncomingMessage.data = NULL;
|
|
mNextIncomingMessage.length = -1;
|
|
mNextIncomingMessage.nextToProcessIndex = 0;
|
|
mNextIncomingMessage.headerBytesProcessed = 0;
|
|
}
|
|
else {
|
|
// more to go
|
|
mNextIncomingMessage.nextToProcessIndex = i;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
// try to receive header of next incoming
|
|
|
|
|
|
// fill buffer with partial stuff already stored in next message
|
|
int numBytes = mNextIncomingMessage.length;
|
|
|
|
unsigned char recvData[6];
|
|
|
|
recvData[0] = mNextIncomingMessage.channel;
|
|
recvData[1] = ( numBytes >> 24 ) & 0xFF;
|
|
recvData[2] = ( numBytes >> 16 ) & 0xFF;
|
|
recvData[3] = ( numBytes >> 8 ) & 0xFF;
|
|
recvData[4] = ( numBytes ) & 0xFF;
|
|
recvData[5] = mNextIncomingMessage.bitCheckSum;
|
|
|
|
// fetch remaining part
|
|
int j = mNextIncomingMessage.headerBytesProcessed;
|
|
|
|
|
|
// non-blocking, receive only the remaining bytes of header
|
|
int numReceived = mSock->receive( &( recvData[j] ), 6 - j, 0 );
|
|
|
|
|
|
|
|
if( numReceived == -1 ) {
|
|
setError( "err_failedReceive" );
|
|
}
|
|
else if( numReceived > 0 ) {
|
|
mNextIncomingMessage.headerBytesProcessed += numReceived;
|
|
|
|
// stick back into length field
|
|
|
|
mNextIncomingMessage.channel = recvData[0];
|
|
|
|
mNextIncomingMessage.length =
|
|
recvData[1] << 24 |
|
|
recvData[2] << 16 |
|
|
recvData[3] << 8 |
|
|
recvData[4];
|
|
mNextIncomingMessage.bitCheckSum = recvData[5];
|
|
|
|
|
|
if( mNextIncomingMessage.headerBytesProcessed == 6 ) {
|
|
// done!
|
|
|
|
// verify checksum
|
|
if( computeBitChecksum( recvData ) !=
|
|
mNextIncomingMessage.bitCheckSum ) {
|
|
|
|
AppLog::error( "Error: Message checksum failed" );
|
|
|
|
// clear it to make room for next one,
|
|
// to ensure that processing of this one stops
|
|
mNextIncomingMessage.data = NULL;
|
|
mNextIncomingMessage.length = -1;
|
|
mNextIncomingMessage.nextToProcessIndex = 0;
|
|
mNextIncomingMessage.headerBytesProcessed = 0;
|
|
|
|
setError( "err_receiveCorrupted" );
|
|
}
|
|
else {
|
|
// checksum correct
|
|
|
|
// make a data buffer
|
|
mNextIncomingMessage.data =
|
|
new unsigned char[ mNextIncomingMessage.length ];
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
void Connection::sendMessage( unsigned char *inData, int inLength,
|
|
unsigned char inChannel ) {
|
|
|
|
// pack the header right into the message to avoid two consecutive sends
|
|
// (which interacts poorly with the Nagle algorithm)
|
|
message m = { new unsigned char[ inLength + 6 ],
|
|
inLength + 6, 0, 0, inChannel,
|
|
0 }; // empty checksum
|
|
|
|
// first 6 bytes of data are header
|
|
m.data[0] = m.channel;
|
|
m.data[1] = ( inLength >> 24 ) & 0xFF;
|
|
m.data[2] = ( inLength >> 16 ) & 0xFF;
|
|
m.data[3] = ( inLength >> 8 ) & 0xFF;
|
|
m.data[4] = ( inLength ) & 0xFF;
|
|
m.data[4] = ( inLength ) & 0xFF;
|
|
|
|
m.bitCheckSum = computeBitChecksum( m.data );
|
|
m.data[5] = m.bitCheckSum;
|
|
|
|
// remainder are data payload
|
|
memcpy( &( m.data[6] ), inData, inLength );
|
|
|
|
mQueuedOutgoing.push_back( m );
|
|
}
|
|
|
|
|
|
|
|
unsigned char *Connection::receiveMessage( int *outLength,
|
|
unsigned char inChannel ) {
|
|
|
|
int queueSize = mQueuedIncoming.size();
|
|
|
|
if( queueSize > 0 ) {
|
|
|
|
// skip messages that don't match our channel
|
|
int i = 0;
|
|
char chanMatch = false;
|
|
while( !chanMatch && i<queueSize ) {
|
|
message *m = mQueuedIncoming.getElement( i );
|
|
|
|
if( m->channel == inChannel ) {
|
|
chanMatch = true;
|
|
}
|
|
else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if( chanMatch ) {
|
|
|
|
message *m = mQueuedIncoming.getElement( i );
|
|
|
|
*outLength = m->length;
|
|
|
|
unsigned char *returnValue = m->data;
|
|
|
|
mQueuedIncoming.deleteElement( i );
|
|
|
|
return returnValue;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char Connection::isError() {
|
|
return mError;
|
|
}
|
|
|
|
|
|
|
|
void Connection::clearError() {
|
|
if( mErrorString != NULL ) {
|
|
delete [] mErrorString;
|
|
}
|
|
mErrorString = NULL;
|
|
mError = false;
|
|
}
|
|
|
|
|
|
|
|
char *Connection::getErrorString() {
|
|
return mErrorString;
|
|
}
|
|
|
|
|
|
|
|
void Connection::setError( const char *inErrorTranslationKey ) {
|
|
mError = true;
|
|
|
|
if( mErrorString != NULL ) {
|
|
delete [] mErrorString;
|
|
}
|
|
|
|
mErrorString = stringDuplicate( TranslationManager::translate(
|
|
(char*)inErrorTranslationKey ) );
|
|
}
|
|
|
|
|