PDA

View Full Version : Implementing a Fortune Server



KeineAhnung
17th June 2014, 15:17
Hi there,

I am trying to add a server feature to my project. My idea was to have the clients sent information to the server. These information are processed and displayed by the server. The server user needs to react on the information he gets from the clients or request more information. Actually I think the whole thing can be seen as an instant messaging system since server and client will be handled by a user.

To realize that I looked around and found the fortune server/client examples (http://qt-project.org/doc/qt-4.8/network-fortuneserver.html). So I just copied the code from server.h and server.cpp into my code and added "network" to Qt.
The code compiles without any errors but the program crashes right at the start. I do not get any error messages besides that it crashed.
This is the line that makes the trouble:


// Start Server
QNetworkConfigurationManager manager;
qDebug() << "Hello Server!";
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequir ed) {
qDebug() << "Are you starting";

Hello Server appears in the console but are you starting not. The else statement is also not triggered. So I assume the if statement makes the trouble.
qDebug() << manager.capabilities(); gives me QFlags(0x10)

What could I have possibly missed? Where should I start looking?
The software I am writing for shall run on OS X. If I am running the example as stand alone it worked after I added #include <QWidget> to the .h file.

Any hints would be appreciated.

KeineAhnung
18th June 2014, 09:19
Okay, I tried several things to get what I want but so far it is only partly working. I try to peace together the parts that I found on the web to get a client and a sever code that could be added to an existing project. Goal is to get two programs talking to each other. My sources are the link and the chatterbox (http://thesmithfam.org/blog/2009/07/09/example-qt-chat-program/) example. Hopefully some of you can contribute to it so that there is an example code at the end of this.

First the sever part. Code needs to be added to your project:
xy.pro (Server)


QT +=network


mainwindow.h (Server)


#include <QTcpSocket>
#include <QTcpServer>
#include <QNetworkInterface>
#include <QRegExp>
[...]
private slots:
void readyRead();
void disconnected();
void incomingConnection();

[...]
private:
//Server
QTcpServer *tcpServer;
QSet<QTcpSocket*> clients;
QMap<QTcpSocket*,QString> users;


mainwindow.cpp (Server)


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow){

ui->setupUi(this);

// Server
tcpServer = new QTcpServer(this);
if (!tcpServer->listen()) {
QMessageBox::critical(this, tr("Server Error"), tr("The server could not be started: %1.").arg(tcpServer->errorString()));
close();
return;
}

QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost && ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}

// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
// output you need to know the IP and the port the server listens to
qDebug() << ipAddress << tcpServer->serverPort();
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
[...]
}

// Server Functions and Slots
void MainWindow::incomingConnection(){
QTcpSocket *client = tcpServer->nextPendingConnection();
// client->setSocketDescriptor(socketfd); <- this is from the original code not sure if I need it or not and where to get the socketfd.
clients.insert(client);

qDebug() << "New client from:" << client->peerAddress().toString();

connect(client, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
}

void MainWindow::readyRead(){
QTcpSocket *client = (QTcpSocket*)sender();
while(client->canReadLine()) {
QString line = QString::fromUtf8(client->readLine()).trimmed();
qDebug() << "Read line:" << line;

QRegExp meRegex("^/me:(.*)$");

if(meRegex.indexIn(line) != -1){
QString user = meRegex.cap(1);
users[client] = user;
}else if(users.contains(client)){
QString message = line;
QString user = users[client];
qDebug() << "User:" << user;
qDebug() << "Message:" << message;
}else{
qWarning() << "Got bad message from client:" << client->peerAddress().toString() << line;
}
}
}

void MainWindow::disconnected(){
QTcpSocket *client = (QTcpSocket*)sender();
clients.remove(client);
QString user = users[client];
users.remove(client);
qDebug() << "Lost connection to Client: << user << client->peerAddress().toString());
}
// End Server Functions and Slots


And the code on the client side:
xy.pro


QT += network

mainwindow.h (Client)


#include <QTcpSocket>
#include <QtNetwork>
[...]
private slots:
void connectToServer();
void readyRead();
void connected();
void displayError(QAbstractSocket::SocketError socketError);
void enableGetFortuneButton();
void sessionOpened();

void on_connectButton_clicked();
[...]

private:
Ui::MainWindow *ui;

// Client
QTcpSocket *tcpSocket;
QNetworkSession *networkSession;
void sentMSG(QString msg);

mainwindow.ui (Client) needs to have to inputs for the Server IP and the server port plus an connect button. To display messages a window is also required.

mainwindow.cpp (Client)


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow){
ui->setupUi(this);
// Setup Client
// find out which IP to connect to
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();

tcpSocket = new QTcpSocket(this);

connect(ui->hostLineEdit, SIGNAL(textChanged(QString)), this, SLOT(enableConnectButton()));
connect(ui->portLineEdit, SIGNAL(textChanged(QString)), this, SLOT(enableConnectButton()));
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readReady()));
connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
}

// Network Functions
void MainWindow::on_connectButton_clicked(){
connectToServer();
}

void MainWindow::sentMSG(QString msg){
msg = msg.trimmed();
qint64 bytes = 0;
if(!msg.isEmpty())
bytes = tcpSocket->write(msg.toUtf8());

if(bytes == -1)
qDebug() << "Error while sending data";
else
qDebug() << bytes << "sent.";
}
void MainWindow::connected(){
// And send our username to the chat server.
sentMSG("/me:"+clientName+"\n");
}
// End Network Functions
//Slots
void MainWindow::connectToServer(){
ui->connectButton->setEnabled(false);
tcpSocket->abort();
tcpSocket->connectToHost(ui->hostLineEdit->text(), ui->portLineEdit->text().toInt());
}

void MainWindow::readReady(){
while(tcpSocket->canReadLine()){
QString line = QString::fromUtf8(tcpSocket->readLine()).trimmed();

// Using QRegExp allows us to specify different types of messages. Normal messges look like this: "username:The message"
QRegExp messageRegex("^([^:]+):(.*)$");

if(messageRegex.indexIn(line) != -1){
QString user = messageRegex.cap(1);
QString message = messageRegex.cap(2);

ui->log->append("<b>" + user + "</b>: " + message);
}else{
aDebug() << "Bad message.";
}
}
}
void MainWindow::displayError(QAbstractSocket::SocketEr ror socketError){
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The host was not found. Please check the "
"host name and port settings."));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the host name and port "
"settings are correct."));
break;
default:
QMessageBox::information(this, tr("Fortune Client"),
tr("The following error occurred: %1.")
.arg(tcpSocket->errorString()));
}

ui->connectButton->setEnabled(true);
}

void MainWindow::enableConnectButton(){
ui->connectButton->setEnabled(!ui->hostLineEdit->text().isEmpty() && !ui->portLineEdit->text().isEmpty());

}


So for the code can connect to the server but messages from the client to the server are ignored. I get the information that the message was sent but the server does not react to it.

There are probably tons of errors in here. So I would be grateful if some could help me clean up.

Thanks.

anda_skoa
18th June 2014, 10:42
The incomingConnection() slot is wrong.

It creates an unconnected QTcpSocket, it should be retrieving the server side end point of the incoming client connection using QTcpServer::nextPendingConnection().

Basically instead of


QTcpSocket *client = new QTcpSocket(this);

you have


QTcpSocket *client = tcpServer->nextPendingConnection();


Cheers,
_

KeineAhnung
18th June 2014, 13:51
Thank you for the hint. I corrected that in the previous post. Now the line qDebug() "New client from:"... gives me the correct IP of the client but messages are still ignored.

If line 44 in mainwindow.cpp (Client) put out "24 bytes" does this mean that the server received the message or only the the whole message was sent by the client?

[edit]
Okay, I build some minimal programs using the code. So far they work with one exception, message from the client to the server are ignored. I think there is something wrong with connect(client, SIGNAL(readyRead()), this, SLOT(readyRead())); but I cannot figure out what. Can anyone tell me where the mistake is?

Thanks

KeineAhnung
18th June 2014, 21:47
I have tried a little bit more to figure this out but without any success. How can I trouble shout this? Is it possible to see what information is sent to the server and what the server does with it? How can I check that the right connection is made?

Thanks

StrikeByte
19th June 2014, 11:39
The problem is not with the server, its in the client. in the function sentMSG you use msg = msg.trimmed() this also removes the \n
socket->canReadLine() expects an \n else it stays false.
to solve it change the trim or add an \n after the trim, if you dont want to use line endings you should use socket->bytesAvailable() instead of socket->canReadLine()

KeineAhnung
20th June 2014, 17:05
Thanks, you were right. Good that I know what I am doing here ;-)