From 2c351a8bccdfe0fe9ad0ccb4dba5e15ef23b4c0c Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 13 Apr 2011 17:24:38 +0200 Subject: - rewrite network methods (ugly but good performance) - fix memleaks --- pacman-c++/client.cpp | 10 +++- pacman-c++/mainwidget.cpp | 34 +++++++------ pacman-c++/pacman.proto | 1 - pacman-c++/server.cpp | 85 ++++++++++++++++++-------------- pacman-c++/server.h | 4 +- pacman-c++/util.cpp | 122 ++++++++++++++++++++++++++++++++++++++-------- pacman-c++/util.h | 14 ++++-- 7 files changed, 190 insertions(+), 80 deletions(-) (limited to 'pacman-c++') diff --git a/pacman-c++/client.cpp b/pacman-c++/client.cpp index 4843d95..5cc9279 100644 --- a/pacman-c++/client.cpp +++ b/pacman-c++/client.cpp @@ -67,6 +67,9 @@ bool Constants::server = false; int main(int argc, char ** argv) { + /* Verify that the version of the library that we linked against is + * compatible with the version of the headers we compiled against. + */ GOOGLE_PROTOBUF_VERIFY_VERSION; QApplication app(argc, argv, true); @@ -80,5 +83,10 @@ int main(int argc, char ** argv) client.show(); client.setWindowTitle(app.applicationName()); - return app.exec(); + int ret = app.exec(); + + /* Delete all global objects allocated by libprotobuf */ + google::protobuf::ShutdownProtobufLibrary(); + + return ret; } diff --git a/pacman-c++/mainwidget.cpp b/pacman-c++/mainwidget.cpp index 5dc392c..d441607 100644 --- a/pacman-c++/mainwidget.cpp +++ b/pacman-c++/mainwidget.cpp @@ -35,11 +35,14 @@ MainWidget::MainWidget(QWidget *parent) /* call updateMap after m_color ist set! */ createGui(); - m_scene->updateMap(Util::createDemoMap()); + Transmission::map_t map = Util::createDemoMap(); + m_scene->updateMap(map); + Util::deleteMap(map); + map = NULL; connect(m_socket, SIGNAL(readyRead()), this, SLOT(tick())); - QTimer *sendTimer = new QTimer(); + QTimer *sendTimer = new QTimer(this); connect(sendTimer, SIGNAL(timeout()), this, SLOT(sendKeyUpdate())); sendTimer->start(Constants::tick); @@ -143,12 +146,12 @@ Transmission::field_t MainWidget::translateKey(int key) void MainWidget::tick() { - std::string dataStr; - Util::QByteArrayToStdString(m_socket->readAll(), dataStr); - + //TODO: allocate + reuse packet + QSharedPointer data = Util::receivePacket(m_socket); ProtoBuf::MapUpdate packet; - bool worked = packet.ParseFromString(dataStr); + bool worked = packet.ParseFromArray(data->data(), data->size()); Q_ASSERT(worked); + Q_UNUSED(worked); Transmission::map_t map = Util::createUninitialisedMap(); Q_ASSERT(packet.field_size() == (int) (Constants::map_size.width * Constants::map_size.height)); int i = 0; @@ -161,7 +164,11 @@ void MainWidget::tick() } } m_scene->updateMap(map); + Util::deleteMap(map); updateScore(packet); + + if (m_socket->bytesAvailable() > (qint64)sizeof(qint64)) + tick(); } void MainWidget::keyPressEvent(QKeyEvent* event) @@ -226,28 +233,27 @@ void MainWidget::playerScoreClicked() Color::Color MainWidget::connectToServer() { - // check command line arguments for server port + /* TODO: check command line arguments for server port */ const QStringList &args = QCoreApplication::arguments(); QString srv = args.value(1, "127.0.0.1"); qDebug() << "[Connect] server=" << srv; - // connect to server + /* connect to server */ m_socket = new QTcpSocket(this); m_socket->connectToHost(srv, Constants::Networking::port); bool worked = m_socket->waitForConnected(Constants::Networking::connection_timeout); if (worked) { - // additional init - // first packet is our color + /* additional init: first packet is our color */ worked = m_socket->waitForReadyRead(); if (worked) { - // receive color - std::string data; - Util::QByteArrayToStdString(m_socket->readAll(), data); + /* receive color */ + QSharedPointer data = Util::receivePacket(m_socket); ProtoBuf::WhoAmI packet; - bool worked = packet.ParseFromString(data); + bool worked = packet.ParseFromArray(data->data(), data->size()); Q_ASSERT(worked); + Q_UNUSED(worked); return static_cast(packet.color() & Transmission::color_mask); } } diff --git a/pacman-c++/pacman.proto b/pacman-c++/pacman.proto index 40c7af0..98478a0 100644 --- a/pacman-c++/pacman.proto +++ b/pacman-c++/pacman.proto @@ -4,7 +4,6 @@ message KeyPressUpdate { required uint32 newKey = 1; } -//TODO move points inside array message MapUpdate { repeated uint32 field = 1 [packed=true]; repeated uint32 round_points = 2; diff --git a/pacman-c++/server.cpp b/pacman-c++/server.cpp index 1c42174..b1877ad 100644 --- a/pacman-c++/server.cpp +++ b/pacman-c++/server.cpp @@ -14,7 +14,10 @@ Server::Server(QWidget *parent) waitForClientConnections(); qDebug() << "[Server] All Clients connected"; - updateMap(Util::createDemoMap()); + Transmission::map_t map = Util::createDemoMap(); + updateMap(map); + Util::deleteMap(map); + map = NULL; QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(tick())); @@ -24,11 +27,10 @@ Server::Server(QWidget *parent) void Server::tick() { qDebug() << "[Tick] Doing server update"; - Transmission::map_t map = calculateUpdates(); + Transmission::map_t map = calculateUpdates(); updateMap(map); - - QSharedPointer packet = createUpdatePacket(map); - sendUpdate(packet); + sendUpdate(map); + Util::deleteMap(map); } Transmission::map_t Server::calculateUpdates() @@ -145,26 +147,6 @@ invalid_direction: return map; } -QSharedPointer Server::createUpdatePacket(Transmission::map_t map) -{ - QSharedPointer updatePacket = - QSharedPointer(new ProtoBuf::MapUpdate); - - for (unsigned int x = 0; x < Constants::map_size.width; ++x) - { - for (unsigned int y = 0; y < Constants::map_size.height; ++y) - updatePacket->add_field(map[x][y]); - } - - for(unsigned i = 0; Color::order[i] != Color::none; ++i) - { - updatePacket->add_round_points(m_actors.value(Color::order[i])->getRoundPoints()); - updatePacket->add_game_points(m_actors.value(Color::order[i])->getGamePoints()); - } - - return updatePacket; -} - void Server::waitForClientConnections() { QTcpServer *tcpSrv = new QTcpServer(this); @@ -182,13 +164,15 @@ void Server::waitForClientConnections() #endif bool connectionAvailable = tcpSrv->waitForNewConnection(-1); Q_ASSERT(connectionAvailable); + Q_UNUSED(connectionAvailable); QTcpSocket *socket = tcpSrv->nextPendingConnection(); connect(socket, SIGNAL(readyRead()), this, SLOT(keyPressUpdate())); - // assign color + /* assign color */ Color::Color color = Color::order[i]; m_clientConnections[color] = socket; - // notify player of color + /* notify player of color */ + //TODO: allocate + reuse packet ProtoBuf::WhoAmI packet; packet.set_color(color); Util::sendPacket(packet, socket); @@ -197,12 +181,31 @@ void Server::waitForClientConnections() } } -void Server::sendUpdate(QSharedPointer< ProtoBuf::MapUpdate > packet) +void Server::sendUpdate(Transmission::map_t map) { - std::string dataStr = packet->SerializeAsString(); - const char *data = dataStr.c_str(); + ProtoBuf::MapUpdate packet; + + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + packet.add_field(map[x][y]); + } + + for(unsigned i = 0; Color::order[i] != Color::none; ++i) + { + packet.add_round_points(m_actors.value(Color::order[i])->getRoundPoints()); + packet.add_game_points(m_actors.value(Color::order[i])->getGamePoints()); + } + + QSharedPointer data = Util::createPacket(packet); foreach(QTcpSocket *socket, m_clientConnections) - Util::sendPacket(data, dataStr.length(), socket); + { + if (!Util::sendPacket(data.data(), socket)) + { + qDebug() << "[sendUpdate] Error while sending data to client, exiting..."; + qApp->quit(); + } + } } void Server::keyPressUpdate() @@ -213,13 +216,15 @@ void Server::keyPressUpdate() i.next(); Color::Color color = i.key(); QTcpSocket *socket = i.value(); - if (socket->bytesAvailable() > 0) + QDataStream in(i.value()); + while (socket->bytesAvailable() > (qint64)sizeof(qint64)) { - std::string dataStr; - Util::QByteArrayToStdString(socket->readAll(), dataStr); + QSharedPointer data = Util::receivePacket(socket); + //TODO: allocate + reuse packet ProtoBuf::KeyPressUpdate packet; - bool worked = packet.ParseFromString(dataStr); + bool worked = packet.ParseFromArray(data->data(), data->size()); Q_ASSERT(worked); + Q_UNUSED(worked); Transmission::field_t direction = packet.newkey(); qDebug() << "[KeyPress] actor=" << color << "direction=" << direction; m_actorMovements[color] = Util::transmissionMovementToActor(direction); @@ -231,6 +236,9 @@ bool Constants::server = true; int main(int argc, char ** argv) { + /* Verify that the version of the library that we linked against is + * compatible with the version of the headers we compiled against. + */ GOOGLE_PROTOBUF_VERIFY_VERSION; QApplication app(argc, argv, false); @@ -240,5 +248,10 @@ int main(int argc, char ** argv) qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); Server Server; - return app.exec(); + int ret = app.exec(); + + /* Delete all global objects allocated by libprotobuf */ + google::protobuf::ShutdownProtobufLibrary(); + + return ret; } diff --git a/pacman-c++/server.h b/pacman-c++/server.h index 32bdf15..f7505a6 100644 --- a/pacman-c++/server.h +++ b/pacman-c++/server.h @@ -28,10 +28,8 @@ protected: /* calculate updates of current tick for sending to client */ Transmission::map_t calculateUpdates(); - QSharedPointer createUpdatePacket(Transmission::map_t); - /* update client maps */ - void sendUpdate(QSharedPointer); + void sendUpdate(Transmission::map_t map); QMap m_clientConnections; diff --git a/pacman-c++/util.cpp b/pacman-c++/util.cpp index c60f2df..2fb9f69 100644 --- a/pacman-c++/util.cpp +++ b/pacman-c++/util.cpp @@ -1,5 +1,4 @@ #include "util.h" - #include namespace Util @@ -27,6 +26,13 @@ namespace Util return map; } + void deleteMap(Transmission::map_t map) + { + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + delete[] map[x]; + delete[] map; + } + // temporary Transmission::map_t createDemoMap() { @@ -141,32 +147,108 @@ namespace Util return def; } - void QByteArrayToStdString(const QByteArray& arr, std::string& str) + QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet) { - // TODO: normal conversion to std::string won't work, - // probably due to \0-bytes. - //std::string dataStr = std::string(data.constData()); - //std::string dataStr = QString(data).toStdString(); - for (int i=0; i data = QSharedPointer(new QByteArray); + data->resize(datalen); + + /* use QDataStream for length to avoid endianess shit */ + QDataStream out(data.data(), QIODevice::WriteOnly); + out << packetlen; + + /* use protobuf.SerializeWithCachedSizesToArray() to avoid calling protobuf.ByteSize() again */ + ::google::protobuf::uint8 *dataptr = reinterpret_cast(data->data()); + packet.SerializeWithCachedSizesToArray(dataptr + sizeof(qint64)); + + return data; } - void sendPacket(const char *data, int length, QTcpSocket *socket) + bool sendPacket(QByteArray *data, QTcpSocket *socket) { - int bytesWritten = socket->write(data, length); - if (bytesWritten != length) + int bytesWritten = socket->write(*data); + if (bytesWritten != data->size()) { - qDebug() << "[sendPacket] Not all data has been sent." - << "written=" << bytesWritten << ", length=" << length; + qWarning() << "[sendPacket] Not all data has been sent:" + << "written=" << bytesWritten << ", length=" << data->size(); + return false; } - Q_ASSERT(bytesWritten == length); socket->flush(); + return true; + } + + bool sendPacket(const ::google::protobuf::MessageLite& packet, QTcpSocket *socket) + { + return sendPacket(createPacket(packet).data(), socket); + } + + QSharedPointer receivePacket(QTcpSocket *socket) + { + QDataStream in(socket); + qint64 datalen; + in >> datalen; + + QSharedPointer data = QSharedPointer(new QByteArray); + data->resize(datalen); + socket->read(data->data(), data->size()); + return data; + } + +#if 0 + void hexdump(void *pAddressIn, long lSize) + { + char szBuf[100]; + long lIndent = 1; + long lOutLen, lIndex, lIndex2, lOutLen2; + long lRelPos; + struct { char *pData; unsigned long lSize; } buf; + unsigned char *pTmp,ucTmp; + unsigned char *pAddress = (unsigned char *)pAddressIn; + + buf.pData = (char *)pAddress; + buf.lSize = lSize; + + while (buf.lSize > 0) + { + pTmp = (unsigned char *)buf.pData; + lOutLen = (int)buf.lSize; + if (lOutLen > 16) + lOutLen = 16; + + // create a 64-character formatted output line: + sprintf(szBuf, " > " + " " + " %08lX", pTmp-pAddress); + lOutLen2 = lOutLen; + + for(lIndex = 1+lIndent, lIndex2 = 53-15+lIndent, lRelPos = 0; + lOutLen2; + lOutLen2--, lIndex += 2, lIndex2++ + ) + { + ucTmp = *pTmp++; + + sprintf(szBuf + lIndex, "%02X ", (unsigned short)ucTmp); + if(!isprint(ucTmp)) ucTmp = '.'; // nonprintable char + szBuf[lIndex2] = ucTmp; + + if (!(++lRelPos & 3)) // extra blank after 4 bytes + { lIndex++; szBuf[lIndex+2] = ' '; } + } + + if (!(lRelPos & 3)) lIndex--; + + szBuf[lIndex ] = '<'; + szBuf[lIndex+1] = ' '; + + printf("%s\n", szBuf); + + buf.pData += lOutLen; + buf.lSize -= lOutLen; + } } +#endif } diff --git a/pacman-c++/util.h b/pacman-c++/util.h index e0f1264..87dbd86 100644 --- a/pacman-c++/util.h +++ b/pacman-c++/util.h @@ -12,17 +12,21 @@ namespace Util Transmission::map_t createUninitialisedMap(); Transmission::map_t createDemoMap(); Transmission::map_t createEmptyMap(); + void deleteMap(Transmission::map_t map); - // default is to assert false with -1 Transmission::field_t actorMovementToTransmission(Actor::Movement mov, Transmission::field_t def = Transmission::none); Actor::Movement transmissionMovementToActor(Transmission::field_t field, Actor::Movement def = Actor::None); - void QByteArrayToStdString(const QByteArray& arr, std::string& str); + /* send packet with error check and flush */ + QSharedPointer createPacket(const ::google::protobuf::MessageLite& packet); + bool sendPacket(QByteArray *data, QTcpSocket *socket); + bool sendPacket(const ::google::protobuf::MessageLite& packet, QTcpSocket *socket); + QSharedPointer receivePacket(QTcpSocket *socket); - // send packet with error check and flush - void sendPacket(const ::google::protobuf::Message& packet, QTcpSocket *socket); - void sendPacket(const char *data, int length, QTcpSocket *socket); +#if 0 + void hexdump(void *pAddressIn, long lSize); +#endif } #endif // UTIL_H -- cgit v1.2.3