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

标签 c++ qt tcp qtcpsocket

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

项目概述。我有 Qwidget (主应用程序),带有 2 行编辑,供用户输入端口号和服务器 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();
}

最佳答案

有一些因素可能共同导致您的崩溃:

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

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

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

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

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

关于c++ - Qt 断开连接后连接到服务器时客户端程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32203984/

相关文章:

c++ qt 将图像添加到匿名标签(或其他小部件)

Azure Vnet 专用 IP 范围

c++ - 共享内存中的 STL 结构

c++ - Socket SO_RCVTIMEO超时时间是C++/VC++中设置值的两倍

c++ - 如何在 QTreeView 中设置 QDateTimeEdit 的显示格式和类似属性?

c# - QNetworkAccessManager->在 DLL 中调用时卡住

sockets - 如何使用 Golang 编写原始 TCP 数据包(使用 gopacket)并通过原始套接字发送

java - 通过套接字在对象内发送文件字节

c++ - 使用 boost 检查端口可用性

c++ - 错误: expected ';' before defining object of class