OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2514|回复: 0

《I.MX6U 嵌入式Qt开发指南》第十一章 网络编程 11.2 TCP通信

[复制链接]

1070

主题

1081

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4443
金钱
4443
注册时间
2019-5-8
在线时间
1199 小时
发表于 2022-8-9 16:30:59 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2022-8-10 17:33 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)  章节摘自【正点原子】《I.MX6U 嵌入式Qt开发指南》

3)购买链接:https://detail.tmall.com/item.htm?id=609033604451
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子阿尔法Linux交流群:1027879335




第十一章 网络编程


11.2 TCP通信
11.2.1 TCP
TCP协议(Transmission Control Protocol)全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP通信必须先建立TCP连接,通信端分为客户端和服务端。服务端通过监听某个端口来监听是否有客户端连接到来,如果有连接到来,则建立新的socket连接;客户端通过ip和port连接服务端,当成功建立连接之后,就可进行数据的收发了。需要注意的是,在Qt中,Qt把socket当成输入输出流来对待的,数据的收发是通过read()和write()来进行的,需要与我们常见的send()与recv()进行区分。
TCP客户端与服务端通信示意图如下。
image004.png

11.2.2 TCP服务端应用实例
本例目的:了解TCP服务端的使用。
例08_tcpserver,TCP服务端(难度:一般)。项目路径为Qt/2/08_tcpserver。本例大体流程首先获取本地IP地址。创建一个tcpSocket套接字,一个tcpServer服务端。点击监听即监听本地的主机IP地址和端口,同时等待服务端的连接。此程序需要结合客户端一起使用。
项目文件08_tcpserver.pro文件第一行添加的代码部分如下。
  1. 08_tcpserver.pro编程后的代码
  2. 1   QT       += core gui network
  3. 2
  4. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  5. 4
  6. 5   CONFIG += c++11
  7. 6
  8. 7   # The following define makes your compiler emit warnings if you use
  9. 8   # any Qt feature that has been marked deprecated (the exact warnings
  10. 9   # depend on your compiler). Please consult the documentation of the
  11. 10  # deprecated API in order to know how to port your code away from it.
  12. 11  DEFINES += QT_DEPRECATED_WARNINGS
  13. 12
  14. 13  # You can also make your code fail to compile if it uses deprecated APIs.
  15. 14  # In order to do so, uncomment the following line.
  16. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.
  17. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
  18. 17
  19. 18  SOURCES += \
  20. 19      main.cpp \
  21. 20      mainwindow.cpp
  22. 21
  23. 22  HEADERS += \
  24. 23      mainwindow.h
  25. 24
  26. 25  # Default rules for deployment.
  27. 26  qnx: target.path = /tmp/${TARGET}/bin
  28. 27  else: unix:!android: target.path = /opt/${TARGET}/bin
  29. 28  !isEmpty(target.path): INSTALLS += target
复制代码
在头文件“mainwindow.h”具体代码如下。
  1. mainwindow.h编程后的代码
  2.     /******************************************************************
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   08_tcpserver
  5.     * @brief         mainwindow.h
  6.     * @author        Deng Zhimao
  7.     * @EMAIL         1252699831@qq.com
  8.     * @net            www.openedv.com
  9.     * @date           2021-04-13
  10.     *******************************************************************/
  11. 1   #ifndef MAINWINDOW_H
  12. 2   #define MAINWINDOW_H
  13. 3
  14. 4   #include <QMainWindow>
  15. 5   #include <QTcpServer>
  16. 6   #include <QTcpSocket>
  17. 7   #include <QVBoxLayout>
  18. 8   #include <QHBoxLayout>
  19. 9   #include <QPushButton>
  20. 10  #include <QTextBrowser>
  21. 11  #include <QLabel>
  22. 12  #include <QComboBox>
  23. 13  #include <QSpinBox>
  24. 14  #include <QHostInfo>
  25. 15  #include <QLineEdit>
  26. 16  #include <QNetworkInterface>
  27. 17  #include <QDebug>
  28. 18
  29. 19  class MainWindow : public QMainWindow
  30. 20  {
  31. 21      Q_OBJECT
  32. 22
  33. 23  public:
  34. 24      MainWindow(QWidget *parent = nullptr);
  35. 25      ~MainWindow();
  36. 26
  37. 27  private:
  38. 28      /* tcp服务器 */
  39. 29      QTcpServer *tcpServer;
  40. 30
  41. 31      /* 通信套接字 */
  42. 32      QTcpSocket *tcpSocket;
  43. 33
  44. 34      /* 按钮 */
  45. 35      QPushButton *pushButton[4];
  46. 36
  47. 37      /* 标签文本 */
  48. 38      QLabel *label[2];
  49. 39
  50. 40      /* 水平容器 */
  51. 41      QWidget *hWidget[3];
  52. 42
  53. 43      /* 水平布局 */
  54. 44      QHBoxLayout *hBoxLayout[3];
  55. 45
  56. 46      /* 垂直容器 */
  57. 47      QWidget *vWidget;
  58. 48
  59. 49      /* 垂直布局 */
  60. 50      QVBoxLayout *vBoxLayout;
  61. 51
  62. 52      /* 文本浏览框 */
  63. 53      QTextBrowser *textBrowser;
  64. 54
  65. 55      /* 用于显示本地ip */
  66. 56      QComboBox *comboBox;
  67. 57
  68. 58      /* 用于选择端口 */
  69. 59      QSpinBox  *spinBox;
  70. 60
  71. 61      /* 文本输入框 */
  72. 62      QLineEdit *lineEdit;
  73. 63
  74. 64      /* 存储本地的ip列表地址 */
  75. 65      QList<QHostAddress> IPlist;
  76. 66
  77. 67      /* 获取本地的所有ip */
  78. 68      void getLocalHostIP();
  79. 69
  80. 70  private slots:
  81. 71      /* 客户端连接处理槽函数 */
  82. 72      void clientConnected();
  83. 73
  84. 74      /* 开始监听槽函数 */
  85. 75      void startListen();
  86. 76
  87. 77      /* 停止监听槽函数 */
  88. 78      void stopListen();
  89. 79
  90. 80      /* 清除文本框时的内容 */
  91. 81      void clearTextBrowser();
  92. 82
  93. 83      /* 接收到消息 */
  94. 84      void receiveMessages();
  95. 85
  96. 86      /* 发送消息 */
  97. 87      void sendMessages();
  98. 88
  99. 89      /* 连接状态改变槽函数 */
  100. 90      void socketStateChange(QAbstractSocket::SocketState);
  101. 91  };
  102. 92  #endif // MAINWINDOW_H
复制代码
头文件里主要是声明界面用的元素,及一些槽函数。重点是声明tcpServer和tcpSocket。
在源文件“mainwindow.cpp”具体代码如下。
  1. mainwindow.cpp编程后的代码
  2.     /******************************************************************
  3.     Copyright &#169; Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   08_tcpserver
  5.     * @brief         mainwindow.cpp
  6.     * @author        Deng Zhimao
  7.     * @email         1252699831@qq.com
  8.     * @net            www.openedv.com
  9.     * @date           2021-04-13
  10.     *******************************************************************/
  11. 1   #include "mainwindow.h"
  12. 2
  13. 3   MainWindow::MainWindow(QWidget *parent)
  14. 4       : QMainWindow(parent)
  15. 5   {
  16. 6       /* 设置主窗体的位置与大小 */
  17. 7       this->setGeometry(0, 0, 800, 480);
  18. 8
  19. 9       /* 实例化tcp服务器与tcp套接字 */
  20. 10      tcpServer = new QTcpServer(this);
  21. 11      tcpSocket = new QTcpSocket(this);
  22. 12
  23. 13      /* 开始监听按钮 */
  24. 14      pushButton[0] = new QPushButton();
  25. 15      /* 停止监听按钮 */
  26. 16      pushButton[1] = new QPushButton();
  27. 17      /* 清空聊天文本按钮 */
  28. 18      pushButton[2] = new QPushButton();
  29. 19      /* 发送消息按钮 */
  30. 20      pushButton[3] = new QPushButton();
  31. 21
  32. 22      /* 水平布局一 */
  33. 23      hBoxLayout[0] = new QHBoxLayout();
  34. 24      /* 水平布局二 */
  35. 25      hBoxLayout[1] = new QHBoxLayout();
  36. 26      /* 水平布局三 */
  37. 27      hBoxLayout[2] = new QHBoxLayout();
  38. 28      /* 水平布局四 */
  39. 29      hBoxLayout[3] = new QHBoxLayout();
  40. 30
  41. 31      /* 水平容器一 */
  42. 32      hWidget[0] =  new QWidget();
  43. 33      /* 水平容器二 */
  44. 34      hWidget[1] =  new QWidget();
  45. 35      /* 水平容器三 */
  46. 36      hWidget[2] =  new QWidget();
  47. 37
  48. 38      vWidget = new QWidget();
  49. 39      vBoxLayout = new QVBoxLayout();
  50. 40
  51. 41      /* 标签实例化 */
  52. 42      label[0] = new QLabel();
  53. 43      label[1] = new QLabel();
  54. 44
  55. 45      lineEdit = new QLineEdit();
  56. 46      comboBox = new QComboBox();
  57. 47      spinBox = new QSpinBox();
  58. 48      textBrowser = new QTextBrowser();
  59. 49
  60. 50      label[0]->setText("监听IP地址:");
  61. 51      label[1]->setText("监听端口:");
  62. 52
  63. 53      /* 设置标签根据文本文字大小自适应大小  */
  64. 54      label[0]->setSizePolicy(QSizePolicy::Fixed,
  65. 55                              QSizePolicy::Fixed);
  66. 56      label[1]->setSizePolicy(QSizePolicy::Fixed,
  67. 57                              QSizePolicy::Fixed);
  68. 58
  69. 59      /* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */
  70. 60      spinBox->setRange(10000, 99999);
  71. 61
  72. 62      pushButton[0]->setText("开始监听");
  73. 63      pushButton[1]->setText("停止监听");
  74. 64      pushButton[2]->setText("清空文本");
  75. 65      pushButton[3]->setText("发送消息");
  76. 66
  77. 67      /* 设置停止监听状态不可用 */
  78. 68      pushButton[1]->setEnabled(false);
  79. 69
  80. 70      /* 设置输入框默认的文本 */
  81. 71      lineEdit->setText("www.openedv.com正点原子论坛");
  82. 72
  83. 73      /* 水平布局一添加内容 */
  84. 74      hBoxLayout[0]->addWidget(pushButton[0]);
  85. 75      hBoxLayout[0]->addWidget(pushButton[1]);
  86. 76      hBoxLayout[0]->addWidget(pushButton[2]);
  87. 77
  88. 78      /* 设置水平容器一的布局为水平布局一 */
  89. 79      hWidget[0]->setLayout(hBoxLayout[0]);
  90. 80
  91. 81      /* 水平布局二添加内容 */
  92. 82      hBoxLayout[1]->addWidget(label[0]);
  93. 83      hBoxLayout[1]->addWidget(comboBox);
  94. 84      hBoxLayout[1]->addWidget(label[1]);
  95. 85      hBoxLayout[1]->addWidget(spinBox);
  96. 86
  97. 87      /* 设置水平容器二的布局为水平布局二 */
  98. 88      hWidget[1]->setLayout(hBoxLayout[1]);
  99. 89
  100. 90      /* 水平布局三添加内容 */
  101. 91      hBoxLayout[2]->addWidget(lineEdit);
  102. 92      hBoxLayout[2]->addWidget(pushButton[3]);
  103. 93
  104. 94      /* 设置水平容器三的布局为水平布局一 */
  105. 95      hWidget[2]->setLayout(hBoxLayout[2]);
  106. 96
  107. 97      /* 垂直布局添加内容 */
  108. 98      vBoxLayout->addWidget(textBrowser);
  109. 99      vBoxLayout->addWidget(hWidget[1]);
  110. 100     vBoxLayout->addWidget(hWidget[0]);
  111. 101     vBoxLayout->addWidget(hWidget[2]);
  112. 102
  113. 103     /* 设置垂直容器的布局为垂直布局 */
  114. 104     vWidget->setLayout(vBoxLayout);
  115. 105
  116. 106     /* 居中显示 */
  117. 107     setCentralWidget(vWidget);
  118. 108
  119. 109     /* 获取本地ip */
  120. 110     getLocalHostIP();
  121. 111
  122. 112     /* 信号槽连接 */
  123. 113     connect(pushButton[0], SIGNAL(clicked()),
  124. 114             this, SLOT(startListen()));
  125. 115     connect(pushButton[1], SIGNAL(clicked()),
  126. 116             this, SLOT(stopListen()));
  127. 117     connect(pushButton[2], SIGNAL(clicked()),
  128. 118             this, SLOT(clearTextBrowser()));
  129. 119     connect(pushButton[3], SIGNAL(clicked()),
  130. 120             this, SLOT(sendMessages()));
  131. 121     connect(tcpServer, SIGNAL(newConnection()),
  132. 122             this, SLOT(clientConnected()));
  133. 123 }
  134. 124
  135. 125 MainWindow::~MainWindow()
  136. 126 {
  137. 127 }
  138. 128
  139. 129 /* 新的客户端连接 */
  140. 130 void MainWindow::clientConnected()
  141. 131 {
  142. 132     /* 获取客户端的套接字 */
  143. 133     tcpSocket = tcpServer->nextPendingConnection();
  144. 134     /* 客户端的ip信息 */
  145. 135     QString ip = tcpSocket->peerAddress().toString();
  146. 136     /* 客户端的端口信息 */
  147. 137     quint16 port = tcpSocket->peerPort();
  148. 138     /* 在文本浏览框里显示出客户端的连接信息 */
  149. 139     textBrowser->append("客户端已连接");
  150. 140     textBrowser->append("客户端ip地址:"
  151. 141                         + ip);
  152. 142     textBrowser->append("客户端端口:"
  153. 143                         + QString::number(port));
  154. 144
  155. 145     connect(tcpSocket, SIGNAL(readyRead()),
  156. 146             this, SLOT(receiveMessages()));
  157. 147     connect(tcpSocket,
  158. 148             SIGNAL(stateChanged(QAbstractSocket::SocketState)),
  159. 149             this,
  160. 150             SLOT(socketStateChange(QAbstractSocket::SocketState)));
  161. 151 }
  162. 152
  163. 153 /* 获取本地IP */
  164. 154 void MainWindow::getLocalHostIP()
  165. 155 {
  166. 156     // /* 获取主机的名称 */
  167. 157     // QString hostName = QHostInfo::localHostName();
  168. 158
  169. 159     // /* 主机的信息 */
  170. 160     // QHostInfo hostInfo = QHostInfo::fromName(hostName);
  171. 161
  172. 162     // /* ip列表,addresses返回ip地址列表,注意主机应能从路由器获取到
  173. 163     // * IP,否则可能返回空的列表(ubuntu用此方法只能获取到环回IP) */
  174. 164     // IPlist = hostInfo.addresses();
  175. 165     // qDebug()<<IPlist<<endl;
  176. 166
  177. 167     // /* 遍历IPlist */
  178. 168     // foreach (QHostAddress ip, IPlist) {
  179. 169     //      if (ip.protocol() == QAbstractSocket::IPv4Protocol)
  180. 170     //          comboBox->addItem(ip.toString());
  181. 171     //    }
  182. 172
  183. 173     /* 获取所有的网络接口,
  184. 174      * QNetworkInterface类提供主机的IP地址和网络接口的列表 */
  185. 175     QList<QNetworkInterface> list
  186. 176             = QNetworkInterface::allInterfaces();
  187. 177
  188. 178     /* 遍历list */
  189. 179     foreach (QNetworkInterface interface, list) {
  190. 180
  191. 181         /* QNetworkAddressEntry类存储IP地址子网掩码和广播地址 */
  192. 182         QList<QNetworkAddressEntry> entryList
  193. 183                 = interface.addressEntries();
  194. 184
  195. 185         /* 遍历entryList */
  196. 186         foreach (QNetworkAddressEntry entry, entryList) {
  197. 187             /* 过滤IPv6地址,只留下IPv4 */
  198. 188             if (entry.ip().protocol() ==
  199. 189                     QAbstractSocket::IPv4Protocol) {
  200. 190                 comboBox->addItem(entry.ip().toString());
  201. 191                 /* 添加到IP列表中 */
  202. 192                 IPlist<<entry.ip();
  203. 193             }
  204. 194         }
  205. 195     }
  206. 196 }
  207. 197
  208. 198 /* 开始监听 */
  209. 199 void MainWindow::startListen()
  210. 200 {
  211. 201     /* 需要判断当前主机是否有IP项 */
  212. 202     if (comboBox->currentIndex() != -1) {
  213. 203         qDebug()<<"start listen"<<endl;
  214. 204         tcpServer->listen(IPlist[comboBox->currentIndex()],
  215. 205                 spinBox->value());
  216. 206
  217. 207         /* 设置按钮与下拉列表框的状态 */
  218. 208         pushButton[0]->setEnabled(false);
  219. 209         pushButton[1]->setEnabled(true);
  220. 210         comboBox->setEnabled(false);
  221. 211         spinBox->setEnabled(false);
  222. 212
  223. 213         /* 在文本浏览框里显示出服务端 */
  224. 214         textBrowser->append("服务器IP地址:"
  225. 215                             + comboBox->currentText());
  226. 216         textBrowser->append("正在监听端口:"
  227. 217                             + spinBox->text());
  228. 218     }
  229. 219 }
  230. 220
  231. 221 /* 停止监听 */
  232. 222 void MainWindow::stopListen()
  233. 223 {
  234. 224     qDebug()<<"stop listen"<<endl;
  235. 225     /* 停止监听 */
  236. 226     tcpServer->close();
  237. 227
  238. 228     /* 如果是连接上了也应该断开,如果不断开客户端还能继续发送信息,
  239. 229      * 因为socket未断开,还在监听上一次端口 */
  240. 230     if (tcpSocket->state() == tcpSocket->ConnectedState)
  241. 231         tcpSocket->disconnectFromHost();
  242. 232
  243. 233     /* 设置按钮与下拉列表框的状态  */
  244. 234     pushButton[1]->setEnabled(false);
  245. 235     pushButton[0]->setEnabled(true);
  246. 236     comboBox->setEnabled(true);
  247. 237     spinBox->setEnabled(true);
  248. 238
  249. 239     /* 将停止监听的信息添加到文本浏览框中 */
  250. 240     textBrowser->append("已停止监听端口:"
  251. 241                         + spinBox->text());
  252. 242 }
  253. 243
  254. 244 /* 清除文本浏览框里的内容 */
  255. 245 void MainWindow::clearTextBrowser()
  256. 246 {
  257. 247     /* 清除文本浏览器的内容 */
  258. 248     textBrowser->clear();
  259. 249 }
  260. 250
  261. 251 /* 服务端接收消息 */
  262. 252 void MainWindow::receiveMessages()
  263. 253 {
  264. 254     /* 读取接收到的消息 */
  265. 255     QString messages = "客户端:" + tcpSocket->readAll();
  266. 256     textBrowser->append(messages);
  267. 257 }
  268. 258
  269. 259 /* 服务端发送消息 */
  270. 260 void MainWindow::sendMessages()
  271. 261 {
  272. 262     if(NULL == tcpSocket)
  273. 263         return;
  274. 264
  275. 265     /* 如果已经连接 */
  276. 266     if(tcpSocket->state() == tcpSocket->ConnectedState) {
  277. 267         /* 发送消息 */
  278. 268         tcpSocket->write(lineEdit->text().toUtf8().data());
  279. 269
  280. 270         /* 在服务端插入发送的消息 */
  281. 271         textBrowser->append("服务端:" + lineEdit->text());
  282. 272     }
  283. 273 }
  284. 274
  285. 275 /* 服务端状态改变 */
  286. 276 void MainWindow::socketStateChange(QAbstractSocket::SocketState state)
  287. 277 {
  288. 278     switch (state) {
  289. 279     case QAbstractSocket::UnconnectedState:
  290. 280         textBrowser->append("scoket状态:UnconnectedState");
  291. 281         break;
  292. 282     case QAbstractSocket::ConnectedState:
  293. 283         textBrowser->append("scoket状态:ConnectedState");
  294. 284         break;
  295. 285     case QAbstractSocket::ConnectingState:
  296. 286         textBrowser->append("scoket状态:ConnectingState");
  297. 287         break;
  298. 288     case QAbstractSocket::HostLookupState:
  299. 289         textBrowser->append("scoket状态:HostLookupState");
  300. 290         break;
  301. 291     case QAbstractSocket::ClosingState:
  302. 292         textBrowser->append("scoket状态:ClosingState");
  303. 293         break;
  304. 294     case QAbstractSocket::ListeningState:
  305. 295         textBrowser->append("scoket状态:ListeningState");
  306. 296         break;
  307. 297     case QAbstractSocket::BoundState:
  308. 298         textBrowser->append("scoket状态:BoundState");
  309. 299         break;
  310. 300     default:
  311. 301         break;
  312. 302     }
  313. 303 }
复制代码
       上面的代码主要是服务端开启监听,如果有客户端连到服务端,就会发射newConnection()信号,同时也连接到接收消息的信号与槽函数。点击发送消息按钮就可以使用tcpSocket发送消息。注意发送消息和接收消息都是通过tcpSocket的read()和write()进行。

11.2.3 TCP客户端应用实例
本例目的:了解TCP客户的使用。
例09_tcpclient,TCP客户端(难度:一般)。项目路径为Qt/2/09_ tcpclient。本例大体流程:首先获取本地IP地址。创建一个tcpSocket套接字,然后用tcpSocket套接字使用connectToHost函数连接服务端的主机IP地址和端口,即可相互通信。
项目文件08_tcpserver.pro文件第一行添加的代码部分如下。
  1. 09_tcpclient.pro编程后的代码

  2. 1   QT       += core gui network

  3. 2

  4. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

  5. 4

  6. 5   CONFIG += c++11

  7. 6

  8. 7   # The following define makes your compiler emit warnings if you use

  9. 8   # any Qt feature that has been marked deprecated (the exact warnings

  10. 9   # depend on your compiler). Please consult the documentation of the

  11. 10  # deprecated API in order to know how to port your code away from it.

  12. 11  DEFINES += QT_DEPRECATED_WARNINGS

  13. 12

  14. 13  # You can also make your code fail to compile if it uses deprecated APIs.

  15. 14  # In order to do so, uncomment the following line.

  16. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.

  17. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

  18. 17

  19. 18  SOURCES += \

  20. 19      main.cpp \

  21. 20      mainwindow.cpp

  22. 21

  23. 22  HEADERS += \

  24. 23      mainwindow.h

  25. 24

  26. 25  # Default rules for deployment.

  27. 26  qnx: target.path = /tmp/${TARGET}/bin

  28. 27  else: unix:!android: target.path = /opt/${TARGET}/bin

  29. 28  !isEmpty(target.path): INSTALLS += target
复制代码
在头文件“mainwindow.h”具体代码如下。
  1. mainwindow.h编程后的代码

  2.     /******************************************************************

  3.     Copyright &#169; Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.

  4.     * @projectName   09_tcpclient

  5.     * @brief         mainwindow.h

  6.     * @author        Deng Zhimao

  7.     * @email         1252699831@qq.com

  8.     * @net            www.openedv.com

  9.     * @date           2021-04-13

  10.     *******************************************************************/

  11. 1   #ifndef MAINWINDOW_H

  12. 2   #define MAINWINDOW_H

  13. 3

  14. 4   #include <QMainWindow>

  15. 5   #include <QTcpServer>

  16. 6   #include <QTcpSocket>

  17. 7   #include <QVBoxLayout>

  18. 8   #include <QHBoxLayout>

  19. 9   #include <QPushButton>

  20. 10  #include <QTextBrowser>

  21. 11  #include <QLabel>

  22. 12  #include <QComboBox>

  23. 13  #include <QSpinBox>

  24. 14  #include <QHostInfo>

  25. 15  #include <QLineEdit>

  26. 16  #include <QNetworkInterface>

  27. 17  #include <QDebug>

  28. 18

  29. 19  class MainWindow : public QMainWindow

  30. 20  {

  31. 21      Q_OBJECT

  32. 22

  33. 23  public:

  34. 24      MainWindow(QWidget *parent = nullptr);

  35. 25      ~MainWindow();

  36. 26

  37. 27  private:

  38. 28      /* 通信套接字 */

  39. 29      QTcpSocket *tcpSocket;

  40. 30

  41. 31      /* 按钮 */

  42. 32      QPushButton *pushButton[4];

  43. 33

  44. 34      /* 标签文本 */

  45. 35      QLabel *label[2];

  46. 36

  47. 37      /* 水平容器 */

  48. 38      QWidget *hWidget[3];

  49. 39

  50. 40      /* 水平布局 */

  51. 41      QHBoxLayout *hBoxLayout[3];

  52. 42

  53. 43      /* 垂直容器 */

  54. 44      QWidget *vWidget;

  55. 45

  56. 46      /* 垂直布局 */

  57. 47      QVBoxLayout *vBoxLayout;

  58. 48

  59. 49      /* 文本浏览框 */

  60. 50      QTextBrowser *textBrowser;

  61. 51

  62. 52      /* 用于显示本地ip */

  63. 53      QComboBox *comboBox;

  64. 54

  65. 55      /* 用于选择端口 */

  66. 56      QSpinBox  *spinBox;

  67. 57

  68. 58      /* 文本输入框 */

  69. 59      QLineEdit *lineEdit;

  70. 60

  71. 61      /* 存储本地的ip列表地址 */

  72. 62      QList<QHostAddress> IPlist;

  73. 63

  74. 64      /* 获取本地的所有ip */

  75. 65      void getLocalHostIP();

  76. 66

  77. 67  private slots:

  78. 68      /* 连接 */

  79. 69      void toConnect();

  80. 70

  81. 71      /* 断开连接 */

  82. 72      void toDisConnect();

  83. 73

  84. 74      /* 已连接 */

  85. 75      void connected();

  86. 76

  87. 77      /* 已断开连接 */

  88. 78      void disconnected();

  89. 79

  90. 80      /* 清除文本框时的内容 */

  91. 81      void clearTextBrowser();

  92. 82

  93. 83      /* 接收到消息 */

  94. 84      void receiveMessages();

  95. 85

  96. 86      /* 发送消息 */

  97. 87      void sendMessages();

  98. 88

  99. 89      /* 连接状态改变槽函数 */

  100. 90      void socketStateChange(QAbstractSocket::SocketState);

  101. 91  };

  102. 92  #endif // MAINWINDOW_H
复制代码
头文件里主要是声明界面用的元素,及一些槽函数。重点是声明tcpSocket。
在源文件“mainwindow.cpp”具体代码如下。
  1. mainwindow.cpp编程后的代码

  2.     /******************************************************************

  3.     Copyright &#169; Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.

  4.     * @projectName   09_tcpclient

  5.     * @brief         mainwindow.cpp

  6.     * @author        Deng Zhimao

  7.     * @email         1252699831@qq.com

  8.     * @net            www.openedv.com

  9.     * @date           2021-04-13

  10.     *******************************************************************/

  11. 1   #include "mainwindow.h"

  12. 2

  13. 3   MainWindow::MainWindow(QWidget *parent)

  14. 4       : QMainWindow(parent)

  15. 5   {

  16. 6       /* 设置主窗体的位置与大小 */

  17. 7       this->setGeometry(0, 0, 800, 480);

  18. 8

  19. 9       /* tcp套接字 */

  20. 10      tcpSocket = new QTcpSocket(this);

  21. 11

  22. 12      /* 开始监听按钮 */

  23. 13      pushButton[0] = new QPushButton();

  24. 14      /* 停止监听按钮 */

  25. 15      pushButton[1] = new QPushButton();

  26. 16      /* 清空聊天文本按钮 */

  27. 17      pushButton[2] = new QPushButton();

  28. 18      /* 发送消息按钮 */

  29. 19      pushButton[3] = new QPushButton();

  30. 20

  31. 21      /* 水平布局一 */

  32. 22      hBoxLayout[0] = new QHBoxLayout();

  33. 23      /* 水平布局二 */

  34. 24      hBoxLayout[1] = new QHBoxLayout();

  35. 25      /* 水平布局三 */

  36. 26      hBoxLayout[2] = new QHBoxLayout();

  37. 27      /* 水平布局四 */

  38. 28      hBoxLayout[3] = new QHBoxLayout();

  39. 29

  40. 30      /* 水平容器一 */

  41. 31      hWidget[0] =  new QWidget();

  42. 32      /* 水平容器二 */

  43. 33      hWidget[1] =  new QWidget();

  44. 34      /* 水平容器三 */

  45. 35      hWidget[2] =  new QWidget();

  46. 36

  47. 37

  48. 38      vWidget = new QWidget();

  49. 39      vBoxLayout = new QVBoxLayout();

  50. 40

  51. 41      /* 标签实例化 */

  52. 42      label[0] = new QLabel();

  53. 43      label[1] = new QLabel();

  54. 44

  55. 45      lineEdit = new QLineEdit();

  56. 46      comboBox = new QComboBox();

  57. 47      spinBox = new QSpinBox();

  58. 48      textBrowser = new QTextBrowser();

  59. 49

  60. 50      label[0]->setText("服务器地址:");

  61. 51      label[1]->setText("服务器端口:");

  62. 52

  63. 53      /* 设置标签根据文本文字大小自适应大小  */

  64. 54      label[0]->setSizePolicy(QSizePolicy::Fixed,

  65. 55                              QSizePolicy::Fixed);

  66. 56      label[1]->setSizePolicy(QSizePolicy::Fixed,

  67. 57                              QSizePolicy::Fixed);

  68. 58

  69. 59      /* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */

  70. 60      spinBox->setRange(10000, 99999);

  71. 61

  72. 62      pushButton[0]->setText("连接服务器");

  73. 63      pushButton[1]->setText("断开连接");

  74. 64      pushButton[2]->setText("清空文本");

  75. 65      pushButton[3]->setText("发送消息");

  76. 66

  77. 67      /* 设置停止监听状态不可用 */

  78. 68      pushButton[1]->setEnabled(false);

  79. 69

  80. 70      /* 设置输入框默认的文本 */

  81. 71      lineEdit->setText("广州星翼电子科技有限公司");

  82. 72

  83. 73      /* 水平布局一添加内容 */

  84. 74      hBoxLayout[0]->addWidget(pushButton[0]);

  85. 75      hBoxLayout[0]->addWidget(pushButton[1]);

  86. 76      hBoxLayout[0]->addWidget(pushButton[2]);

  87. 77

  88. 78      /* 设置水平容器的布局为水平布局一 */

  89. 79      hWidget[0]->setLayout(hBoxLayout[0]);

  90. 80

  91. 81      hBoxLayout[1]->addWidget(label[0]);

  92. 82      hBoxLayout[1]->addWidget(comboBox);

  93. 83      hBoxLayout[1]->addWidget(label[1]);

  94. 84      hBoxLayout[1]->addWidget(spinBox);

  95. 85

  96. 86      /* 设置水平容器的布局为水平布局二 */

  97. 87      hWidget[1]->setLayout(hBoxLayout[1]);

  98. 88

  99. 89      /* 水平布局三添加内容 */

  100. 90      hBoxLayout[2]->addWidget(lineEdit);

  101. 91      hBoxLayout[2]->addWidget(pushButton[3]);

  102. 92

  103. 93      /* 设置水平容器三的布局为水平布局一 */

  104. 94      hWidget[2]->setLayout(hBoxLayout[2]);

  105. 95

  106. 96      /* 垂直布局添加内容 */

  107. 97      vBoxLayout->addWidget(textBrowser);

  108. 98      vBoxLayout->addWidget(hWidget[1]);

  109. 99      vBoxLayout->addWidget(hWidget[0]);

  110. 100     vBoxLayout->addWidget(hWidget[2]);

  111. 101

  112. 102     /* 设置垂直容器的布局为垂直布局 */

  113. 103     vWidget->setLayout(vBoxLayout);

  114. 104

  115. 105     /* 居中显示 */

  116. 106     setCentralWidget(vWidget);

  117. 107

  118. 108     /* 获取本地ip */

  119. 109     getLocalHostIP();

  120. 110

  121. 111     /* 信号槽连接 */

  122. 112     connect(pushButton[0], SIGNAL(clicked()),

  123. 113             this, SLOT(toConnect()));

  124. 114     connect(pushButton[1], SIGNAL(clicked()),

  125. 115             this, SLOT(toDisConnect()));

  126. 116     connect(pushButton[2], SIGNAL(clicked()),

  127. 117             this, SLOT(clearTextBrowser()));

  128. 118     connect(pushButton[3], SIGNAL(clicked()),

  129. 119             this, SLOT(sendMessages()));

  130. 120     connect(tcpSocket, SIGNAL(connected()),

  131. 121             this, SLOT(connected()));

  132. 122     connect(tcpSocket, SIGNAL(disconnected()),

  133. 123             this, SLOT(disconnected()));

  134. 124     connect(tcpSocket, SIGNAL(readyRead()),

  135. 125             this, SLOT(receiveMessages()));

  136. 126     connect(tcpSocket,

  137. 127             SIGNAL(stateChanged(QAbstractSocket::SocketState)),

  138. 128             this,

  139. 129             SLOT(socketStateChange(QAbstractSocket::SocketState)));

  140. 130 }

  141. 131

  142. 132 MainWindow::~MainWindow()

  143. 133 {

  144. 134 }

  145. 135

  146. 136 void MainWindow::toConnect()

  147. 137 {

  148. 138     /* 如果连接状态还没有连接 */

  149. 139     if (tcpSocket->state() != tcpSocket->ConnectedState) {

  150. 140         /* 指定IP地址和端口连接 */

  151. 141         tcpSocket->connectToHost(IPlist[comboBox->currentIndex()],

  152. 142                 spinBox->value());

  153. 143     }

  154. 144 }

  155. 145

  156. 146 void MainWindow::toDisConnect()

  157. 147 {

  158. 148     /* 断开连接 */

  159. 149     tcpSocket->disconnectFromHost();

  160. 150

  161. 151     /* 关闭socket*/

  162. 152     tcpSocket->close();

  163. 153 }

  164. 154

  165. 155 void MainWindow::connected()

  166. 156 {

  167. 157     /* 显示已经连接 */

  168. 158     textBrowser->append("已经连上服务端");

  169. 159

  170. 160     /* 设置按钮与下拉列表框的状态 */

  171. 161     pushButton[0]->setEnabled(false);

  172. 162     pushButton[1]->setEnabled(true);

  173. 163     comboBox->setEnabled(false);

  174. 164     spinBox->setEnabled(false);

  175. 165 }

  176. 166

  177. 167 void MainWindow::disconnected()

  178. 168 {

  179. 169     /* 显示已经断开连接 */

  180. 170     textBrowser->append("已经断开服务端");

  181. 171

  182. 172     /* 设置按钮与下拉列表框的状态  */

  183. 173     pushButton[1]->setEnabled(false);

  184. 174     pushButton[0]->setEnabled(true);

  185. 175     comboBox->setEnabled(true);

  186. 176     spinBox->setEnabled(true);

  187. 177 }

  188. 178

  189. 179 /* 获取本地IP */

  190. 180 void MainWindow::getLocalHostIP()

  191. 181 {

  192. 182     // /* 获取主机的名称 */

  193. 183     // QString hostName = QHostInfo::localHostName();

  194. 184

  195. 185     // /* 主机的信息 */

  196. 186     // QHostInfo hostInfo = QHostInfo::fromName(hostName);

  197. 187

  198. 188     // /* ip列表,addresses返回ip地址列表,注意主机应能从路由器获取到

  199. 189     // * IP,否则可能返回空的列表(ubuntu用此方法只能获取到环回IP) */

  200. 190     // IPlist = hostInfo.addresses();

  201. 191     // qDebug()<<IPlist<<endl;

  202. 192

  203. 193     // /* 遍历IPlist */

  204. 194     // foreach (QHostAddress ip, IPlist) {

  205. 195     //      if (ip.protocol() == QAbstractSocket::IPv4Protocol)

  206. 196     //          comboBox->addItem(ip.toString());

  207. 197     //    }

  208. 198

  209. 199     /* 获取所有的网络接口,

  210. 200      * QNetworkInterface类提供主机的IP地址和网络接口的列表 */

  211. 201     QList<QNetworkInterface> list

  212. 202             = QNetworkInterface::allInterfaces();

  213. 203

  214. 204     /* 遍历list */

  215. 205     foreach (QNetworkInterface interface, list) {

  216. 206

  217. 207         /* QNetworkAddressEntry类存储IP地址子网掩码和广播地址 */

  218. 208         QList<QNetworkAddressEntry> entryList

  219. 209                 = interface.addressEntries();

  220. 210

  221. 211         /* 遍历entryList */

  222. 212         foreach (QNetworkAddressEntry entry, entryList) {

  223. 213             /* 过滤IPv6地址,只留下IPv4 */

  224. 214             if (entry.ip().protocol() ==

  225. 215                     QAbstractSocket::IPv4Protocol) {

  226. 216                 comboBox->addItem(entry.ip().toString());

  227. 217                 /* 添加到IP列表中 */

  228. 218                 IPlist<<entry.ip();

  229. 219             }

  230. 220         }

  231. 221     }

  232. 222 }

  233. 223

  234. 224 /* 清除文本浏览框里的内容 */

  235. 225 void MainWindow::clearTextBrowser()

  236. 226 {

  237. 227     /* 清除文本浏览器的内容 */

  238. 228     textBrowser->clear();

  239. 229 }

  240. 230

  241. 231 /* 客户端接收消息 */

  242. 232 void MainWindow::receiveMessages()

  243. 233 {

  244. 234     /* 读取接收到的消息 */

  245. 235     QString messages = tcpSocket->readAll();

  246. 236     textBrowser->append("服务端:" + messages);

  247. 237 }

  248. 238

  249. 239 /* 客户端发送消息 */

  250. 240 void MainWindow::sendMessages()

  251. 241 {

  252. 242     if(NULL == tcpSocket)

  253. 243         return;

  254. 244

  255. 245     if(tcpSocket->state() == tcpSocket->ConnectedState) {

  256. 246         /* 客户端显示发送的消息 */

  257. 247         textBrowser->append("客户端:" + lineEdit->text());

  258. 248

  259. 249         /* 发送消息 */

  260. 250         tcpSocket->write(lineEdit->text().toUtf8().data());

  261. 251     }

  262. 252 }

  263. 253

  264. 254 /* 客户端状态改变 */

  265. 255 void MainWindow::socketStateChange(QAbstractSocket::SocketState state)

  266. 256 {

  267. 257     switch (state) {

  268. 258     case QAbstractSocket::UnconnectedState:

  269. 259         textBrowser->append("scoket状态:UnconnectedState");

  270. 260         break;

  271. 261     case QAbstractSocket::ConnectedState:

  272. 262         textBrowser->append("scoket状态:ConnectedState");

  273. 263         break;

  274. 264     case QAbstractSocket::ConnectingState:

  275. 265         textBrowser->append("scoket状态:ConnectingState");

  276. 266         break;

  277. 267     case QAbstractSocket::HostLookupState:

  278. 268         textBrowser->append("scoket状态:HostLookupState");

  279. 269         break;

  280. 270     case QAbstractSocket::ClosingState:

  281. 271         textBrowser->append("scoket状态:ClosingState");

  282. 272         break;

  283. 273     case QAbstractSocket::ListeningState:

  284. 274         textBrowser->append("scoket状态:ListeningState");

  285. 275         break;

  286. 276     case QAbstractSocket::BoundState:

  287. 277         textBrowser->append("scoket状态:BoundState");

  288. 278         break;

  289. 279     default:

  290. 280         break;

  291. 281     }

  292. 282 }
复制代码
       上面的代码主要是客户端开使用connectToHost通过IP地址和端口与服务端连接,如果连接成功,就会发射connected ()信号,同时也连接到接收消息的信号与槽函数。点击发送消息按钮就可以使用tcpSocket发送消息。注意发送消息和接收消息都是通过tcpSocket的read()和write()进行。

11.2.4 程序运行效果
开启服务端后,需要选择本地监听的IP地址和监听的端口(特别需要注意,不要选择监听的端口与本地主机的已经使用的端口,所以编者把端口号设置的特别大,查看本地已经使用的端口号可以使用netstat指令。)
启动客户端后,选择需要连接的服务器IP地址和服务器监听的端口。点击连接后就可以相互发送消息了。
注意服务端和客户端都本例都是选择了本地环回IP 127.0.0.1测试。也可以选择本地的其他IP地址进行测试。
      TCP服务端:
image006.png

      TCP客户端:
image008.jpg




正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-8 13:31

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表