PDA

View Full Version : Connected client tree



cooler123
27th September 2007, 08:17
I want to show the connected client in tree. I am able to create the tree but i having problem to remove it when a client is disconnected.

Header file:


#ifndef SERVER_H
#define SERVER_H

#include <QDialog>
#include <QList>
#include <QHash>

class QLabel;
class QPushButton;
class QTcpSocket;
class QTextEdit;
class QTcpServer;
class QLineEdit;
class QBuffer;
class QStringList;
class QTreeWidget;
class QTreeWidgetItem;

class Server : public QDialog
{
Q_OBJECT

public:
Server(QWidget *parent = 0);
~Server();

protected:
void sendToClients(const QByteArray& line);

private slots:
//void sendMessage();
void slotStartButtonEnable();
void slotStartClicked();
void slotStopClicked();

void slotNewClient();
void slotClientDisconnected();

void slotSocketRead();

private:
QList<QTcpSocket*> connections;
QHash<QTcpSocket*, QBuffer*> buffers;
//QHash<QTcpSocket*, int> itemList;
QList<QTreeWidgetItem *> liClients;
QTreeWidget *twClientConnected;
QStringList slTreeLabel;
QLineEdit *lePort;
QLabel *lblPort;
QLabel *lblStatus;
QPushButton *btnQuit;
QPushButton *btnStart;
QPushButton *btnStop;
QTcpSocket *serverSocket;
QTcpServer *tcpServer;
QTextEdit *teMessage;
};

#endif


Source file:


#include <QtGui>
#include <QtNetwork>
#include <QBuffer>

#include <stdlib.h>

#include "server.h"

Server::Server(QWidget *parent)
: QDialog(parent)
{
lblPort = new QLabel(tr("Port:"));
lePort = new QLineEdit;
QRegExp regExp("[1-9][0-9]{0,4}");
lePort->setValidator(new QRegExpValidator(regExp, this));
lblPort->setBuddy(lePort);

btnStart = new QPushButton(tr("Start"));
btnStop = new QPushButton(tr("Stop"));
btnStart->setDisabled(true);
btnStop->setDisabled(true);

QGroupBox *gbServerControl = new QGroupBox(tr("Server Control"));
QGroupBox *gbMessage = new QGroupBox(tr("Message"));
QGroupBox *gbConnectedClient = new QGroupBox(tr("Connected Client"));

twClientConnected = new QTreeWidget();
twClientConnected->setColumnCount(2);
slTreeLabel << tr("Address") << tr("Port");
twClientConnected->setHeaderLabels(slTreeLabel);
twClientConnected->header()->setStretchLastSection(false);
twClientConnected->header()->setResizeMode(QHeaderView::Stretch);
liClients.append(new QTreeWidgetItem(twClientConnected, QStringList(QString(""))));
twClientConnected->addTopLevelItems(liClients);
twClientConnected->takeTopLevelItem(0);
twClientConnected->takeTopLevelItem(0);

lblStatus = new QLabel;
lblStatus->setFrameShape(QLabel::Panel);
lblStatus->setFrameShadow(QLabel::Sunken);
lblStatus->setText(tr("Ready"));

btnQuit = new QPushButton(tr("Quit"));
btnQuit->setAutoDefault(false);

teMessage = new QTextEdit(this);
teMessage->setReadOnly(true);

tcpServer = new QTcpServer(this);
serverSocket = new QTcpSocket(this);

connect(lePort, SIGNAL(textChanged(QString)), this, SLOT(slotStartButtonEnable()));
connect(btnStart, SIGNAL(clicked()), this, SLOT(slotStartClicked()));
connect(btnStop, SIGNAL(clicked()), this, SLOT(slotStopClicked()));
connect(btnQuit, SIGNAL(clicked()), this, SLOT(close()));
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(slotNewClient()));

QHBoxLayout *portLayout = new QHBoxLayout;
portLayout->addWidget(lblPort);
portLayout->addWidget(lePort);
portLayout->addStretch(1);
portLayout->addWidget(btnStart);
portLayout->addWidget(btnStop);
gbServerControl->setLayout(portLayout);

QHBoxLayout *clientLayout = new QHBoxLayout;
clientLayout->addWidget(twClientConnected);
gbConnectedClient->setLayout(clientLayout);

QHBoxLayout *messageLayout = new QHBoxLayout;
messageLayout->addWidget(teMessage);
gbMessage->setLayout(messageLayout);

QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(lblStatus, 1);
bottomLayout->addWidget(btnQuit);

QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(gbServerControl);
mainLayout->addWidget(gbConnectedClient);
mainLayout->addWidget(gbMessage);
mainLayout->addLayout(bottomLayout);
setLayout(mainLayout);

setWindowTitle(tr("TCP Chat Server - by Strife Low"));
lePort->setFocus();
}

Server::~Server()
{
}

void Server::sendToClients(const QByteArray& line)
{
foreach (QTcpSocket* connection, connections)
{
connection->write(line);
}
}

void Server::slotStartButtonEnable()
{
btnStart->setDisabled(false);
}

void Server::slotStartClicked()
{
lblStatus->setText(tr("Server starting"));
if (lePort->text() == "")
{
QMessageBox::critical(this, tr("Server"), tr("Please enter a valid port number"));
return;
}
if (lePort->text().toInt() > 65535)
{
lePort->setText("65535");
}

if (!tcpServer->listen(QHostAddress::Any, lePort->text().toInt())) {
QMessageBox::critical(this, tr("Server"), tr("Unable to start the server: %1.").arg(tcpServer->errorString()));
return;
}

btnStart->setDisabled(true);
btnStop->setDisabled(false);
lePort->setDisabled(true);
lblStatus->setText(tr("Server started at port %1").arg(tcpServer->serverPort()));
}

void Server::slotStopClicked()
{
btnStart->setEnabled(true);
btnStop->setEnabled(false);
lePort->setDisabled(false);

if (tcpServer->isListening())
{
tcpServer->close();
}

if (!connections.isEmpty())
{
foreach (QTcpSocket* connection, connections)
{
connection->close();
}
connections.clear();
}

if (serverSocket->isValid())
{
serverSocket->close();
}
lblStatus->setText(tr("Server stopped"));
}

void Server::slotNewClient()
{
if (!connections.isEmpty())
{
QByteArray msg = "New Client connected\n";
foreach (QTcpSocket* connection, connections)
{
connection->write(msg);
}
}

serverSocket = tcpServer->nextPendingConnection();
QStringList tmpClientAddressPort;
tmpClientAddressPort << tr("%1").arg(serverSocket->peerAddress().toString()) << tr("%1").arg(serverSocket->peerPort());
liClients.append(new QTreeWidgetItem(twClientConnected, tmpClientAddressPort));

//itemList.insert(serverSocket, (liClients.count() - 1));
//tmpClientAddressPort.clear();

connections.append(serverSocket);
QBuffer* buffer = new QBuffer(this);
buffer->open(QIODevice::ReadWrite);
buffers.insert(serverSocket, buffer);
connect(serverSocket, SIGNAL(disconnected()), this, SLOT(slotClientDisconnected()));
connect(serverSocket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
}

void Server::slotClientDisconnected()
{
QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
QBuffer* buffer = buffers.take(socket);
buffer->close();
buffer->deleteLater();
connections.removeAll(socket);
socket->deleteLater();

QByteArray msg = "Client disconnected\n";
foreach (QTcpSocket* connection, connections)
{
connection->write(msg);
}
}

void Server::slotSocketRead()
{
QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
QBuffer* buffer = buffers.value(socket);

qint64 bytes = buffer->write(socket->readAll());
buffer->seek(buffer->pos() - bytes);

while (buffer->canReadLine())
{
QByteArray line = buffer->readLine();
sendToClients(line);
teMessage->append(line);
}
}

// end of file

wysota
27th September 2007, 10:35
Hmm... why is serverSocket a member variable of the class?

Anyway... I don't see any code for removing an item from the tree, so I'm not surprised that entries are not removed :)

cooler123
27th September 2007, 10:57
Anyway... I don't see any code for removing an item from the tree, so I'm not surprised that entries are not removed :)

Thats what Im asking for. How am I suppose to remove the list.

wysota
27th September 2007, 11:07
QTreeWidget::takeTopLevelItem() and QList::removeAt() are certainly things to start with.

cooler123
28th September 2007, 02:58
=.=
I know which function to use to remove the list. But its not as easy as just using that function. Anyway, I will just keep trying myself 1st. Thanks for the reply.

cooler123
28th September 2007, 03:54
Still having problem in removing the list. Can anyone please help?


void Server::slotNewClient()
{
if (!connections.isEmpty())
{
QByteArray msg = "New Client connected\n";
foreach (QTcpSocket* connection, connections)
{
connection->write(msg);
}
}

serverSocket = tcpServer->nextPendingConnection();
QStringList tmpClientAddressPort;
tmpClientAddressPort << tr("%1").arg(serverSocket->peerAddress().toString()) << tr("%1").arg(serverSocket->peerPort());

itemList.insert(serverSocket, liClients.count());
liClients.append(new QTreeWidgetItem(twClientConnected, tmpClientAddressPort));

connections.append(serverSocket);
QBuffer* buffer = new QBuffer(this);
buffer->open(QIODevice::ReadWrite);
buffers.insert(serverSocket, buffer);
connect(serverSocket, SIGNAL(disconnected()), this, SLOT(slotClientDisconnected()));
connect(serverSocket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
}

void Server::slotClientDisconnected()
{
QMessageBox::critical(this, tr("Server"), tr("list count: %1.").arg(liClients.count()));
QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
for (int j = 0; j < (twClientConnected->topLevelItemCount()); j++)
{
if (liClients.at(itemList.take(socket)) == twClientConnected->topLevelItem(j))
{
twClientConnected->takeTopLevelItem(0);
liClients.removeAt(itemList.take(socket));
}
}
QBuffer* buffer = buffers.take(socket);
buffer->close();
buffer->deleteLater();
connections.removeAll(socket);
socket->deleteLater();

QByteArray msg = "Client disconnected\n";
foreach (QTcpSocket* connection, connections)
{
connection->write(msg);
}
}


itemList is:
QHash<QTcpSocket*, int> itemList;

mchara
28th September 2007, 06:48
I suggest you to use sender() method to find a singnal emitting object if signal connected to this slot is emited by socket object, and add additional QMap<QTcpSocket*, QTreeWidgetItem*>

It will be easy to find what exactly you should remove i think...

doctore
13th July 2009, 20:49
Hmm... why is serverSocket a member variable of the class?

Anyway... I don't see any code for removing an item from the tree, so I'm not surprised that entries are not removed :)

Firstly let start by saying that I am sorry for bumping such an old thread.

I'm interested in creating a simple chat application. There's a server to which the various clients connect.

My first doubt comes from the above quote: I don't understand the question. Is there any problem in having serverSocket as a member variable of the class server??
(I'm a very recent member of this board, but from what I could read I know that wysota as a great understanding not only of the Qt framework but also of the C++ language. So clearly there's some "problem" in there :D )


But that is not my only doubt. I was hoping someone could shed some light on the functioning of this kind of application.
What strategy do you recomend for someone who's new to the framework?

I intend to use tcpsockets and threads.
In my understanding, and after reading the various examples that came with the Qt documentation, I think that the application should perform as follows:

on one side there's the server. It has got a serversocket responsible for handling the connections of the new clients. And for each connection (client) i should start a new thread responsible for analising the type of communication: login, logout, message, etc. and send the corresponding answer to the client(s).

In the threadedfortuneserver example, each thread does the same thing: creates a new socket with the socket_descriptor passed by the server, sends the fortune and disconnects the socket.

I think that, in a chat application the sockets should remain open as long as the client is connected to server, correct?

Also, each socket should be able to access the list of the connected clients at a given time.
What data structure do you recommend that I should use that information (and also, what is the information that I should store), an HashMap<username, socket *>??

When a client sends data, that data is read and analised by the corresponding thread and, in case it's a message it's sent to all the other clients:

foreach(tcpsocket *, socket_list)
tcpsocket.write(msg)

is this correct??
Is there any other way (a better way) for doing this??

I really sorry for such a long post and I hope someone can help me.

Thank you all.