断开Qt后连接到服务器时客户端程序崩溃

Posted

技术标签:

【中文标题】断开Qt后连接到服务器时客户端程序崩溃【英文标题】:client program crashes when connect to server after disconnect Qt 【发布时间】:2015-08-25 12:25:47 【问题描述】:

我正在基于 Qt 实现 TCP-Ip 服务器的客户端,但是当我关闭连接并通过点击连接按钮重新启动时程序崩溃。

项目概述。我有带有 2 行编辑的 Qwidget(主应用程序)供用户输入端口号和服务器 IP 地址。 它还有 2 个连接到服务器和断开连接的按钮。在点击连接按钮时,它将调用客户端套接字的构造函数并调用connectToHost。

测试:尝试在同一台计算机上进行测试,服务器在端口 6000 和 IP 地址 127.0.0.1 上运行。

问题:当我启动客户端应用程序并输入端口号和地址并点击连接按钮时,它连接成功。我可以成功写入服务器。然后我单击断开连接按钮并成功断开连接,但之后如果我通过单击连接按钮再次连接它会崩溃。我知道问题出在 tcpSocket 但不知道如何解决。

clientAgent(QWidget appilciation).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();

【问题讨论】:

您是否调试过应用程序以查看它究竟在哪一行崩溃? @BowDzone,是的,它在 ClientForTest 的构造函数中崩溃 @Bowdzone,如果我将构造函数中定义的所有连接都用于函数 executeSignals() 它工作正常。但现在我想知道我添加了太多删除和新调用。有更好的方法吗? 您不必在每次断开连接时删除QTcpSocket 对象并在每次连接时创建它的新实例。例如在构造函数中初始化一个实例。但问题似乎出在您的 ClientForTest 构造函数上。您连接sockFortest 对象的信号,但该对象尚未初始化。你在那里使用了一个未初始化的指针。 没错。每次建立新连接时,都会创建一个新的 QTcpSocket 对象。当您断开连接时,您会删除该对象。我不知道您的应用程序究竟是如何工作的,但这似乎没有必要。您可以在构造函数中创建一次QTcpSocket 对象并使用该实例。 QTcpSocketQObject 的子类,所以as long as you set a parent to it, you won't have to worry about memory leaks。 【参考方案1】:

有几件事可能共同导致您的崩溃:

您在ClientForTest 构造函数中连接信号,但稍后会创建套接字本身。那是行不通的。在连接之前将行 sockFortest = new QTcpSocket(this); 移动到构造函数 同一行,使用this 而不是this-&gt;parent() 最后:您可以在多个位置创建/删除套接字。不要这样做。在构造函数内创建以this 为父级的套接字,就是这样。在您的connectToServer 中建立连接,并在disconnectToServer 中使用disconnectFromHost 将其关闭。 startClientstopClient 也是如此。创建一次对象,只需使用连接/断开功能,无需删除。

如果需要代码,我可以添加一些。

既然有人要求,这里有一些解释:

    当然你可以使用 parent,但在你的情况下,ClientForTest 也是QObject。如果您将ClientForTest 设置为套接字的父级,并将小部件设置为ClientForTest 的父级,它们都将被正确清理。如果您使用this-&gt;parent(),两者都将“同时”被销毁。但是,一个是第一位的,有时 Qt 会更改顺序,因此您的套接字可能会在 ClientForTest 之前被销毁。 ClientForTest 的析构函数会崩溃。如果套接字是ClientForTest 的子级,则不会发生这种情况 close()disconnectFromHost() 之间的主要区别在于第一个实际上关闭了操作系统套接字,而第二个没有。问题是,在套接字关闭后,您无法使用它来创建新连接。因此,如果您想重用套接字,请使用disconnectFromHost(),否则使用close()

关于 3. 和 4.: 您正在做的是在用户单击连接时创建ClientForTest,并在他单击断开连接时立即将其删除。但这不是好的设计(恕我直言)。由于tcpSockForTest 已经是该类的成员,因此在构造函数中创建它(通过new),并在析构函数中删除它(可选,因为如果您将小部件作为父级传递,Qt 会为您删除它)。

【讨论】:

谢谢伙计,信息量很大。我猜主要问题是构造函数中的连接函数。我已经纠正了,事情看起来更好,但显然是为了学习目的你能解释一下 1)为什么我应该使用 "this" 而不是 "this->parent()" 。我使用 parent 的主要想法是将它链接到主小部件类,当我们关闭 mainwidget 时,它也会破坏客户端。 2) disconnectToServer func-> 我应该不使用 sockFortest->close(); 3)startClient -> 我已经使用 new 创建了 .. 如果我不使用 new 我将如何创建对象? 4)StopClient -> 类似地,我在 stopClient 中删除创建的套接字。你建议更好的方法吗?我问的原因是删除我的代码的缺陷。

以上是关于断开Qt后连接到服务器时客户端程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Qt 中修改 QWebsocket 定时器

客户端断开连接时套接字服务器崩溃

Qt 客户端(GUI)连接到 QT 服务器

电缆与 Mac 断开连接时应用程序崩溃

OSX - Qt 应用程序在接收系统事件 QEvent::FileOpen 时崩溃

故障转移后连接到镜像数据库