Seite 1 von 1

Wieder Problem mit TcpSocket / TcpServer

Verfasst: 21. Dezember 2009 19:33
von rurseekatze
Hallo,
ich habe wieder ein Problem mit meinem selbst geschriebenem Chatprogramm.

Das Verbinden klappt soweit, allerdings soll der Client die Verbindung auch trennen können. Dazu sendet er QUIT an den Server, wenn man auf Trennen klickt. Der Server registriert, dass Daten ankommen, zeigt dann aber nichts an.

SERVER

italk.h

Code: Alles auswählen

#ifndef italk_H
#define italk_H

#include "ui_italk.h"
#include "logfile.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <QtGui>
#include <QString>
#include <QTranslator>
#include <QTimer>
#include <QKeyEvent>
#include <QtNetwork>
#include <QDataStream>
#include <QByteArray>
#include <QHostAddress>
#include <QAbstractSocket>
#include <QTcpSocket>
#include <QTcpServer>

using namespace std;

class italk : public QMainWindow, public Ui::MainWindow
{
	Q_OBJECT
	public:
		italk(QMainWindow *parent = 0);
		~italk();
	private slots:
		void showAbout();
		void setServerOnline();
		void newClient();
		void recieveCommand();
	private:
		QTcpServer *dataServer;
		QTcpServer *commandServer;
		QTcpSocket *commandSocket;
		QTcpSocket *dataSocket;
};

#endif
italk.cpp

Code: Alles auswählen

#include "italk.h"

italk::italk(QMainWindow *parent) : QMainWindow(parent)
{
	setupUi(this);
	connect(actionAbout, SIGNAL (triggered()), this, SLOT(showAbout()));
	connect(actionAboutQt, SIGNAL (triggered()), qApp, SLOT(aboutQt()));
	setServerOnline();
}

italk::~italk()
{
}

void italk::showAbout()
{
	QMessageBox aboutBox;
	aboutBox.about(this, QString(tr("Info")), QString(tr("Infotext")));
}

void italk::setServerOnline()
{
	dataServer = new QTcpServer(this);
	commandServer = new QTcpServer(this);
	dataServer->listen(QHostAddress::Any,51239);
	commandServer->listen(QHostAddress::Any,51238);
	connect(commandServer, SIGNAL(newConnection()), this, SLOT(newClient()));
}

void italk::newClient()
{
	cout << "Neuer Client hat die Verbindung aufgenommen" << endl;
	if (commandServer->hasPendingConnections())
	{
		commandSocket = commandServer->nextPendingConnection();
		connect(commandSocket, SIGNAL(readyRead()), this, SLOT(recieveCommand()));
	}
}

void italk::recieveCommand()
{
	while (commandSocket->canReadLine())
	{
		if(QString(commandSocket->readLine()).startsWith("NEW"))
		{
			cout << "Neuer Client sendete NEW und versucht, sich am Server anzumelden..." << endl;
			commandSocket->write("OK\n");
			commandSocket->flush();
			cout << "Client hat sich erfolgreich am Server angemeldet." << endl;
		}
		if(QString(commandSocket->readLine()).startsWith("QUIT"))
		{
			cout << "Client sendete QUIT und wird vom Server getrennt..." << endl;
			commandSocket->write("QUIT\n");
			commandSocket->flush();
			cout << "Client wurde vom Server getrennt." << endl;
		}
		else
			cout << "unbekannter Befehl: " << QString(commandSocket->readAll()).toStdString() << endl;
	}
}

CLIENT

italk.h

Code: Alles auswählen

#ifndef italk_H
#define italk_H

#include "ui_italk.h"
#include "settings.h"
#include "connections.h"
#include "connectionssettings.h"
#include "logfile.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <QtGui>
#include <QTimer>
#include <QString>
#include <QTranslator>
#include <QKeyEvent>
#include <QtNetwork>
#include <QDataStream>
#include <QByteArray>
#include <QHostAddress>
#include <QAbstractSocket>
#include <QTcpSocket>

using namespace std;

class italk : public QMainWindow, public Ui::MainWindow
{
	Q_OBJECT
	public:
		italk(QMainWindow *parent=0);
		~italk();
	private slots:
	void connectToServer();
		void setUpConnection();
		void resetConnection();
		void quitFromServer();
		void disconnectFromServer();
		void showConnectionError(QAbstractSocket::SocketError socketError);
		void sendData();
		void readData();
		void sendCommand();
		void readCommand();
		void showNotification(int event, QString user);
		void keyPressEvent(QKeyEvent *keyEvent);
		void showSettings();
		void showConnections();
		void showLogfile();
		void showAbout();
		void loadSettings();
		void loadConnections();
	private:
		QTcpSocket *dataSocket;
		QTcpSocket *commandSocket;
		QTimer *serverTimeout;
		bool enterFocus;
		QString setting[9];
		QString connection[10][3];
		int dataSet;
		int entry;
};

#endif
italk.cpp

Code: Alles auswählen

#include "italk.h"

italk::italk(QMainWindow *parent) : QMainWindow(parent)
{
	setupUi(this);
	loadSettings();
	loadConnections();
	connect(actionSettings, SIGNAL (triggered()), this, SLOT(showSettings()));
	connect(actionConnections, SIGNAL (triggered()), this, SLOT(showConnections()));
	connect(actionLogfile, SIGNAL (triggered()), this, SLOT(showLogfile()));
	connect(actionAbout, SIGNAL (triggered()), this, SLOT(showAbout()));
	connect(actionAboutQt, SIGNAL (triggered()), qApp, SLOT(aboutQt()));
	connect(actionQuit, SIGNAL (triggered()), qApp, SLOT(quit()));
	connect(connectButton, SIGNAL (clicked()), this, SLOT(connectToServer()));
	connect(disconnectButton, SIGNAL (clicked()), this, SLOT(quitFromServer()));
	connect(sendButton, SIGNAL (clicked()), this, SLOT(sendData()));
	sendButton->setEnabled(FALSE);
	runButton->setEnabled(FALSE);
	disconnectButton->setEnabled(FALSE);
	messageEdit->setEnabled(FALSE);
	runEdit->setEnabled(FALSE);
	messageTextBox->setReadOnly(TRUE);
	nicknameEdit->setFocus();
	enterFocus=TRUE;
	connectionStatusBar->showMessage(QString(tr("Nicht verbunden.")));
}

italk::~italk()
{
}

void italk::connectToServer()
{
	if (nicknameEdit->text().trimmed().length()<=64 && !nicknameEdit->text().trimmed().isEmpty())
	{
		QString command("NEW###"+nicknameEdit->text().trimmed()+"\n");
		commandSocket = new QTcpSocket(this);
		serverTimeout = new QTimer(this);
		connect(commandSocket, SIGNAL(readyRead()), this, SLOT(readCommand()));
		connect(commandSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(showConnectionError(QAbstractSocket::SocketError)));
		connect(serverTimeout, SIGNAL(timeout()), this, SLOT(resetConnection()));
		commandSocket->connectToHost("127.0.0.1", 51238);
		connectionStatusBar->showMessage(QString(tr("Anfrage an Server %1.")) .arg(commandSocket->peerName()));
		if (commandSocket->waitForConnected(10000))
		{
			commandSocket->write(command.toAscii().data());
			serverTimeout->start(10000);
		}
		else
		{
			QMessageBox errorBox;
			errorBox.critical(this, QString(tr("Fehler")), QString(tr("<b>Server antwortet nicht</b><br/>Der Server hat in der gegebenen Zeit nicht geantwortet. Bitte versuchen Sie es sp&auml;ter noch einmal.")));
			resetConnection();
		}
	}
	else
	{
		if(nicknameEdit->text().trimmed().isEmpty())
		{
			QMessageBox errorBox;
			errorBox.warning(this, QString(tr("Warnung")), QString(tr("<b>Eingabefeld leer</b><br/>Sie haben keinen Nicknamen angegeben.")));
		}
		else
		{
			QMessageBox errorBox;
			errorBox.warning(this, QString(tr("Warnung")), QString(tr("<b>Nickname zu lang</b><br/>Der eingegebene Nickname ist zu lang.")));
		}
	}
}

void italk::setUpConnection()
{
	serverTimeout->stop();
	connectionStatusBar->showMessage(QString(tr("Server hat geantwortet...")));
	dataSocket = new QTcpSocket(this);
	connect(dataSocket, SIGNAL(readyRead()), this, SLOT(readData()));
	dataSocket->connectToHost("127.0.0.1", 51239);
	connectionStatusBar->showMessage(QString(tr("Verbindung hergestellt mit %1.")) .arg(dataSocket->peerName()));
	sendButton->setEnabled(TRUE);
	runButton->setEnabled(TRUE);
	disconnectButton->setEnabled(TRUE);
	connectButton->setEnabled(FALSE);
	messageEdit->setEnabled(TRUE);
	runEdit->setEnabled(TRUE);
	nicknameEdit->setEnabled(FALSE);
	connectionsBox->setEnabled(FALSE);
	messageEdit->setFocus();
	enterFocus=FALSE;
}

void italk::resetConnection()
{
	// erstellte Objekte löschen bzw. zurücksetzten
	// Statusbar eintrag ändern
}

void italk::quitFromServer()
{
	commandSocket->write("QUIT\n");
	commandSocket->flush();
}

void italk::disconnectFromServer()
{
	dataSocket->close();
	commandSocket->close();
	connectionStatusBar->showMessage(QString(tr("Verbindung getrennt.")));
	nicknameEdit->clear();
	messageEdit->clear();
	runEdit->clear();
	messageTextBox->clear();
	sendButton->setEnabled(FALSE);
	runButton->setEnabled(FALSE);
	disconnectButton->setEnabled(FALSE);
	connectButton->setEnabled(TRUE);
	messageEdit->setEnabled(FALSE);
	runEdit->setEnabled(FALSE);
	nicknameEdit->setEnabled(TRUE);
	connectionsBox->setEnabled(TRUE);
	nicknameEdit->setFocus();
	enterFocus=TRUE;
}

void italk::showConnectionError(QAbstractSocket::SocketError socketError)
{
	resetConnection();
	switch (socketError)
	{
		case QAbstractSocket::RemoteHostClosedError:
			break;
		case QAbstractSocket::HostNotFoundError:
			QMessageBox::information(this, tr("1"), tr("The host was not found. Please check the host name and port settings."));
			break;
		default:
			QMessageBox::information(this, tr("3"), tr("The following error occurred: %1.") .arg(dataSocket->errorString()));
	}
}

void italk::sendData()
{
	if (messageEdit->text().trimmed().length()<=1024 && !messageEdit->text().trimmed().isEmpty())
	{
		QString message(messageEdit->text().trimmed());
		dataSocket->write(message.toAscii());
		dataSocket->flush();
		messageEdit->clear();
	}
	else
	{
		if(messageEdit->text().trimmed().isEmpty())
		{
			QMessageBox errorBox;
			errorBox.warning(this, QString(tr("Warnung")), QString(tr("<b>Eingabefeld leer</b><br/>Sie haben keine Nachricht eingegeben, die Sie versenden wollen.")));
		}
		else
		{
			QMessageBox errorBox;
			errorBox.warning(this, QString(tr("Warnung")), QString(tr("<b>Nachricht zu lang</b><br/>Sie haben eine zu lange Nachricht eingegeben.")));
		}
	}
}

void italk::readData()
{
	QString message(dataSocket->readAll());
	if (message.endsWith("%%%") && !message.isEmpty())
	{
		message.remove(-3, 3 );
<b>"+self.data[1]+": </b>"+self.data[0]
		message="<br/>"+message;
		messageTextBox->insertHtml(message);
	}
}

void italk::sendCommand()
{

}

void italk::readCommand()
{
	while (commandSocket->canReadLine())
	{
		QString command(commandSocket->readLine());
		if (command.startsWith("OK"))
			setUpConnection();
		if (command.startsWith("NEW"))
			newUser(command);
		if (command.startsWith("AWAY"))
			userAway(command);
		if (command.startsWith("QUIT"))
			disconnectFromServer();
	}
}

void italk::showNotification(int event, QString user)
{
	if (event>=0 && event<=3)
	{
		if (setting[event+1]=="1")
		{
			if (setting[6]=="1")
			{
				cout << "Hinweiston" << endl;
			}
			if (setting[5]=="0")
			{
				cout << "KDE" << endl;
			}
			if (setting[5]=="1")
			{
				cout << "Gnome" << endl;
			}
		}
	}
}

void italk::keyPressEvent(QKeyEvent *keyEvent)
{
	if (keyEvent->key() == Qt::Key_Return)
	{
		if(enterFocus==TRUE)
		{
			connectToServer();
		}
		else
		{
			sendData();
		}
	}
}

void italk::showSettings()
{
	settings dialog;
	dialog.exec();
}

void italk::showConnections()
{
	connections dialog;
	dialog.exec();
}

void italk::showLogfile()
{
	logfile dialog;
	dialog.exec();
}

void italk::showAbout()
{
	QMessageBox aboutBox;
	aboutBox.about(this, QString(tr("Info")), QString(tr("Infotext")));
}

void italk::loadSettings()
{
	dataSet=0;
	QFile settingsFile("settings");
	if (!settingsFile.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QMessageBox errorBox;
		errorBox.critical(this, QString(tr("Fehler")), QString(tr("<b>Einstellungsdatei nicht gefunden</b><br/>Die Einstellungsdatei konnte nicht gefunden werden. Daher werden die Standarteinstellungen verwendet und eine neue Datei mit den Standarteinstellungen angelegt.")));
		setting[0]=QString("0");
		setting[1]=QString("0");
		setting[2]=QString("0");
		setting[3]=QString("0");
		setting[4]=QString("0");
		setting[5]=QString("0");
		setting[6]=QString("0");
		setting[7]=QString("2");
	}
	QTextStream in(&settingsFile);
	while (!in.atEnd())
	{
		QString line = in.readLine();
		setting[dataSet]=line;
		dataSet++;
	}
	settingsFile.close();
}

void italk::loadConnections()
{
	dataSet=0;
	QFile connectionsFile("connections");
	if (!connectionsFile.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QMessageBox errorBox;
		errorBox.critical(this, QString(tr("Fehler")), QString(tr("<b>Verbindungsdatei nicht gefunden</b><br/>Die Verbindungsdatei konnte nicht gefunden werden. Daher wird eine neue Datei angelegt.")));
	}
	QTextStream in(&connectionsFile);
	while (!in.atEnd())
	{
		for(entry=0; entry<3; entry++)
		{
			QString line = in.readLine();
			connection[dataSet][entry]=line;
		}
		connectionsBox->insertItem(dataSet, QString(connection[dataSet][0]));
		dataSet++;
	}
	connectionsFile.close();
	connectionsBox->insertItem(dataSet, QString(tr("-")));
	connectionsBox->insertItem(dataSet+1, QString(tr("Server nehmen, der online ist...")));
	connectionsBox->insertItem(dataSet+2, QString(tr("Manuell mit Server verbinden...")));
}

Ich habe einfach keine Ahnung, warum das Versenden einmal funktioniert, und dann nicht mehr.

Verfasst: 22. Dezember 2009 09:28
von solarix
Das:

Code: Alles auswählen

void italk::recieveCommand()
{
   while (commandSocket->canReadLine())
   {
      if(QString(commandSocket->readLine()).startsWith("NEW"))
        ...
      if(QString(commandSocket->readLine()).startsWith("QUIT"))
        ...
ist nicht besonders clever.... oder?

Dazu noch folgende Hinweise:

- OOP erlaubt auch die Verwendung _mehrerer_ Klassen.. :wink: Dein Projekt wäre erheblich übersichtlicher, wenn der Netzwerkcode nicht in der GUI enthalten wäre...

- verwende sinnvolle Klassennamen.. weil bei dir beide Klassen "italk" heissen kann der Code kein Mensch vernünftig lesen ("ItalkServer", "ItalkClient"?)

- für Debug-Ausgaben ist "qDebug()" besser als "cout()"...

- verwende nicht nur bei den Klassen sinnvolle (wiedererkennbare) Namen, sondern auch bei den Methoden (einmal nennst du die Methode "readCommand()", ein andermal "recieveCommand()" (übrigens mit Schreibfehler)).

hth..

Verfasst: 22. Dezember 2009 16:09
von rurseekatze
Problem gelöst, hab es jetzt so gemacht:

Code: Alles auswählen

void italk::receiveCommand()
{
	while (commandSocket->canReadLine())
	{
		QString command=commandSocket->readLine();
		if(command.startsWith("NEW"))
		{
			cout << "Neuer Client sendete NEW und versucht, sich am Server anzumelden..." << endl;
			commandSocket->write("OK\n");
			commandSocket->flush();
			cout << "Client hat sich erfolgreich am Server angemeldet." << endl;
		}
		else if(command.startsWith("QUIT"))
		{
			cout << "Client sendete QUIT und wird vom Server getrennt..." << endl;
			commandSocket->write("QUIT\n");
			commandSocket->flush();
			cout << "Client wurde vom Server getrennt." << endl;
		}
		else
			cout << "unbekannter Befehl: " << command.toStdString() << endl;
	}
}
solarix hat geschrieben:Das:

Code: Alles auswählen

void italk::recieveCommand()
{
   while (commandSocket->canReadLine())
   {
      if(QString(commandSocket->readLine()).startsWith("NEW"))
        ...
      if(QString(commandSocket->readLine()).startsWith("QUIT"))
        ...
ist nicht besonders clever.... oder?
Hab es jetzt wie oben verbessert.

solarix hat geschrieben: - OOP erlaubt auch die Verwendung _mehrerer_ Klassen.. :wink: Dein Projekt wäre erheblich übersichtlicher, wenn der Netzwerkcode nicht in der GUI enthalten wäre...
Bei einem solch (relativ) kleinen Projekt ist mir das egal. Ich find mich (noch) gut zurecht.
solarix hat geschrieben: - verwende sinnvolle Klassennamen.. weil bei dir beide Klassen "italk" heissen kann der Code kein Mensch vernünftig lesen ("ItalkServer", "ItalkClient"?)
Stimmt natürlich, aber es sind zwei getrennte Projekte, daher kommt es da zu keiner Verwechselung.
solarix hat geschrieben: - für Debug-Ausgaben ist "qDebug()" besser als "cout()"...
Na ja, ist vielleicht besser, aber mit cout gehts auch wunderbar!
solarix hat geschrieben: - verwende nicht nur bei den Klassen sinnvolle (wiedererkennbare) Namen, sondern auch bei den Methoden (einmal nennst du die Methode "readCommand()", ein andermal "recieveCommand()" (übrigens mit Schreibfehler)).
Schreibfehler ist bereits behoben, aber ich finde die Funktionsnamen sehr übersichtlich.[/code]