PDA

View Full Version : Multiple TcpClients to multiple Servers



sa5webber
20th June 2012, 08:23
It's not done obviously but I'm trying to put together the basics for a test executive that creates multiple client connections where each client connects to a different server. So I've created a ConnectionMgr class that is supposed to handle each of the connections using a map that ties the serverName to a TcpClient class. In the TcpClient class I create the connections and the signals for receiving data.

My current problem is that I'm getting compile errors in the TcpClient class that says I have multiple definitions of ReadTcpData, Connected, Disconnected, etc. It appears that its having problems with the fact that I'm creating multiple instances of the TcpClient class.

.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.o:-1: In function `TcpClient::ReadTcpData()':
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.cpp:104: multiple definition of `TcpClient::ReadTcpData()'
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/../B2TestSet/tcpclient.cpp:37: error: first defined here
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.o:-1: In function `TcpClient::Connected()':
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.cpp:110: multiple definition of `TcpClient::Connected()'
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/../B2TestSet/tcpclient.cpp:47: error: first defined here
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.o:-1: In function `TcpClient::Disconnected()':
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.cpp:116: multiple definition of `TcpClient::Disconnected()'
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/../B2TestSet/tcpclient.cpp:52: error: first defined here
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.o:-1: In function `TcpClient::ErrHandler(QAbstractSocket::SocketErro r)':
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/moc_tcpclient.cpp:122: multiple definition of `TcpClient::ErrHandler(QAbstractSocket::SocketErro r)'
.../QtProjects/B2TestSet-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug/../B2TestSet/tcpclient.cpp:57: error: first defined here

I'm not sure if this is the best way to manage multiple client connections. If somebody has a better idea for a simpliar approach I would be interested.



mainwindow.cpp
***************************************

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

....

// Instantiate a connection manager
ConnectionMgr *conMgr = new ConnectionMgr(_serverMap, this);
conMgr->ConnectAll();
}

connectionmgr.cpp
***************************************
#include "connectionMgr.h"

ConnectionMgr::ConnectionMgr(std::map<string, string>serverMap, QObject *parent) :
QObject(parent)
{
_serverMap = serverMap;
}

void ConnectionMgr::ConnectAll()
{
map<string, string>::iterator it;

// Attempt to connect to the list of servers
// and add each client to a client map
for(it = _serverMap.begin(); it != _serverMap.end(); it++)
{
QString srvName = QString::fromStdString((*it).first);
QString srvInfo = QString::fromStdString((*it).second);
NetInfo netInfo = ParseSrvInfo( srvInfo );

TcpClient *tcpClient = new TcpClient(netInfo.ipAddr, netInfo.port, this);
_clientMap[srvName.toStdString()] = tcpClient;
}
}

connectionmgr.h
***************************************
#ifndef CONNECTIONMGR_H
#define CONNECTIONMGR_H

#include "qstring.h"
#include "qtcpsocket.h"
#include "qstringlist.h"
#include "tcpclient.h"

using namespace std;

struct NetInfo
{
QString ipAddr;
int port;
};

class ConnectionMgr : public QObject
{
Q_OBJECT

public:
ConnectionMgr(map<string, string>serverMap, QObject *parent = 0);
~ConnectionMgr();

TcpClient *GetTcpClient(QString srv);
void ConnectAll();

private:
map<string, string>_serverMap;
map<string, TcpClient*>_clientMap;

NetInfo GetSrvInfo( QString srvName );
NetInfo ParseSrvInfo( QString srvInfo );
};

#endif // CONNECTIONMGR_H

tcpclient.cpp
***************************************
#include "tcpclient.h"

TcpClient::TcpClient(QObject *parent) :
QObject(parent)
{
_isConnected = false;
}

TcpClient::TcpClient(QString ipAddr, int port, QObject *parent) :
QObject(parent)
{
_isConnected = false;
Connect(ipAddr, port);
}

void TcpClient::Connect(QString ipAddr, int port)
{
// When it did compile a segment fault occurs when the connect is called
_tcpClient = new QTcpSocket();
connect(_tcpClient, SIGNAL(readyRead()),SLOT(ReadTcpData()));
connect(_tcpClient, SIGNAL(connected()),SLOT(Connected()));
connect(_tcpClient, SIGNAL(disconnected()),SLOT(Disconnected()));
connect(_tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),
SLOT(ErrHandler(QAbstractSocket::SocketError)));
_tcpClient->connectToHost(ipAddr, port);
}

qint64 TcpClient::Write(const char* data)
{
qint64 bytesWritten = 0;
if(_tcpClient->waitForConnected(5000))
bytesWritten = _tcpClient->write(data);

return bytesWritten;
}

void TcpClient::ReadTcpData()
{
QByteArray data = _tcpClient->readAll();
// Need to probably add an emit of some sort here
}

bool TcpClient::IsConnected()
{
return _isConnected;
}

void TcpClient::Connected()
{
_isConnected = true;
}

void TcpClient::Disconnected()
{
_isConnected = false;
}

void TcpClient::ErrHandler(QAbstractSocket::SocketError socketError)
{
QString errStr;
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
errStr = "TcpClient error: The remote host closed the connection.";
break;
case QAbstractSocket::HostNotFoundError:
errStr = "TcpClient error: The host was not found.";
break;
case QAbstractSocket::ConnectionRefusedError:
errStr = "TcpClient error: The connection was refused by the peer.";
break;
default:
errStr = tr("TcpClient error: %1").arg(_tcpClient->errorString());
break;
}
}

void TcpClient::Disconnect()
{
_tcpClient->disconnect();
}

tcpclient.h
***************************************
#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QObject>
#include "qtcpsocket.h"

using namespace std;

class TcpClient : public QObject
{
Q_OBJECT
public:
explicit TcpClient(QObject *parent = 0);
explicit TcpClient(QString ipAddr, int port, QObject *parent = 0);
~TcpClient();

void Connect(QString ipAddr, int port);
bool IsConnected();
qint64 Write(const char* data);
void Disconnect();

signals:
void ReadTcpData();
void Connected();
void Disconnected();
void ErrHandler(QAbstractSocket::SocketError socketError);

public slots:

private:
QTcpSocket* _tcpClient;
bool _isConnected;
};

#endif // TCPCLIENT_H

ChrisW67
20th June 2012, 08:47
TcpClient::ReadTcpData(), Connected(), Disconnected() and ErrHandler() are declared as signals not slots: you do not provide implementations for signals because they are generated by moc. I think you intend these to be slots based on earlier attempts to connect to them as slots.

sa5webber
20th June 2012, 15:53
You're absolutely right. Thanks so much for taking the time to look at it.

I have another question if you don't mind. In the TcpClient class readyRead() signal is there a way of sending the client socket connection that received/generated the readyRead() to the slot that receives the signal? In other words I would like to have a common slot routine which not only reads the receiving message but knows which server connection it received the data on for communicating back to that server. As I have it now each TcpClient connection has its own slot routine and from this I was planning on emitting/relaying the client connection and data to the subscriber. Is what I'm doing the best way of accomplishing what I want?

Also if I continue with my current approach, I would like to connect, I guess effectively signals from each TcpClient object to a slot in MainWindow. Does this have to be relayed or re-emitted through ConnectionMgr or is there a more efficient way to connect directly to emits from the TcpClient to slots in the MainWindow.

Thanks again for your time.

ChrisW67
21st June 2012, 00:15
There is no TcpClient::readyRead() signal declared that I can see. Assuming you mean readyRead() emitted by TcpClient::_tcpClient then you can connect it to a slot in TcpClient that simply emits a signal, defined on TcpClient, that carries the TcpClient address to any listeners. Passing the QTcpSocket's address would allow external access to the private member variable: possibly not a good idea..

sa5webber
21st June 2012, 01:51
Yes the readyRead() signal is not defined in TcpClient.h segment here. When I entered this listing I was still trying to resolve the errors I had asked about with this listing which as you pointed out was due to me still getting my head around the signal/slot naming convention. That mistake has definitely made that clearer to me.

In any event I have what I was asking about working with the ConnectionMgr receiving and re-emitting each of the TcpClient signals to the MainWindow. So in this case I might have actually answered my own question. Again I can't tell you how much I appreciate and admire all of the help people like you provide to this forum. I'm sure I'll have other questions as I forage my way through QT. Thanks for being there.