Seite 1 von 1

Fragen zu QTcpSocket und QTcpServer

Verfasst: 23. November 2009 23:05
von Toni0815
Hallo Leute,
also, ich habe gerade begonnen ein kleines Chatprogrammen zu erstellen und dabei sind bei mir Unklarheiten aufgetaucht.

Der Chat, den ich machen will besteht aus zwei Programmen, einem Sender und einem Empfänger.

So bin ich vorgegangen:

Sender-Programm
----------------------

1. Ich erstelle ein QTcpSocket-Objekt und erstelle einen Signal/Slot um herauszufinden, ob die verbindung besteht.

Code: Alles auswählen

tcpSocket = new QTcpSocket(this);
QObject::connect(tcpSocket, SIGNAL(connected()), this, SLOT(verbindungAufgebaut()));
2. Ich baue die Verbindung zu host auf

Code: Alles auswählen

tcpSocket->connectToHost( string_host, int_port );
3. Ich erzeuge einen Stream, den ich dann auf tcpSocket ausgebe

Code: Alles auswählen

QTextStream stream_nachricht(tcpSocket);
  stream_nachricht << string_nachricht << endl;



Empfänger-Programm
----------------------

1. Ich erzeuge ein QTcpServer-Objekt und ein QTcpSocket-Objekt und weise ihm SIGNALS/SLOTS zu um eine reinkommende Verbindung zu erkennen und um die Daten auszulesen

Code: Alles auswählen

tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(tcpServer);
QObject::connect(tcpServer, SIGNAL(newConnection()), this, SLOT(verbindungAufgebaut()));
    QObject::connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(nachrichtEmpfangen()));
2. Ich weise dem tcpServer-Objekt einen Adapter und einen Port zu, den er abhören soll

Code: Alles auswählen

tcpServer->listen(QHostAddress::Any,int_port);

Das funktioniert schon
-------------------------
Am Server wird bei Verbindung das SINAL newConnection() ausgelöst, also eine Verbindung wird aufgebaut, das funktioniert bei localhost, LAN, Hamachi und übers internet. Soweit alles ok.


Das funktioniert nicht
-------------------------
Am Server wird das SIGNAL readyRead() nicht ausgelöst und ich kann die Daten nicht empfangen.
Ich kann nicht auslesen wie die IP des verbundenen Hosts ist.


Nun meine Fragen
---------------------
1. Dient das QTcpServer-Objekt nur dazu um an einem Port zu horchen ?
2. Muss ich am Server (wie schon gemacht) ein QTcpSocket-Objekt erstellt werden?
3. Ich muss doch irgendwie dem QTcpSocket-Objekt die Eigenschaften des QTcpServer-Objekt übergeben, wie z.B Port auf dem Daten ankommen usw., wie funktioniert das genau?
4.Wie kann ich am Server die IP des Verbundenen Client auswerten und als String verwenden?
5. Wie genau müsste der Empfang von Daten funktionieren?





Vielen Dank im Vorraus,
ich hoffe ihr könnt mir bei meinem N00b-Problem helfen.

Verfasst: 24. November 2009 08:12
von upsala
Schau dir mal den Fortune-Server & -Client an.

http://doc.trolltech.com/4.4/examples.html#network

Verfasst: 24. November 2009 08:14
von solarix
> 1. Dient das QTcpServer-Objekt nur dazu um an einem Port zu horchen ?
.. und natürlich auch die Verbindung(en) aufzubauen..

> 2. Muss ich am Server (wie schon gemacht) ein QTcpSocket-Objekt erstellt werden?
Nein.. siehe http://doc.trolltech.com/4.5/network-fortuneserver.html

> 3. Ich muss doch irgendwie dem QTcpSocket-Objekt die Eigenschaften des QTcpServer-Objekt übergeben[...]
Nein.. siehe http://doc.trolltech.com/4.5/network-fortuneserver.html

> 4.Wie kann ich am Server die IP des Verbundenen Client auswerten und als String verwenden?
siehe Doku: http://doc.trolltech.com/4.5/qabstractsocket.html

> 5. Wie genau müsste der Empfang von Daten funktionieren?
Kommt auf dein Protokoll an.. du musst selbst definieren, wie die Daten des Streams(!) interpretiert werden müssen. Für ein ASCII "HelloWorld" genügt readAll().. (siehe die zahlreichen read-Methoden von http://doc.trolltech.com/4.5/qtcpsocket-members.html)

Verfasst: 24. November 2009 22:06
von Toni0815
Danke für eure Antworten,

aber könntet ihr mir mal vom sinn her schritt für schritt erklären wie so eine Verbindung abläuft...

Ich stell mir das so vor.

1. QTcpSocktet-Objekt baut Verbindung zum Host mit gewünschter IP un Port auf.

2. QTcpServer-Objekt im Server "horcht" an einem Port und akzeptiert die Verbindung, somit besteht die Verbindung und newConnection() wird ausgelöst.

??? aber wie geht es vom Sinn her weiter ??? könntet ihr das bitte mal beschreiben...



MfG Toni0815 :-)

Verfasst: 24. November 2009 22:27
von upsala
void QTcpServer::newConnection ()
QTcpSocket * QTcpServer::nextPendingConnection ()
void QIODevice::readyRead ()
QByteArray QIODevice::readAll ()
qint64 QIODevice::write ( const QByteArray & byteArray )
void QIODevice::bytesWritten ( qint64 bytes )
void QAbstractSocket::close ()

Verfasst: 25. November 2009 01:21
von Toni0815

Code: Alles auswählen

QTcpSocket * QTcpServer::nextPendingConnection () 
also hiermit übergebe ich die aktuelle Verbindung des Servers an den Socket
aber leider wird bei mir readyRead nicht ausgelöst :-(

Ich poste nun mal das, was ich bis jetzt gemacht habe, vielleicht kann mir ja einer von euch Experten einen Tipp geben :-)

Hier der Code des Server-Programms
---------------------------------------------

Code: Alles auswählen

#include "netzwerknachrichtempfaenger.h"
#include "ui_netzwerknachrichtempfaenger.h"

NetzwerkNachrichtEmpfaenger::NetzwerkNachrichtEmpfaenger(QWidget *parent)
    : QDialog(parent), ui(new Ui::NetzwerkNachrichtEmpfaenger)
{
    ui->setupUi(this);

    tcpServer = new QTcpServer(this);
    tcpServer->listen(QHostAddress::Any,55555);
    tcpSocket = new QTcpSocket(this);
   
    QObject::connect(tcpServer, SIGNAL(newConnection()), this, SLOT(verbindungAufgebaut()));
    QObject::connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(nachrichtEmpfangen()));
}

NetzwerkNachrichtEmpfaenger::~NetzwerkNachrichtEmpfaenger()
{
    delete ui;
    delete tcpServer;
}

void NetzwerkNachrichtEmpfaenger::nachrichtEmpfangen(void)
{
    ui->feldNachrichten->addItem("Test, ob readyRead ausgelöst wurde"); 
}

void NetzwerkNachrichtEmpfaenger::verbindungAufgebaut(void)
{
    tcpSocket = tcpServer->nextPendingConnection();
}



Hier der Code des Client-Programms
---------------------------------------------

Code: Alles auswählen

#include "netzerknachrichtsender.h"
#include "ui_netzerknachrichtsender.h"

NetzerkNachrichtSender::NetzerkNachrichtSender(QWidget *parent)
    : QDialog(parent), ui(new Ui::NetzerkNachrichtSender)
{
    ui->setupUi(this);

    tcpSocket = new QTcpSocket(this);
    QObject::connect(tcpSocket, SIGNAL(connected()), this, SLOT(verbindungAufgebaut()));
    QObject::connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(verbindungGetrennt()));
}


NetzerkNachrichtSender::~NetzerkNachrichtSender()
{
    delete ui;
    delete tcpSocket;
}


void NetzerkNachrichtSender::on_buttonSenden_clicked()
{
      string_host   = ui->ipFeld->text();
      string_port   = ui->portFeld->text();
      int_port      = string_port.toInt(&bool_eingabeOk, 10);

      if(!bool_eingabeOk)
      {
            ui->portFeld->setText("55555");
            return;
      }

      tcpSocket->connectToHost( string_host, int_port );
      string_nachricht = ui->nachrichtFeld->text();

      if ( string_nachricht.isEmpty() )
      {
            return;
      }

      nachrichtSenden();

}

void NetzerkNachrichtSender::verbindungAufgebaut(void)
{
    ui->statusFeld->setText("Connected to "+tcpSocket->peerName()+"::"+QString::number(tcpSocket->peerPort(),10));
    tcpSocket->write("Test");
}


void NetzerkNachrichtSender::verbindungGetrennt(void)
{
    ui->statusFeld->setText("Disconnected");
}


void NetzerkNachrichtSender::nachrichtSenden(void)
{
    if ( string_nachricht.isEmpty() || string_port.isEmpty() || string_host.isEmpty() )
    {
        return;
    }
    QTextStream stream_nachricht(tcpSocket);
    stream_nachricht << string_nachricht << endl;
}


Mfg Toni0815

Verfasst: 25. November 2009 08:32
von solarix

Code: Alles auswählen

tcpSocket = new QTcpSocket(this);
    QObject::connect(tcpSocket, SIGNAL(connected()), this, SLOT(verbindungAufgebaut()));
    QObject::connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(verbindungGetrennt())); 
Dieser Code im Konstruktor ist komplett sinnfrei... schau dir mal folgendes Beispiel an:

http://doc.trolltech.com/4.5/network-fo ... r-cpp.html

Und nun kannst du dir selbst die Frage stellen: Wer erstellt den QTcpSocket und wann muessen die Signals des Sockets zu den eigenen Slots verbunden werden?

[edit]
Deine Frage 2 (ob beim Server der Socket selbst erstellt werden muss) habe ich uebrigens schon lange mit nein beantwortet..

Verfasst: 25. November 2009 09:10
von Toni0815
Dieser Code im Konstruktor ist komplett sinnfrei... schau dir mal folgendes Beispiel an:
Wäre super, wenn du mal zu deinen Antworten eine Begründung hinzuschreiben würdest, warum kann ich im Konstruktor kein Objekt erzeugen und keine Signal/Slots zuweisen?
Was soll daran sinnfrei sein, oder gibt es bei so einem Socketobjekt eine besonderheit .....

Verfasst: 25. November 2009 10:32
von solarix
Die fehlende Begruendung begruende ich damit, dass der Fehler in deinem Programm auf fehlendes C++-Grundwissen und mangelnder Umgang mit der Doku zurueckzufuehren ist.

Folgende Erklaerung:
Auch fuer jemanden ohne Qt-Erfahrung sollte es ersichtlich sein, dass im Konstruktor eine Instanz erzeugt wird und der Pointer darauf jedoch spaeter ueberschrieben wird:

Code: Alles auswählen

// im C-Tor
  tcpSocket = new QTcpSocket(this); 
// in verbindungAufgebaut:
  tcpSocket = tcpServer->nextPendingConnection(); 
tcpSocket zeigt damit damit auf ein anderes Objekt, welches eben nicht mit deinen Slots verbunden ist.

kommen wir zum Qt-Teil: sowohl im Fortune-Server Beispiel:

Code: Alles auswählen

void Server::sendFortune()
 {
     ...
     QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
     connect(clientConnection, SIGNAL(disconnected()),
             clientConnection, SLOT(deleteLater()));
als auch in der Doku (http://doc.trolltech.com/4.5/qtcpserver ... Connection)
The socket is created as a child of the server
steht, dass der Socket von QTcpServer erstellt wird..

Ist das genug Begruendung?

Verfasst: 25. November 2009 15:14
von Toni0815
danke erstmal ...
ja, das macht sinn ... C++ Vorlesung ist schon ein paar Semester her :-)
ich werds gleich mal ändern