Pingpong Client/Server mit QTcpServer & QTcpSocket

Alles rund um die Programmierung mit Qt
Antworten
_titus_
Beiträge: 2
Registriert: 25. August 2007 15:12
Kontaktdaten:

Pingpong Client/Server mit QTcpServer & QTcpSocket

Beitrag von _titus_ »

Hallo Qt-Community,

ich habe folgendes Problem: Habe ein kleines Client-Server Programm geschrieben (siehe http://miloi.de/priv/pingpong.zip), bei dem der Client sich mit dem Server verbindet und ein "PING" zum Server schickt. Darauf soll der Server mit "PONG" antworten, worauf wieder ein "PING" vom Client kommen soll usw. Leider klappt das nicht, weil das "PING" nie geschickt wird. Die Socketverbindung wird aufgebaut, aber sock->write() schickt nichts über den Socket, obwohl danach sogar sock->flush() aufgerufen wird. Eine Event Loop ist ebenfalls vorhanden.
Meine Vermutung ist, dass flush() nur bewirkt, dass der QTcpSocket Puffer and den Betriebssystem-TCP-Socket geschrieben wird, aber nicht, dass die Daten tatsächlich auch übertragen werden. Falls das so ist, muss es doch aber irgendeine Möglichkeit geben, das BS dazu zu veranlassen, die Daten rauszuschicken. Jedes Client-Server-System lebt doch von Requests und Replies?! Oder ist der Fehler doch im Code (siehe http://miloi.de/priv/pingpong.zip). Habe tagelang erfolglos versucht, das Problem ausfindig zu machen, wäre also für jeden Tipp, Hinweis oder jede Meinung dankbar! Code siehe unten.

Qt: 4.3.1 Commercial und OSS
BS: Win XP SP2, OpenSuSE Linux 10.2 und FreeBSD 6.2

Gruß

Titus

PingpongClient.h:

Code: Alles auswählen

#include <QTcpSocket>

class PingpongClient : public QTcpSocket
{
Q_OBJECT

public:
   PingpongClient();
   
private slots:
   void ping();
   void readPong();
   void displayError(QAbstractSocket::SocketError socketError);
   
private:
   int blockSize;
   
};
main.cpp (vom Client):

Code: Alles auswählen

#include "PingpongClient.h"

#include <QCoreApplication>
#include <QDataStream>
#include <QByteArray>
#include <string>
#include <iostream>


PingpongClient::PingpongClient() : QTcpSocket()
{
    connect(this, SIGNAL(readyRead()), this, SLOT(readPong()));
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstractSocket::SocketError)));
    connect(this, SIGNAL(connected()), this, SLOT(ping()));
}

void PingpongClient::ping()
{
   std::cout << "Sending PING!" << std::endl;
   QByteArray block;
   QDataStream out(&block, QIODevice::WriteOnly);
   out.setVersion(QDataStream::Qt_4_0);
   out << (quint16)0;
   out << "PING!";
   out.device()->seek(0);
   out << (quint16)(block.size() - sizeof(quint16));

   blockSize = 0;
   write(block);
   flush();
}

void PingpongClient::readPong()
{
   QDataStream in(this);
   in.setVersion(QDataStream::Qt_4_0);

   if (blockSize == 0)
   {
      if (bytesAvailable() < (int)sizeof(quint16))
         return;
      in >> blockSize;
   }

   if (bytesAvailable() < blockSize)
      return;

   QString line;
   in >> line;
   
   std::cout << "Response: " << line.toAscii().data() << std::endl;

   ping();
}

void PingpongClient::displayError(QAbstractSocket::SocketError socketError)
{
   switch (socketError) {
   case QAbstractSocket::RemoteHostClosedError:
      std::cout << "Connection closed by server" << std::endl;
      break;
   case QAbstractSocket::HostNotFoundError:
      std::cout << "Host not found" << std::endl;
      break;
   case QAbstractSocket::ConnectionRefusedError:
      std::cout << "Connection refused" << std::endl;
      break;
   default:
      std::cout << "Error: " << errorString().toAscii().data() << std::endl;
   }
   exit(0);
}

/******************************************************************************/

int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
   PingpongClient client;
   client.connectToHost("127.0.0.1", 4242);
   return app.exec();
}
PingpongServer.h:

Code: Alles auswählen

#include <QTcpServer>
#include <QTcpSocket>

class PingpongServer : public QTcpServer
{
Q_OBJECT

public:
   PingpongServer();
   virtual ~PingpongServer();
   
private slots:
   void openConnection();
   void pong();
   void readPing();
   void clientDisconnect();
   void displayError(QAbstractSocket::SocketError socketError);

private:
   QTcpSocket *sock;
   int blockSize;

};
main.cpp (vom Server):

Code: Alles auswählen

#include "PingpongServer.h"

#include <QCoreApplication>
#include <QDataStream>
#include <QByteArray>
#include <string>
#include <iostream>

PingpongServer::PingpongServer() : QTcpServer()
{
   sock = NULL;
   connect(this, SIGNAL(newConnection()), this, SLOT(openConnection()));
}

PingpongServer::~PingpongServer()
{
   clientDisconnect();
}

void PingpongServer::openConnection()
{
   if (sock != NULL)
      std::cout << "Client already connected" << std::endl;
   else
   {
      blockSize = 0;
      sock = nextPendingConnection();
      connect(sock, SIGNAL(disconnected()), this, SLOT(clientDisconnect()));
      connect(sock, SIGNAL(readyRead()), this, SLOT(readPing()));
      connect(sock, SIGNAL(error(QAbstractSocket::SocketError)),
              this, SLOT(displayError(QAbstractSocket::SocketError)));
   }
}

void PingpongServer::pong()
{
   if (sock == NULL)
      return;

   std::cout << "Sending PONG!" << std::endl;
   QByteArray block;
   QDataStream out(&block, QIODevice::WriteOnly);
   out.setVersion(QDataStream::Qt_4_0);
   out << (quint16)0;
   out << "PONG!";
   out.device()->seek(0);
   out << (quint16)(block.size() - sizeof(quint16));

   blockSize = 0;
   sock->write(block);
   sock->flush();
}

void PingpongServer::readPing()
{
   if (sock == NULL)
      return;

   QDataStream in(sock);
   in.setVersion(QDataStream::Qt_4_0);

   if (blockSize == 0)
   {
      if (sock->bytesAvailable() < (int)sizeof(quint16))
         return;
      in >> blockSize;
   }

   if (sock->bytesAvailable() < blockSize)
      return;

   QString line;
   in >> line;
   
   std::cout << "Response: " << line.toAscii().data() << std::endl;

   pong();
}

void PingpongServer::clientDisconnect()
{
   if (sock != NULL)
   {
      sock->deleteLater();
      sock = NULL;
   }
}

void PingpongServer::displayError(QAbstractSocket::SocketError socketError)
{
   switch (socketError) {
   case QAbstractSocket::RemoteHostClosedError:
      std::cout << "Connection closed by client" << std::endl;
      break;
   case QAbstractSocket::HostNotFoundError:
      std::cout << "Host not found" << std::endl;
      break;
   case QAbstractSocket::ConnectionRefusedError:
      std::cout << "Connection refused" << std::endl;
      break;
   default:
      if (sock != NULL)
         std::cout << "Error: " << sock->errorString().toAscii().data() << std::endl;
      else
         std::cout << "Unknown error" << std::endl;
   }
   exit(0);
}

/******************************************************************************/

int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
   PingpongServer server;
   if (!server.listen(QHostAddress::Any, 4242))
   {
      std::cout << "Could not start server" << std::endl;
      return 0;
   }
   return app.exec();
}
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Bei Qt sind viele Netzwerkbeispiel dabei. Z.B. auch FortuneClient/Server - der macht genau das Gleiche was Du auch machen möchtest. Einfach mal schauen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
_titus_
Beiträge: 2
Registriert: 25. August 2007 15:12
Kontaktdaten:

Problem teilweise gelöst...

Beitrag von _titus_ »

Danke für den Hinweis! Die Beispiele waren natürlich das erste, was ich mir angeguckt habe. Das erwähnte Beispiel macht leider nicht genau das, was ich machen möchte - nachdem eine Fortune gesendet wurde, wird nämlich der Socket direkt wieder geschlossen - und DA werden ja auch bei mir die Daten spätestens gesendet. Aber ich will ja nicht nach jedem Datenpaket den Socket schliessen, um sicher zu gehen, dass die daten auch versendet werden.

Einen Bug im geposteten Code habe ich bereits gefunden: in der Klassendeklaration muss das Feld packetSize natürlich ein quint16 und kein int sein. So funktioniert zumindest das hier gepostete Beispiel.

Nur im größeren Rahmen (mit nem nonblocking multithreaded server) krieg ichs immer noch nicht richtig hin.
Antworten