QT 中Socket客户端与服务器异常断开后重连

转载地址:https://blog.csdn.net/lms1008611/article/details/81507161

在现在的项目开发中,经常要使用TCP/IP协议来进行通讯,但有时候与服务器端的链接由于网络问题导致连接异常或断开,这就需要我们的软件能自动重连,在Linux中,我们的思维一般是通过心跳包来监控连接是否断开,有时候还单独开一个线程,但是在QT中,就变得简单多了,当连接异常断开时,会触发相应的信号,我们只要在这个信号对应的槽函数中做重连处理就可以了,不需要另开线程也不需要心跳包。由于网上查的QT重连服务器端,大多都是通过建立的线程去实现,我觉得比较麻烦,这里就记录下我自己的思路:使用定时器QTimer或定时器事件QTimerEvent,来实现重连,当当前的连接断开时,QT便会发送 disconnected()信号,我们在这个信号对应的槽函数中开启定时器,重连的操作就放在定时器对应的槽函数中执行(或重写的定时器事件中执行),当重连成功后,QT会发送connected()信号,此时我们在其对应的槽函数中关闭定时器即可。

    下边直接来贴代码,我们是建了一个简单的clientt类,来做一个Socket客户端,首先是头文件client.h文件

 
  1. #ifndef CLIENT_H

  2. #define CLIENT_H

  3.  
  4. #include <QtWidgets/QWidget>

  5. #include <QtNetwork>

  6. #include <QtNetwork/QTcpSocket>

  7. #include <QLineEdit>

  8. #include <QTextEdit>

  9. #include <QPushButton>

  10. #include <QLabel>

  11. #include <QTimerEvent>

  12. #include <QTimer>

  13.  
  14. class Client : public QWidget

  15. {

  16. Q_OBJECT

  17.  
  18. public:

  19. Client(QWidget *parent = 0);

  20. ~Client();

  21.  
  22. private:

  23. //方式一:使用定时器事件实现socket重连

  24. int m_TimerID; //定时器事件ID

  25. void timerEvent(QTimerEvent * event); //需要重写改定时器事件函数

  26.  
  27. //方式二:使用定时器实现重连

  28. QTimer m_Timer;

  29.  
  30. protected:

  31. void init(); //UI初始化

  32. void newTCPConnect(); //建立一个新连接

  33.  
  34. private slots:

  35. void clientReadData(); //几个Socket连接相关的槽函数

  36. void clientConnected();

  37. void clientDisconnected();

  38. void clientError(QAbstractSocket::SocketError socketError);

  39. void clientStateChange(QAbstractSocket::SocketState socketState);

  40.  
  41. void onConnectButtonClicked();

  42.  
  43. void slotTimeOut();

  44.  
  45.  
  46. private:

  47. bool m_IsConnected;

  48.  
  49. QTcpSocket *tcpSocket;

  50.  
  51. QPushButton m_ConnectButton;

  52.  
  53. QLabel m_IPLabel;

  54. QLabel m_PortLabel;

  55. QLineEdit m_IPEdit;

  56. QLineEdit m_PortEdit;

  57. QTextEdit m_TextEdit;

  58. };

  59.  
  60. #endif // CLIENT_H

下边是client.cpp文件

 
  1. #include "Client.h"

  2.  
  3. #include <QHostAddress>

  4. #include <QFormLayout>

  5. #include <QHBoxLayout>

  6. #include <QVBoxLayout>

  7.  
  8. #include <QDebug>

  9.  
  10. Client::Client(QWidget *parent) : QWidget(parent), m_IsConnected(false)

  11. {

  12. this->setFixedSize(500, 300);

  13. init();

  14. }

  15.  
  16. void Client::init()

  17. {

  18. m_IPLabel.setParent(this);

  19. m_IPLabel.setText("IP");

  20. m_PortLabel.setParent(this);

  21. m_PortLabel.setText("Port");

  22. m_IPEdit.setParent(this);

  23. m_IPEdit.setText("127.0.0.1");

  24. m_PortEdit.setParent(this);

  25. m_PortEdit.setText("5000");

  26. m_ConnectButton.setParent(this);

  27. m_ConnectButton.setText("connect");

  28. m_TextEdit.setParent(this);

  29.  
  30. QFormLayout *fLayout1 = new QFormLayout;

  31. fLayout1->addRow(&m_IPLabel, &m_IPEdit);

  32. QFormLayout *fLayout2 = new QFormLayout;

  33. fLayout2->addRow(&m_PortLabel, &m_PortEdit);

  34.  
  35. QHBoxLayout *hLayout = new QHBoxLayout;

  36. hLayout->addLayout(fLayout1);

  37. hLayout->addLayout(fLayout2);

  38. hLayout->addWidget(&m_ConnectButton);

  39.  
  40. QVBoxLayout *vLayout = new QVBoxLayout;

  41. vLayout->addLayout(hLayout);

  42. vLayout->addWidget(&m_TextEdit);

  43.  
  44. this->setLayout(vLayout);

  45.  
  46. tcpSocket = new QTcpSocket(this);

  47.  
  48. connect(&m_ConnectButton, SIGNAL(clicked()), this, SLOT(onConnectButtonClicked()));

  49.  
  50. connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(clientReadData()));

  51. connect(tcpSocket, SIGNAL(connected()), this, SLOT(clientConnected()));

  52. connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));

  53. connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(clientStateChange(QAbstractSocket::SocketState)));

  54. connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(clientError(QAbstractSocket::SocketError)));

  55.  
  56. connect(&m_Timer, SIGNAL(timeout()), this, SLOT(slotTimeOut()));

  57.  
  58. }

  59.  
  60. void Client::newTCPConnect()

  61. {

  62. tcpSocket->abort();

  63. QHostAddress address = QHostAddress(m_IPEdit.text());

  64. unsigned int port = m_PortEdit.text().toInt();

  65. tcpSocket->connectToHost(address, port);

  66.  
  67. if (!tcpSocket->waitForConnected(3000)) //连接时长为3s,超过3秒连不上则输出错误信息

  68. {

  69. }

  70. }

  71.  
  72. //接收数据

  73. void Client::clientReadData()

  74. {

  75. QString msg = "\nrecieve msg : " + tcpSocket->readAll();

  76. m_TextEdit.append(msg);

  77. tcpSocket->write("GET");

  78. }

  79.  
  80. //连接成功是时调用

  81. void Client::clientConnected()

  82. {

  83. m_Timer.stop(); //连接成功后定时器停止

  84. //killTimer(m_TimerID); //连接成功后终止对应ID的定时器事件

  85. m_IsConnected = true;

  86. QString msg = "server IP : " + tcpSocket->peerAddress().toString();

  87. m_TextEdit.append(msg);

  88. msg = "server Port : " + QString::number(tcpSocket->peerPort());

  89. m_TextEdit.append(msg);

  90.  
  91. m_ConnectButton.setText("disconnect");

  92. m_ConnectButton.setStyleSheet("background-color: green");

  93.  
  94. //当重连成功时,可以根据具体情况恢复软件的一些工作

  95.  
  96. }

  97.  
  98. //连接断开时调用

  99. void Client::clientDisconnected()

  100. {

  101. m_Timer.start(2000); //连接断开时开启定时器,定时时间为2s

  102. //m_TimerID = startTimer(2000); //连接断开时开始一个定时器事件,并将ID赋给m_TimerID

  103. m_IsConnected = false;

  104. m_ConnectButton.setText("connect");

  105. m_ConnectButton.setStyleSheet("background-color: red");

  106.  
  107. //当连接断开时,可以根据具体情况决定是否停止软件当前的一些工作,比方暂停测试什么的

  108. }

  109.  
  110. //获取socket错误信息

  111. void Client::clientError(QAbstractSocket::SocketError socketError)

  112. {

  113. m_IsConnected = false;

  114.  
  115. tcpSocket->close();

  116. }

  117.  
  118. //连接状态发生改变时获取其状态

  119. void Client::clientStateChange(QAbstractSocket::SocketState socketState)

  120. {

  121. #if 0

  122. if (socketState == QAbstractSocket::UnconnectedState)

  123. {

  124. m_TextEdit.append("clientState : UnconnectedState");

  125. }

  126. else if (socketState == QAbstractSocket::HostLookupState)

  127. {

  128. m_TextEdit.append("clientState : HostLookupState");

  129. }

  130. else if (socketState == QAbstractSocket::ConnectedState)

  131. {

  132. m_TextEdit.append("clientState : ConnectedState");

  133. }

  134. else if (socketState == QAbstractSocket::ConnectingState)

  135. {

  136. m_TextEdit.append("clientState : ConnectingState");

  137. }

  138. else if (socketState == QAbstractSocket::ClosingState)

  139. {

  140. m_TextEdit.append("clientState : ClosingState");

  141. }

  142. #endif

  143. }

  144.  
  145. void Client::onConnectButtonClicked()

  146. {

  147. m_TextEdit.append("");

  148. if (m_ConnectButton.text() == "disconnect")

  149. {

  150. tcpSocket->disconnectFromHost();

  151. }

  152. else if(m_ConnectButton.text() == "connect")

  153. {

  154. newTCPConnect();

  155. }

  156. }

  157.  
  158. void Client::timerEvent(QTimerEvent * event)

  159. {

  160. if (event->timerId() == m_TimerID) //在定时器事件中重连服务器

  161. {

  162. if (!m_IsConnected)

  163. {

  164. m_TextEdit.append(QString::fromLocal8Bit("重连服务器..."));

  165. newTCPConnect();

  166. }

  167. }

  168. }

  169.  
  170. //使用定时器来重连服务器

  171. void Client::slotTimeOut()

  172. {

  173. if (!m_IsConnected)

  174. {

  175. m_TextEdit.append(QString::fromLocal8Bit("重连服务器..."));

  176. newTCPConnect();

  177. }

  178. }

  179.  
  180. Client::~Client()

  181. {

  182.  
  183. }

主程序中直接使用这个类:

 
  1. #include "Client.h"

  2. #include <QtWidgets/QApplication>

  3.  
  4. int main(int argc, char *argv[])

  5. {

  6. QApplication a(argc, argv);

  7. Client w;

  8. w.show();

  9. return a.exec();

  10. }

编译运行:

刚开始与服务器端正常连接并接收数据hello sokect,然后我关掉服务器,于是客户端便进行重连...,后边重开服务器,客户端便重连成功。

上边是把定时器事件给屏蔽了,使用定时器来实现,也可以把定时器屏蔽,使用定时器事件进行重连,具体视项目情况而定。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值