// We'll buffer 8K at a time for each user. // It's smart enough not to read more data than required for the message payload. // After that it would just start eating into the next header - so it stops. void OTClientConnection::ReadBytesIntoBuffer() { // At this point, the checksum has already validated. // Might as well get the PAYLOAD next. int err = 0; uint32_t nread = 0; const int nBufferSize = 8192; // todo no hardcoding. unsigned char szBuffer[8300]; // I made this a little bigger just for safety reasons. memset(szBuffer, 0, 8299); // just in case. //ultimately we want to read until m_Buffer.GetSize equals m_CMD.fields.size // In this function we'll read up to that or 8192, whichever is smaller. uint32_t lNumberOfBytesRemaining = m_CMD.fields.size - m_Buffer.GetSize(); uint32_t nNumberOfBytesToRead = ((lNumberOfBytesRemaining > nBufferSize) ? nBufferSize : lNumberOfBytesRemaining); // actually read the payload from the socket into the buffer. for (nread = 0; nread < nNumberOfBytesToRead; nread += err) { err = SFSocketRead(m_pSocket, szBuffer + nread, nNumberOfBytesToRead - nread); // if we don't read anything more, stop reading and move on #ifdef _WIN32 if (0 == err || SOCKET_ERROR == err) // 0 means disconnect. error means error. >0 means bytes read. #else if (err <= 0) #endif break; } // If we read anything, up to 4K, we add it to the m_Buffer. // This continues happening until m_Buffer.GetSize() == m_CMD.fields.size // and then other code reads the message from the buffer and processes it. if (nread) { OTData toAddData(szBuffer, nread); m_Buffer += toAddData; } }
// If a valid header is received, this function gets called. // The job of this function is to creae the message, read it, and add it to m_listIn. // ...and also, if there are unexpected bytes, to flush them in anticipation of the // next valid message. void OTClientConnection::ProcessMessage(u_header & theCMD) { bool bSuccess = false; OTMessage * pMsg = NULL; if ( theCMD.fields.type_id == CMD_TYPE_1 ) { fprintf(stderr, "Received a Type 1 Command...\n"); if( IsChecksumValid( theCMD.buf, OT_CMD_HEADER_SIZE ) ) { fprintf(stderr, "Checksum is valid! Processing payload.\n"); pMsg = new OTMessage; if (ProcessType1Cmd(theCMD, *pMsg )) { AddToInputList(*pMsg); bSuccess = true; } else { delete pMsg; pMsg = NULL; } } else { //gDebugLog.Write("Invalid checksum - Type 1 Command"); fprintf(stderr, "Invalid checksum - Type 1 Command, header size: %d\n", OT_CMD_HEADER_SIZE); } } else { //gDebugLog.Write("Unknown command type"); fprintf(stderr, "Unknown command type\n"); } // I added this for error correction. In the event that there are errors, // just clean out whatever is in the pipe and throw it away. // Should probably send an Error message back, as well. if (bSuccess == false) { int err = 0, nread = 0; char buffer[1024]; int sizeJunkData = 1024; while (1) { err = SFSocketRead(m_pSocket, buffer, sizeJunkData); if (err > 0) nread += err; #ifdef _WIN32 if (0 == err || SOCKET_ERROR == err) // 0 means disconnect. error means error. >0 means bytes read. #else if (err <= 0) #endif break; } fprintf(stderr, "Transmission error--%d bytes flushed.\n", nread); // we are buffering data from the pipe now, so if we flush the pipe, we // should flush the buffer too. m_Buffer.Release(); } else { // TODO still need to process the commands and send the replies somewhere... //if (bSuccess = theServer.ProcessUserCommand(theMessage, theReply)) //{ // fprintf(stderr, "Successfully processed user command: %s\n", theMessage.m_strCommand.Get()); // ProcessReply(ssl, theReply); // } // else // { // fprintf(stderr, "Unable to process user command in XML, or missing payload, in ProcessMessage.\n"); // } } }
void OTClientConnection::ProcessBuffer() { if (!m_bHaveHeader) { int err, nread; union u_header theCMD; // clear the header object. memset((void *)theCMD.buf, 0, OT_CMD_HEADER_SIZE); // Read the header for (nread = 0; nread < OT_CMD_HEADER_SIZE; nread += err) { err = SFSocketRead(m_pSocket, theCMD.buf + nread, OT_CMD_HEADER_SIZE - nread); #ifdef _WIN32 if (0 == err || SOCKET_ERROR == err) // 0 is a disconnect. error is error. otherwise err contains bytes read. #else if (err <= 0) #endif { break; } else { fprintf(stderr, "Reading input from socket...\n"); } } if (nread == OT_CMD_HEADER_SIZE) { uint32_t lOldSize = theCMD.fields.size; uint32_t lSize = ntohl(lOldSize); // think this might be causing some problems... maybe not. theCMD.fields.size = lSize; // fix the byte order. m_CMD = theCMD; // grab a copy of the header m_bHaveHeader = true; // We need to remember that we are now in "header mode" int nChecksum = theCMD.fields.checksum; fprintf(stderr, "\n************************************************************\n===> Reading header from client message.\n" "First 9 bytes are: %d %d %d %d %d %d %d %d %d.\nSize is: %d...\n", theCMD.buf[0],theCMD.buf[1],theCMD.buf[2],theCMD.buf[3],theCMD.buf[4], theCMD.buf[5], theCMD.buf[6], theCMD.buf[7], theCMD.buf[8], lSize); fprintf(stderr, "\nCMD HEADER: CMD TYPE: %d -- CMD NUM: %d\n" "PAYLOAD SIZE: %d -- CHECKSUM: %d\n", theCMD.fields.type_id, theCMD.fields.command_id, lSize, nChecksum); ReadBytesIntoBuffer(); // When the server knows for SURE it is receiving a message, // then wait for 1 second to make sure we have the entire payload // at once. // TODO: rewrite socket code so that if a complete command has not yet // come in, to buffer the data and wait until next time around the loop. // Because right now, if you have a partial command, it reads it as an error // and returns, discarding what had already been read. Obviously that will // not work for a real server. // In the meantime, this sleep allows me to do testing by insuring that, // with a second's wait, the server will have time to read the entire message. // sleep(1); // ProcessMessage(theCMD); } } else { // If we've finally read enough into our buffer to process the entire mesage, then process it. if (m_Buffer.GetSize() >= m_CMD.fields.size) { ProcessMessage(m_CMD); m_bHaveHeader = false; } // otherwise if we haven't read enough, just read a bit more and wait until next time. else ReadBytesIntoBuffer(); } }