Logo Search packages:      
Sourcecode: kmess version File versions

bool P2PApplicationBase::sendP2PMessageImpl ( const QByteArray &  messageData,
int  flagField = 0,
P2PDataType  footerCode = P2P_TYPE_NEGOTIATION,
P2PMessageType  messageType = P2P_MSG_UNKNOWN,
quint32  messageID = 0 
) [private]

Internal function to send a single P2P message.

This method constructs the P2P binary header fields. The constructed message is delivered to ApplicationList::sendMessage().

The following instance fields are updated automatically:

  • nextMessageID_ is generated for every new P2P message.
  • fragmentOffset_ is updated when a splitted message is sent.
  • fragmentTotalSize_ and messageOffse_ are reset when the last message fragment is sent.
  • outgoingMessages_ is updated to trace ACKs back later.

The fragmentTotalSize_ should be set to the total size before sending message fragments. This is done automatically by sendP2PMessage() function, which is part of the public API.

Parameters:
messageData The message payload to send. This can be binary file data or a SLP MIME data.
flagField The value for the flag field in the P2P header.
footerCode The message footer, which is appended when the message is sent over the switchboard.
messageType The type of the message. This value is stored with in the unacked message queue. This value is used later to respond to ACKs of certain special messages.
messageID Optional message ID to enforce. If nothing is selected, the next available message ID will be used automatically. This parameter is used to send file data with a specific message ID.
Returns:
Whether the message can be sent. If the socket would block, false is returned. This only occurs for direct connections connections.

Definition at line 1799 of file p2papplicationbase.cpp.

References P2PApplicationBase::UnAckedMessage::ackSessionID, P2PApplicationBase::UnAckedMessage::dataSize, KMessShared::generateID(), Application::getContactHandle(), P2PApplicationBase::UnAckedMessage::messageID, P2PApplicationBase::UnAckedMessage::messageType, P2PMessage::MSN_FLAG_OBJECT_DATA, P2P_MSG_DATA_PREPARATION, ApplicationList::sendMessage(), P2PApplicationBase::UnAckedMessage::sentTime, P2PApplicationBase::UnAckedMessage::sessionID, and P2PApplicationBase::UnAckedMessage::totalSize.

Referenced by sendDataPreparation(), sendNextDataParts(), sendP2PMessage(), and sendP2PWaitingError().

{
#ifdef KMESSTEST
  KMESS_ASSERT( messageData.size() <= 1202 );
#endif

  // Initialize the header
  QByteArray header( 48, 0x00 );

  // Determine the message ID, if none is given.
  if( messageID == 0 )
  {
    if( nextMessageID_ == 0)
    {
      // This is the first P2P message we send, initialize message ID
      nextMessageID_ = KMessShared::generateID();
    }
    else if(fragmentOffset_ == 0)
    {
      // Only increments the ID for the first message.
      // Don't increment it for the next fragments (if it's a splitted message).
      // Better approach: pass the desired messageID to this function.
      nextMessageID_++;
    }

    messageID = nextMessageID_;
  }


  // Verify we're not configured to send a splitted message.
  if( fragmentTotalSize_ > 0
  &&  flagField != 0
  &&  flagField != P2PMessage::MSN_FLAG_OBJECT_DATA
  &&  flagField != P2PMessage::MSN_FLAG_FILE_DATA )
  {
    kWarning() << "Attempting to send splitted ACK message "
                  "(flags=0x" << QString::number( flagField, 16 ) <<
                  " state="   << waitingState_ <<
                  " contact=" << getContactHandle() <<
                  " session=" << getSessionID() <<
                  " class="   << metaObject()->className() <<
                  " action=continue)!";
  }


  // Determine the message size:
  // Internal trick: fragmentTotalSize_ is set by the caller if this is a splitted message.
  quint32 messageSize = messageData.size();
  quint32 totalSize;
  quint32 offsetField;

  if( fragmentTotalSize_ == 0 )
  {
    // Normal message
    totalSize   = messageData.size();
    offsetField = 0;
  }
  else
  {
    // Splited message: total size is set.
    totalSize   = fragmentTotalSize_;
    offsetField = fragmentOffset_;
  }


  // Generate ack session identifier for this message
  quint32 ackSessionID = KMessShared::generateID();

  // Set session ID to zero when SLP messages are sent.
  quint32 sessionID = getSessionID();
  if( flagField  == 0
  &&  footerCode == 0      // fixes all data transfers, like msnobjects and webcam.
  &&  messageType != P2P_MSG_DATA_PREPARATION )
  {
    sessionID = 0;
  }

  // Update the header
  P2PMessage::insertBytes(header, sessionID,      0);    // 1: Session ID (zero for SLP messages).
  P2PMessage::insertBytes(header, messageID,      4);    // 2: BaseIdentifier (starts random [4, 4294967295])
  P2PMessage::insertBytes(header, offsetField,    8);    // 3: Offset (if more then 1202 bytes to send)
  P2PMessage::insertBytes(header, totalSize,      16);   // 4: Total size of the message
  P2PMessage::insertBytes(header, messageSize,    24);   // 5: This message size, size of previous if no data
  P2PMessage::insertBytes(header, flagField,      28);   // 6: Message type (flags)
  P2PMessage::insertBytes(header, ackSessionID,   32);   // 7: Used to link ack messages (ACK session ID)
//P2PMessage::insertBytes(header, 0,              36);   // 8: Field7 of previous for ACK-messages (unique ID)
//P2PMessage::insertBytes(header, 0,              40);   // 9: Field4 of previous for ACK-messages (size)


  // Deliver the message over the correct link (switchboard or direct connection)
  bool writeSuccess = applicationList_->sendMessage( this, header, messageData, (uint) footerCode );
  if( ! writeSuccess )
  {
    // This only happens when a direct connection transfer is congested.
    // The same message will be re-sent later.
#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
    kDebug() << "Message sending was blocked, should try again later.";
#endif
    return false;
  }


  // Update unacked message queue to handle incoming ACKs
  if( flagField == 0 || messageType != 0 )
  {
    if( offsetField == 0 )
    {
#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
      kDebug() << "Storing message fields to track the ACK response.";
#endif
      // First message, or single message, store new record for ACKs.
      QDateTime currentTime;
      UnAckedMessage *ackData = new UnAckedMessage;
      ackData->sessionID    = getSessionID();
      ackData->messageID    = messageID;
      ackData->dataSize     = messageSize;
      ackData->totalSize    = totalSize;
      ackData->ackSessionID = ackSessionID;
      ackData->messageType  = messageType;  // used later to handle certain ACKs.
      ackData->sentTime     = currentTime.toTime_t();
      outgoingMessages_.append(ackData);
    }
    else
    {
#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
      kDebug() << "Updating message fields to track the ACK response.";
#endif
      // Follow-up part of a splitted/fragmented message.
      // Update the uniqueID aka ackSID field to handle new acks.
      // TODO: it appears MSN7 uses the same ackSID for some messages, not sure
      //       whether that is a feature (to capture acks in a short time) or a problem in their random function.
      foreach( UnAckedMessage *unAcked, outgoingMessages_ )
      {
        if( unAcked->messageID == messageID )
        {
          unAcked->ackSessionID = ackSessionID;
          break;
        }
      }
    }
  }

  // Update the offset fields for splitted messages
  if(fragmentTotalSize_ != 0)
  {
    // Update offset
    fragmentOffset_ += messageSize;

    // If we transmitted all parts, reset the fields for the next normal message
    if(fragmentOffset_ >= fragmentTotalSize_)
    {
#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
      kDebug() << "All message parts sent, resetting fragmentOffset_ and fragmentTotalSize_.";
#endif

      fragmentOffset_    = 0;
      fragmentTotalSize_ = 0;
    }
  }


#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
  kDebug() << "Message transmitted "
            << "(sid="     << getSessionID()
            << " mid="     << messageID
            << " ackSid="  << ackSessionID
            << " flags=0x" << QString::number(flagField, 16)
            << " size="    << messageSize
            << " type="    << messageType
            << ")";
#endif

  return true;
}


Generated by  Doxygen 1.6.0   Back to index