PacketWriter.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "PacketWriter.h"
00025 #include "Protocols/Transports/Connection.h"
00026 
00027 using Gnutella::PacketProcessing::PacketWriter;
00028 using Protocols::Transports::Connection;
00029 using Gnutella::Packets::Packet;
00030 
00031 PacketWriter::PacketWriter (Connection *connection)
00032  :  p()
00033 {
00034     // Ensure that canWrite() returns true after construction.
00035     p.packetLength          = 0;
00036     p.packetBytesWritten    = 0;
00037     p.connection = connection;
00038 
00039     connect (p.connection,  SIGNAL  (bytesWritten (qint64)),
00040              this,          SLOT    (bytesWritten (qint64)));
00041 }
00042 
00043 PacketWriter::~PacketWriter()
00044 {
00045     //QObject::disconnect (p.connection,    SIGNAL  (bytesWritten (qint64)),
00046     //                   this,              SLOT    (bytesWritten (qint64)));
00047 }
00048 
00049 void PacketWriter::write (const Packet &packet)
00050 {
00051     using Gnutella::Packets::Packet;
00052     using Gnutella::Packets::HeaderLength;
00053     using Gnutella::Packets::MaximalPayloadLength;
00054     // Should we copy the data for each connection or use a shared QByteArray?
00055     // Shared buffer avoids copying the buffer => time + storage (storage may
00056     // be insignificant due to flow control and routing)
00057     // Shared buffer may lead to cache misses => time
00058     // The writer will be UdpConnection friendly if all the data is written
00059     // at once, i.e. not first header, then payload. Even if we want to use a
00060     // shared buffer, we can provide a function rawPacket, which returns
00061     // a QByteArray containing the complete packet. rawHeader() and rawPayload()
00062     // can then construct a buffer fromRawData().
00063     // \todo use rawPacket()
00064     QByteArray header = packet.rawHeader();
00065     QByteArray payload = packet.rawPayload();
00066 
00067     Q_ASSERT (header.length() == HeaderLength);
00068     Q_ASSERT (payload.length() <= MaximalPayloadLength);
00069 
00070     memcpy (p.rawPacket, header.data(), header.length());
00071     memcpy (p.rawPacket + header.length(), payload.data(), payload.length());
00072 
00073     p.packetLength          = header.length() + payload.length();
00074     p.packetBytesWritten    = 0;
00075 
00076     // \todo Should we delay the actual writing. It may be better to write
00077     // as much data as possible in the smallest interval possible. Maybe
00078     // the network implementation will perform better in that case. Do
00079     // more research on the topic.
00080     QMetaObject::invokeMethod (this, "bytesWritten", Qt::QueuedConnection,
00081                 QGenericReturnArgument(), Q_ARG (qint64, 0));
00082 }
00083 
00084 void PacketWriter::bytesWritten (qint64)
00085 {
00086     using Gnutella::Packets::HeaderLength;
00087     using Gnutella::Packets::MaximalPayloadLength;
00088 
00089     if (p.packetLength == p.packetBytesWritten)
00090         return;
00091 
00092     char *dataStart = p.rawPacket + p.packetBytesWritten;
00093     qint64 dataLength = p.packetLength - p.packetBytesWritten;
00094     qint64 written = p.connection->write (dataStart, dataLength);
00095 
00096     if (written >= 0) {
00097         p.packetBytesWritten += written;
00098         if (p.packetBytesWritten == p.packetLength)
00099             emit packetWritten();
00100     } else {
00101         emit writeError(); // would be Error::DeviceError
00102         return;
00103     }
00104 
00105     return;
00106 }
00107 
00108 bool PacketWriter::canWrite() const
00109 {
00110     return p.packetBytesWritten == p.packetLength;
00111 }