Клиентская программа падает при подключении к серверу после отключения 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, Создайте объект один раз и просто используйте функции подключения / отключения, без удаления.

Если код требуется, я могу добавить некоторые.

Поскольку это было запрошено, вот еще несколько объяснений:

  1. Конечно, вы можете использовать родитель, но в вашем случае, ClientForTest является QObject, тоже. Если вы установите ClientForTest как родительский элемент сокета и виджет как родительский элемент ClientForTest, они оба будут очищены должным образом. Если вы используете this->parent(), оба будут уничтожены "одновременно". Однако, один идет первым, и иногда Qt меняет порядок, поэтому ваш сокет может быть уничтожен до того, как ClientForTest, Деструктор ClientForTest потерпит крах. Этого не произойдет, если сокет является потомком ClientForTest
  2. Основное различие между close() а также disconnectFromHost() является то, что первый фактически закрывает сокет ОС, а второй - нет. Проблема в том, что после закрытия сокета вы не можете использовать его для создания нового соединения. Таким образом, если вы хотите повторно использовать сокет, используйте disconnectFromHost() иначе close()

И относительно 3. и 4.: То, что вы делаете, это создание ClientForTest когда пользователь нажимает кнопку подключения, и удаляет его, как только он нажимает кнопку отключения. Но это не хороший дизайн (ИМХО). поскольку tcpSockForTest уже является членом класса, создайте его (через new) внутри конструктора и удалите его в деструкторе (необязательно, потому что, если вы передадите виджет в качестве родителя, Qt удалит его для вас).

Другие вопросы по тегам