Клиентская программа падает при подключении к серверу после отключения Qt
Я реализую клиентскую часть сервера TCP-Ip на основе Qt, но программа завершается сбоем, когда я закрываю соединение и начинаю снова, нажимая кнопку подключения.
Обзор проекта. У меня есть Qwidget (основное приложение) с 2 lineEdit для пользователя, чтобы ввести номер порта и IP-адрес сервера. Он также имеет 2 кнопки, которые подключаются к серверу и отключаются. При нажатии кнопки подключения он вызывает конструктор клиентского сокета и вызывает connectToHost.
Тестирование: пробное тестирование на одном компьютере с сервером, работающим на порту 6000 и IP-адресом 127.0.0.1.
Проблема: Когда я запускаю клиентское приложение. И введите номер порта, адрес и нажмите кнопку подключения, он успешно подключается. И я могу успешно написать на сервер. Затем я нажимаю кнопку "Отключить", и она успешно отключается, но после этого, если я снова подключаюсь, нажимая кнопку "Подключить", происходит сбой. Я знаю, что проблема с tcpSocket, но не знаю, как это исправить.
clientAgent (приложение QWidget).h
namespace Ui {
class ClientAgent;
}
class ClientAgent : public QWidget
{
Q_OBJECT
public:
explicit ClientAgent(QWidget *parent = 0);
~ClientAgent();
private:
Ui::ClientAgent *ui;
ClientForTest *tcpSockForTest;
// Qwidget declartion
below
....
private slots:
void startClient();
void stopClient();
public slots:
void getCliReqTextChanged();
};
ClientAgent.cpp
ClientAgent::ClientAgent( QWidget *parent ) :
QWidget(parent),
ui( new Ui::ClientAgent )
{
// Widget declartion and initilisation
// Layout design etc
//define signals and slots for Client
// signals and slots
connect( btnStartClient, SIGNAL( clicked() ), this, SLOT( startClient() ) );
connect( btnStopClient, SIGNAL( clicked() ), this, SLOT( stopClient() ) );
// for write
connect( lEditCliReq, SIGNAL( textChanged(const QString& ) ), this, SLOT( getCliReqTextChanged() ) );
ui->setupUi( this );
}
ClientAgent::~ClientAgent()
{
delete ui;
}
void ClientAgent::startClient()
{
qDebug() << " we are in strt Clinet";
qDebug() << " connecting ";
// get server address and port number from GUI
QString testAddr = lEditAddr->text();
QString testPort = lEditPrt->text();
tcpSockForTest = new ClientForTest( testAddr, testPort.toInt(), this );
if( tcpSockForTest->connectToServer() == true )
{
lblCliStatus->setText(" connected ....");
}
else
{
lblCliStatus->setText(" failed to connect....");
}
}
void ClientAgent::stopClient()
{
qDebug() << " disconnecting ";
tcpSockForTest->disconnectToServer();
delete tcpSockForTest;
}
ClientForTest.h
class ClientForTest : public QObject
{
Q_OBJECT
public:
ClientForTest( QString hostAddr, quint16 portNum, QObject *parent );
bool connectToServer();
void disconnectToServer();
QString getStoreMsgFrmCliReq( ) const;
void writeToTest(const QString& stripCmd );
void executeSignals();
~ClientForTest();
signals:
void sigSendData();
public slots:
void connectedToTest();
void connectionClosedByServer();
void error();
private:
QTcpSocket *sockFortest;
QString hostName;
quint16 portNumber;
quint16 nextBlockSize;
QString storeLineEditMsg;// store message from lineEditCliReq;
};
ClientForTest.cpp
ClientForTest::ClientForTest( QString hostAddr, quint16 portNum, QObject *parent ) :
hostName( hostAddr ),
portNumber( portNum ),
QObject( parent )
{
connect( sockFortest, SIGNAL( connected() ), this, SLOT( connectedToTest() ) );
connect( sockFortest, SIGNAL( disconnected() ), this, SLOT( connectionClosedByServer() ) );
connect( sockFortest, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( error() ) );
storeLineEditMsg = "";
}
void ClientForTest::executeSignals()
{
// actually I need toplace itin constructor will do later on
connect( this, SIGNAL( sigSendData() ), this, SLOT( connectedToTest() ) );
}
ClientForTest::~ClientForTest()
{
sockFortest->close();
delete sockFortest;
}
bool ClientForTest::connectToServer()
{
sockFortest = new QTcpSocket( this->parent() ); // COULD be Probelm here but how to fix it?
sockFortest->connectToHost( hostName, portNumber );
nextBlockSize = 0;
executeSignals();
return sockFortest->waitForConnected(1000);
}
void ClientForTest::disconnectToServer()
{
sockFortest->close();
//sockFortest.close();
qDebug() << "in disconnect for Test ";
emit updateLabelinParent( updateStatusDis );
}
void ClientForTest::connectionClosedByServer()
{
qDebug() << "connection closed by server ";
if( nextBlockSize != 0xFFFF )
{
disconnectToServer();
}
}
void ClientForTest::error()
{
qDebug() << "in error ";
disconnectToServer();
}
void ClientForTest::writeToTest( const QString& stripCmd )
{
storeLineEditMsg = stripCmd;
if( sockFortest->state() == QTcpSocket::UnconnectedState )
{
sockFortest->close();
delete sockFortest;
sockFortest = new QTcpSocket( this->parent() );
if( connectToServer() == true )
{
qDebug() << " YEEEE CONNECTED AGAIN finally ";
}
}
if( sockFortest->state() == QTcpSocket::ConnectedState )
{
qDebug() << " YEEEE CONNECTED";
sigSendData();
}
}
void ClientForTest::connectedToTest( )
{
QByteArray block;
QDataStream out( &block, QIODevice::WriteOnly );
out.setVersion( QDataStream::Qt_4_3 );
QString stripCmd = getStoreMsgFrmCliReq(); // received info for some other function I havnt shown that func here
out << quint16( 0 ) << stripCmd;
out.device()->seek( 0 );
out << quint16( block.size() - sizeof( quint16 ) );
qDebug()<<" yeeeee connected state...";
sockFortest->write( block );
//reset storeLineEditMessage
storeLineEditMsg.clear();
}
1 ответ
Несколько вещей, которые все вместе могут быть ответственны за ваш сбой:
- вы подключаете сигналы внутри
ClientForTest
конструктор, но сам сокет создается позже. Это не сработает. Переместить линиюsockFortest = new QTcpSocket(this);
в конструктор, перед подключением - Та же строка, используйте
this
вместоthis->parent()
- И наконец: есть несколько мест, где вы создаете / удаляете сокет. Не делай этого. Создайте сокет внутри конструктора с помощью
this
как родитель, и все тут. В вашемconnectToServer
установить соединение, и вdisconnectToServer
закройте его, используяdisconnectFromHost
, То же самое касаетсяstartClient
а такжеstopClient
, Создайте объект один раз и просто используйте функции подключения / отключения, без удаления.
Если код требуется, я могу добавить некоторые.
Поскольку это было запрошено, вот еще несколько объяснений:
- Конечно, вы можете использовать родитель, но в вашем случае,
ClientForTest
являетсяQObject
, тоже. Если вы установитеClientForTest
как родительский элемент сокета и виджет как родительский элементClientForTest
, они оба будут очищены должным образом. Если вы используетеthis->parent()
, оба будут уничтожены "одновременно". Однако, один идет первым, и иногда Qt меняет порядок, поэтому ваш сокет может быть уничтожен до того, какClientForTest
, ДеструкторClientForTest
потерпит крах. Этого не произойдет, если сокет является потомкомClientForTest
- Основное различие между
close()
а такжеdisconnectFromHost()
является то, что первый фактически закрывает сокет ОС, а второй - нет. Проблема в том, что после закрытия сокета вы не можете использовать его для создания нового соединения. Таким образом, если вы хотите повторно использовать сокет, используйтеdisconnectFromHost()
иначеclose()
И относительно 3. и 4.: То, что вы делаете, это создание ClientForTest
когда пользователь нажимает кнопку подключения, и удаляет его, как только он нажимает кнопку отключения. Но это не хороший дизайн (ИМХО). поскольку tcpSockForTest
уже является членом класса, создайте его (через new) внутри конструктора и удалите его в деструкторе (необязательно, потому что, если вы передадите виджет в качестве родителя, Qt удалит его для вас).