TCP 通信
TCP 简介
TCP协议概述
-
全称:Transmission Control Protocol(传输控制协议)
-
特性:面向连接、可靠、基于字节流的传输层通信协议
TCP通信流程
-
建立连接:TCP通信必须先建立连接
-
通信端:分为客户端和服务端
服务端操作
-
监听端口:服务端监听某个端口,等待客户端连接
-
建立连接:当有客户端连接到来时,服务端建立新的socket连接
客户端操作
-
连接服务端:客户端通过指定的IP地址和端口号连接服务端
-
数据收发:成功建立连接后,客户端和服务端即可进行数据的发送和接收
Qt中的TCP通信
-
Socket处理:Qt将socket当成输入输出流来处理
-
数据收发方法:使用read()和write()函数进行数据的读取和写入
-
区别:与常见的send()和recv()函数有区别
TCP 服务端应用实例
大致流程
-
获取本地IP地址
- 首先需要获取本地的IP地址,这是为了确保服务端能够监听正确的网络接口
-
创建套接字和服务端
-
创建一个tcpSocket套接字,用于客户端与服务端之间的通信
-
创建一个tcpServer服务端,用于监听客户端的连接请求
-
-
监听操作
-
点击“监听”按钮后,程序将开始监听本地的主机IP地址和指定的端口
-
服务端进入等待状态,准备接受客户端的连接请求
-
-
结合客户端使用
-
需要与客户端程序配合使用
-
客户端程序将尝试连接到服务端的IP地址和端口,以便进行数据交换
-
项目文件文件第一行添加的代码部分如下
- 1 QT += core gui network
mainwindow.h
-
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18
19 class MainWindow : public QMainWindow
20 {
21 Q_OBJECT
22
23 public:
24 MainWindow(QWidget parent = nullptr);
25 ~MainWindow();
26
27 private:
28 / tcp 服务器 */
29 QTcpServer tcpServer;
30
31 / 通信套接字 */
32 QTcpSocket tcpSocket;
33
34 / 按钮 */
35 QPushButton pushButton[4];
36
37 / 标签文本 */
38 QLabel label[2];
39
40 / 水平容器 */
41 QWidget hWidget[3];
42
43 / 水平布局 */
44 QHBoxLayout hBoxLayout[3];
45
46 / 垂直容器 */
47 QWidget vWidget;
48
49 / 垂直布局 */
50 QVBoxLayout vBoxLayout;
51
52 / 文本浏览框 */
53 QTextBrowser textBrowser;
54
55 / 用于显示本地 ip */
56 QComboBox comboBox;
57
58 / 用于选择端口 */
59 QSpinBox spinBox;
60
61 / 文本输入框 */
62 QLineEdit lineEdit;
63
64 / 存储本地的 ip 列表地址 /
65 QList IPlist;
66
67 / 获取本地的所有 ip /
68 void getLocalHostIP();
69
70 private slots:
71 / 客户端连接处理槽函数 /
72 void clientConnected();
73
74 / 开始监听槽函数 /
75 void startListen();
76
77 / 停止监听槽函数 /
78 void stopListen();
79
80 / 清除文本框时的内容 /
81 void clearTextBrowser();
82
83 / 接收到消息 /
84 void receiveMessages();
85
86 / 发送消息 /
87 void sendMessages();
88
89 / 连接状态改变槽函数 */
90 void socketStateChange(QAbstractSocket::SocketState);
91 };
92 #endif // MAINWINDOW_H -
主要是声明界面用的元素,及一些槽函数。重点是声明 tcpServer 和 tcpSocket
mainwindow.cpp
-
1 #include “mainwindow.h”
2
3 MainWindow::MainWindow(QWidget parent)
4 : QMainWindow(parent)
5{
6 / 设置主窗体的位置与大小 /
7 this->setGeometry(0, 0, 800, 480);
8
9 / 实例化 tcp 服务器与 tcp 套接字 /
10 tcpServer = new QTcpServer(this);
11 tcpSocket = new QTcpSocket(this);
12
13 / 开始监听按钮 /
14 pushButton[0] = new QPushButton();
15 / 停止监听按钮 /
16 pushButton[1] = new QPushButton();
17 / 清空聊天文本按钮 /
18 pushButton[2] = new QPushButton();
19 / 发送消息按钮 /
20 pushButton[3] = new QPushButton();
21
22 / 水平布局一 /
23 hBoxLayout[0] = new QHBoxLayout();
24 / 水平布局二 /
25 hBoxLayout[1] = new QHBoxLayout();
26 / 水平布局三 /
27 hBoxLayout[2] = new QHBoxLayout();
28 / 水平布局四 /
29 hBoxLayout[3] = new QHBoxLayout();
30
31 / 水平容器一 /
32 hWidget[0] = new QWidget();
33 / 水平容器二 /
34 hWidget[1] = new QWidget();
35 / 水平容器三 /
36 hWidget[2] = new QWidget();
37
38 vWidget = new QWidget();
39 vBoxLayout = new QVBoxLayout();
40
41 / 标签实例化 /
42 label[0] = new QLabel();
43 label[1] = new QLabel();
44
45 lineEdit = new QLineEdit();
46 comboBox = new QComboBox();
47 spinBox = new QSpinBox();
48 textBrowser = new QTextBrowser();
49
50 label[0]->setText(“监听 IP 地址:”);
51 label[1]->setText(“监听端口:”);
52
53 / 设置标签根据文本文字大小自适应大小 /
54 label[0]->setSizePolicy(QSizePolicy::Fixed,
55 QSizePolicy::Fixed);
56 label[1]->setSizePolicy(QSizePolicy::Fixed,
57 QSizePolicy::Fixed);
58
59 / 设置端口号的范围,注意不要与主机的已使用的端口号冲突 /
60 spinBox->setRange(10000, 99999);
61
62 pushButton[0]->setText(“开始监听”);
63 pushButton[1]->setText(“停止监听”);
64 pushButton[2]->setText(“清空文本”);
65 pushButton[3]->setText(“发送消息”);
66
67 / 设置停止监听状态不可用 /
68 pushButton[1]->setEnabled(false);
69
70 / 设置输入框默认的文本 /
71 lineEdit->setText(“www.openedv.com 正点原子论坛”);
72
73 / 水平布局一添加内容 /
74 hBoxLayout[0]->addWidget(pushButton[0]);
75 hBoxLayout[0]->addWidget(pushButton[1]);
76 hBoxLayout[0]->addWidget(pushButton[2]);
77
78 / 设置水平容器一的布局为水平布局一 /
79 hWidget[0]->setLayout(hBoxLayout[0]);
80
81 / 水平布局二添加内容 /
82 hBoxLayout[1]->addWidget(label[0]);
83 hBoxLayout[1]->addWidget(comboBox);
84 hBoxLayout[1]->addWidget(label[1]);
85 hBoxLayout[1]->addWidget(spinBox);
86
87 / 设置水平容器二的布局为水平布局二 /
88 hWidget[1]->setLayout(hBoxLayout[1]);
89
90 / 水平布局三添加内容 /
91 hBoxLayout[2]->addWidget(lineEdit);
92 hBoxLayout[2]->addWidget(pushButton[3]);
93
94 / 设置水平容器三的布局为水平布局一 /
95 hWidget[2]->setLayout(hBoxLayout[2]);
96
97 / 垂直布局添加内容 /
98 vBoxLayout->addWidget(textBrowser);
99 vBoxLayout->addWidget(hWidget[1]);
100 vBoxLayout->addWidget(hWidget[0]);
101 vBoxLayout->addWidget(hWidget[2]);
102
103 / 设置垂直容器的布局为垂直布局 /
104 vWidget->setLayout(vBoxLayout);
105
106 / 居中显示 /
107 setCentralWidget(vWidget);
108
109 / 获取本地 ip /
110 getLocalHostIP();
111
112 / 信号槽连接 /
113 connect(pushButton[0], SIGNAL(clicked()),
114 this, SLOT(startListen()));
115 connect(pushButton[1], SIGNAL(clicked()),
116 this, SLOT(stopListen()));
117 connect(pushButton[2], SIGNAL(clicked()),
118 this, SLOT(clearTextBrowser()));
119 connect(pushButton[3], SIGNAL(clicked()),
120 this, SLOT(sendMessages()));
121 connect(tcpServer, SIGNAL(newConnection()),
122 this, SLOT(clientConnected()));
123 }
124
125 MainWindow::~MainWindow()
126 {
127 }
128
129 / 新的客户端连接 /
130 void MainWindow::clientConnected()
131 {
132 / 获取客户端的套接字 /
133 tcpSocket = tcpServer->nextPendingConnection();
134 / 客户端的 ip 信息 /
135 QString ip = tcpSocket->peerAddress().toString();
136 / 客户端的端口信息 /
137 quint16 port = tcpSocket->peerPort();
138 / 在文本浏览框里显示出客户端的连接信息 /
139 textBrowser->append(“客户端已连接”);
140 textBrowser->append(“客户端 ip 地址:”
141 + ip);
142 textBrowser->append(“客户端端口:”
143 + QString::number(port));
144
145 connect(tcpSocket, SIGNAL(readyRead()),
146 this, SLOT(receiveMessages()));
147 connect(tcpSocket,
148 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
149 this,
150 SLOT(socketStateChange(QAbstractSocket::SocketState)));
151 }
152
153 / 获取本地 IP /
154 void MainWindow::getLocalHostIP()
155 {
156 // / 获取主机的名称 /
157 // QString hostName = QHostInfo::localHostName();
158
159 // / 主机的信息 /
160 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
161
162 // / ip 列表,addresses 返回 ip 地址列表,注意主机应能从路由器获取到
163 // * IP,否则可能返回空的列表(ubuntu 用此方法只能获取到环回 IP) /
164 // IPlist = hostInfo.addresses();
165 // qDebug()<<IPlist<<endl;
166
167 // / 遍历 IPlist /
168 // foreach (QHostAddress ip, IPlist) {
169 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
170 // comboBox->addItem(ip.toString());
171 // }
172
173 / 获取所有的网络接口,
174 * QNetworkInterface 类提供主机的 IP 地址和网络接口的列表 /
175 QList list
176 = QNetworkInterface::allInterfaces();
177
178 / 遍历 list /
179 foreach (QNetworkInterface interface, list) {
180
181 / QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 /
182 QList entryList
183 = interface.addressEntries();
184
185 / 遍历 entryList /
186 foreach (QNetworkAddressEntry entry, entryList) {
187 / 过滤 IPv6 地址,只留下 IPv4 /
188 if (entry.ip().protocol() ==
189 QAbstractSocket::IPv4Protocol) {
190 comboBox->addItem(entry.ip().toString());
191 / 添加到 IP 列表中 /
192 IPlist<<entry.ip();
193 }
194 }
195 }
196 }
197
198 / 开始监听 /
199 void MainWindow::startListen()
200 {
201 / 需要判断当前主机是否有 IP 项 /
202 if (comboBox->currentIndex() != -1) {
203 qDebug()<<“start listen”<<endl;
204 tcpServer->listen(IPlist[comboBox->currentIndex()],
205 spinBox->value());
206
207 / 设置按钮与下拉列表框的状态 /
208 pushButton[0]->setEnabled(false);
209 pushButton[1]->setEnabled(true);
210 comboBox->setEnabled(false);
211 spinBox->setEnabled(false);
212
213 / 在文本浏览框里显示出服务端 /
214 textBrowser->append(“服务器 IP 地址:”
215 + comboBox->currentText());
216 textBrowser->append(“正在监听端口:”
217 + spinBox->text());
218 }
219 }
220
221 / 停止监听 /
222 void MainWindow::stopListen()
223 {
224 qDebug()<<“stop listen”<<endl;
225 / 停止监听 /
226 tcpServer->close();
227
228 / 如果是连接上了也应该断开,如果不断开客户端还能继续发送信息,
229 * 因为 socket 未断开,还在监听上一次端口 /
230 if (tcpSocket->state() == tcpSocket->ConnectedState)
231 tcpSocket->disconnectFromHost();
232
233 / 设置按钮与下拉列表框的状态 /
234 pushButton[1]->setEnabled(false);
235 pushButton[0]->setEnabled(true);
236 comboBox->setEnabled(true);
237 spinBox->setEnabled(true);
238
239 / 将停止监听的信息添加到文本浏览框中 /
240 textBrowser->append(“已停止监听端口:”
241 + spinBox->text());
242 }
243
244 / 清除文本浏览框里的内容 /
245 void MainWindow::clearTextBrowser()
246 {
247 / 清除文本浏览器的内容 /
248 textBrowser->clear();
249 }
250
251 / 服务端接收消息 /
252 void MainWindow::receiveMessages()
253 {
254 / 读取接收到的消息 /
255 QString messages = “客户端:” + tcpSocket->readAll();
256 textBrowser->append(messages);
257 }
258
259 / 服务端发送消息 /
260 void MainWindow::sendMessages()
261 {
262 if(NULL == tcpSocket)
263 return;
264
265 / 如果已经连接 /
266 if(tcpSocket->state() == tcpSocket->ConnectedState) {
267 / 发送消息 /
268 tcpSocket->write(lineEdit->text().toUtf8().data());
269
270 / 在服务端插入发送的消息 /
271 textBrowser->append(“服务端:” + lineEdit->text());
272 }
273 }
274
275 / 服务端状态改变 */
276 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
277 {
278 switch (state) {
279 case QAbstractSocket::UnconnectedState:
280 textBrowser->append(“scoket 状态:UnconnectedState”);
281 break;
282 case QAbstractSocket::ConnectedState:
283 textBrowser->append(“scoket 状态:ConnectedState”);
284 break;
285 case QAbstractSocket::ConnectingState:
286 textBrowser->append(“scoket 状态:ConnectingState”);
287 break;
288 case QAbstractSocket::HostLookupState:
289 textBrowser->append(“scoket 状态:HostLookupState”);
290 break;
291 case QAbstractSocket::ClosingState:
292 textBrowser->append(“scoket 状态:ClosingState”);
293 break;
294 case QAbstractSocket::ListeningState:
295 textBrowser->append(“scoket 状态:ListeningState”);
296 break;
297 case QAbstractSocket::BoundState:
298 textBrowser->append(“scoket 状态:BoundState”);
299 break;
300 default:
301 break;
302 }
303 } -
服务端开启监听后,通过newConnection()信号处理客户端连接,连接到接收消息的槽函数,通过点击按钮进行消息发送,一切的消息收发都通过tcpSocket的read()和write()函数完成
TCP 客户端应用实例
大致流程
-
获取本地IP地址
- 程序首先需要获取本地的IP地址,这一步是为了确保客户端能够知道自己的网络接口信息
-
创建套接字
- 创建一个tcpSocket套接字,用于与服务端之间的通信
-
连接服务端
- 使用tcpSocket的connectToHost函数,指定服务端的主机IP地址和端口,尝试建立连接
-
开始通信
- 一旦连接成功,客户端与服务端即可进行相互通信
项目文件文件第一行添加的代码部分如下
- 1 QT += core gui network
mainwindow.h
-
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18
19 class MainWindow : public QMainWindow
20 {
21 Q_OBJECT
22
23 public:
24 MainWindow(QWidget parent = nullptr);
25 ~MainWindow();
26
27 private:
28 / 通信套接字 */
29 QTcpSocket tcpSocket;
30
31 / 按钮 */
32 QPushButton pushButton[4];
33
34 / 标签文本 */
35 QLabel label[2];
36
37 / 水平容器 */
38 QWidget hWidget[3];
39
40 / 水平布局 */
41 QHBoxLayout hBoxLayout[3];
42
43 / 垂直容器 */
44 QWidget vWidget;
45
46 / 垂直布局 */
47 QVBoxLayout vBoxLayout;
48
49 / 文本浏览框 */
50 QTextBrowser textBrowser;
51
52 / 用于显示本地 ip */
53 QComboBox comboBox;
54
55 / 用于选择端口 */
56 QSpinBox spinBox;
57
58 / 文本输入框 */
59 QLineEdit lineEdit;
60
61 / 存储本地的 ip 列表地址 /
62 QList IPlist;
63
64 / 获取本地的所有 ip /
65 void getLocalHostIP();
66
67 private slots:
68 / 连接 /
69 void toConnect();
70
71 / 断开连接 /
72 void toDisConnect();
73
74 / 已连接 /
75 void connected();
76
77 / 已断开连接 /
78 void disconnected();
79
80 / 清除文本框时的内容 /
81 void clearTextBrowser();
82
83 / 接收到消息 /
84 void receiveMessages();
85
86 / 发送消息 /
87 void sendMessages();
88
89 / 连接状态改变槽函数 */
90 void socketStateChange(QAbstractSocket::SocketState);
91 };
92 #endif // MAINWINDOW_H -
主要是声明界面用的元素,及一些槽函数。重点是声明 tcpSocket
mainwindow.cpp
-
1 #include “mainwindow.h”
2
3 MainWindow::MainWindow(QWidget parent)
4 : QMainWindow(parent)
5{
6 / 设置主窗体的位置与大小 /
7 this->setGeometry(0, 0, 800, 480);
8
9 / tcp 套接字 /
10 tcpSocket = new QTcpSocket(this);
11
12 / 开始监听按钮 /
13 pushButton[0] = new QPushButton();
14 / 停止监听按钮 /
15 pushButton[1] = new QPushButton();
16 / 清空聊天文本按钮 /
17 pushButton[2] = new QPushButton();
18 / 发送消息按钮 /
19 pushButton[3] = new QPushButton();
20
21 / 水平布局一 /
22 hBoxLayout[0] = new QHBoxLayout();
23 / 水平布局二 /
24 hBoxLayout[1] = new QHBoxLayout();
25 / 水平布局三 /
26 hBoxLayout[2] = new QHBoxLayout();
27 / 水平布局四 /
28 hBoxLayout[3] = new QHBoxLayout();
29
30 / 水平容器一 /
31 hWidget[0] = new QWidget();
32 / 水平容器二 /
33 hWidget[1] = new QWidget();
34 / 水平容器三 /
35 hWidget[2] = new QWidget();
36
37
38 vWidget = new QWidget();
39 vBoxLayout = new QVBoxLayout();
40
41 / 标签实例化 /
42 label[0] = new QLabel();
43 label[1] = new QLabel();
44
45 lineEdit = new QLineEdit();
46 comboBox = new QComboBox();
47 spinBox = new QSpinBox();
48 textBrowser = new QTextBrowser();
49
50 label[0]->setText(“服务器地址:”);
51 label[1]->setText(“服务器端口:”);
52
53 / 设置标签根据文本文字大小自适应大小 /
54 label[0]->setSizePolicy(QSizePolicy::Fixed,
55 QSizePolicy::Fixed);
56 label[1]->setSizePolicy(QSizePolicy::Fixed,
57 QSizePolicy::Fixed);
58
59 / 设置端口号的范围,注意不要与主机的已使用的端口号冲突 /
60 spinBox->setRange(10000, 99999);
61
62 pushButton[0]->setText(“连接服务器”);
63 pushButton[1]->setText(“断开连接”);
64 pushButton[2]->setText(“清空文本”);
65 pushButton[3]->setText(“发送消息”);
66
67 / 设置停止监听状态不可用 /
68 pushButton[1]->setEnabled(false);
69
70 / 设置输入框默认的文本 /
71 lineEdit->setText(“广州星翼电子科技有限公司”);
72
73 / 水平布局一添加内容 /
74 hBoxLayout[0]->addWidget(pushButton[0]);
75 hBoxLayout[0]->addWidget(pushButton[1]);
76 hBoxLayout[0]->addWidget(pushButton[2]);
77
78 / 设置水平容器的布局为水平布局一 /
79 hWidget[0]->setLayout(hBoxLayout[0]);
80
81 hBoxLayout[1]->addWidget(label[0]);
82 hBoxLayout[1]->addWidget(comboBox);
83 hBoxLayout[1]->addWidget(label[1]);
84 hBoxLayout[1]->addWidget(spinBox);
85
86 / 设置水平容器的布局为水平布局二 /
87 hWidget[1]->setLayout(hBoxLayout[1]);
88
89 / 水平布局三添加内容 /
90 hBoxLayout[2]->addWidget(lineEdit);
91 hBoxLayout[2]->addWidget(pushButton[3]);
92
93 / 设置水平容器三的布局为水平布局一 /
94 hWidget[2]->setLayout(hBoxLayout[2]);
95
96 / 垂直布局添加内容 /
97 vBoxLayout->addWidget(textBrowser);
98 vBoxLayout->addWidget(hWidget[1]);
99 vBoxLayout->addWidget(hWidget[0]);
100 vBoxLayout->addWidget(hWidget[2]);
101
102 / 设置垂直容器的布局为垂直布局 /
103 vWidget->setLayout(vBoxLayout);
104
105 / 居中显示 /
106 setCentralWidget(vWidget);
107
108 / 获取本地 ip /
109 getLocalHostIP();
110
111 / 信号槽连接 /
112 connect(pushButton[0], SIGNAL(clicked()),
113 this, SLOT(toConnect()));
114 connect(pushButton[1], SIGNAL(clicked()),
115 this, SLOT(toDisConnect()));
116 connect(pushButton[2], SIGNAL(clicked()),
117 this, SLOT(clearTextBrowser()));
118 connect(pushButton[3], SIGNAL(clicked()),
119 this, SLOT(sendMessages()));
120 connect(tcpSocket, SIGNAL(connected()),
121 this, SLOT(connected()));
122 connect(tcpSocket, SIGNAL(disconnected()),
123 this, SLOT(disconnected()));
124 connect(tcpSocket, SIGNAL(readyRead()),
125 this, SLOT(receiveMessages()));
126 connect(tcpSocket,
127 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
128 this,
129 SLOT(socketStateChange(QAbstractSocket::SocketState)));
130 }
131
132 MainWindow::~MainWindow()
133 {
134 }
135
136 void MainWindow::toConnect()
137 {
138 / 如果连接状态还没有连接 /
139 if (tcpSocket->state() != tcpSocket->ConnectedState) {
140 / 指定 IP 地址和端口连接 /
141 tcpSocket->connectToHost(IPlist[comboBox->currentIndex()],
142 spinBox->value());
143 }
144 }
145
146 void MainWindow::toDisConnect()
147 {
148 / 断开连接 /
149 tcpSocket->disconnectFromHost();
150
151 / 关闭 socket*/
152 tcpSocket->close();
153 }
154
155 void MainWindow::connected()
156 {
157 /* 显示已经连接 /
158 textBrowser->append(“已经连上服务端”);
159
160 / 设置按钮与下拉列表框的状态 /
161 pushButton[0]->setEnabled(false);
162 pushButton[1]->setEnabled(true);
163 comboBox->setEnabled(false);
164 spinBox->setEnabled(false);
165 }
166
167 void MainWindow::disconnected()
168 {
169 / 显示已经断开连接 /
170 textBrowser->append(“已经断开服务端”);
171
172 / 设置按钮与下拉列表框的状态 /
173 pushButton[1]->setEnabled(false);
174 pushButton[0]->setEnabled(true);
175 comboBox->setEnabled(true);
176 spinBox->setEnabled(true);
177 }
178
179 / 获取本地 IP /
180 void MainWindow::getLocalHostIP()
181 {
182 // / 获取主机的名称 /
183 // QString hostName = QHostInfo::localHostName();
184
185 // / 主机的信息 /
186 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
187
188 // / ip 列表,addresses 返回 ip 地址列表,注意主机应能从路由器获取到
189 // * IP,否则可能返回空的列表(ubuntu 用此方法只能获取到环回 IP) /
190 // IPlist = hostInfo.addresses();
191 // qDebug()<<IPlist<<endl;
192
193 // / 遍历 IPlist /
194 // foreach (QHostAddress ip, IPlist) {
195 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
196 // comboBox->addItem(ip.toString());
197 // }
198
199 / 获取所有的网络接口,
200 * QNetworkInterface 类提供主机的 IP 地址和网络接口的列表 /
201 QList list
202 = QNetworkInterface::allInterfaces();
203
204 / 遍历 list /
205 foreach (QNetworkInterface interface, list) {
206
207 / QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 /
208 QList entryList
209 = interface.addressEntries();
210
211 / 遍历 entryList /
212 foreach (QNetworkAddressEntry entry, entryList) {
213 / 过滤 IPv6 地址,只留下 IPv4 /
214 if (entry.ip().protocol() ==
215 QAbstractSocket::IPv4Protocol) {
216 comboBox->addItem(entry.ip().toString());
217 / 添加到 IP 列表中 /
218 IPlist<<entry.ip();
219 }
220 }
221 }
222 }
223
224 / 清除文本浏览框里的内容 /
225 void MainWindow::clearTextBrowser()
226 {
227 / 清除文本浏览器的内容 /
228 textBrowser->clear();
229 }
230
231 / 客户端接收消息 /
232 void MainWindow::receiveMessages()
233 {
234 / 读取接收到的消息 /
235 QString messages = tcpSocket->readAll();
236 textBrowser->append(“服务端:” + messages);
237 }
238
239 / 客户端发送消息 /
240 void MainWindow::sendMessages()
241 {
242 if(NULL == tcpSocket)
243 return;
244
245 if(tcpSocket->state() == tcpSocket->ConnectedState) {
246 / 客户端显示发送的消息 /
247 textBrowser->append(“客户端:” + lineEdit->text());
248
249 / 发送消息 /
250 tcpSocket->write(lineEdit->text().toUtf8().data());
251 }
252 }
253
254 / 客户端状态改变 */
255 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
256 {
257 switch (state) {
258 case QAbstractSocket::UnconnectedState:
259 textBrowser->append(“scoket 状态:UnconnectedState”);
260 break;
261 case QAbstractSocket::ConnectedState:
262 textBrowser->append(“scoket 状态:ConnectedState”);
263 break;
264 case QAbstractSocket::ConnectingState:
265 textBrowser->append(“scoket 状态:ConnectingState”);
266 break;
267 case QAbstractSocket::HostLookupState:
268 textBrowser->append(“scoket 状态:HostLookupState”);
269 break;
270 case QAbstractSocket::ClosingState:
271 textBrowser->append(“scoket 状态:ClosingState”);
272 break;
273 case QAbstractSocket::ListeningState:
274 textBrowser->append(“scoket 状态:ListeningState”);
275 break;
276 case QAbstractSocket::BoundState:
277 textBrowser->append(“scoket 状态:BoundState”);
278 break;
279 default:
280 break;
281 }
282 } -
客户端使用connectToHost连接到服务端的IP地址和端口,连接成功后发射connected()信号并连接到相应的槽函数,通过点击按钮发送消息,一切消息的发送和接收都是通过tcpSocket的read()和write()函数完成
程序运行效果
选择服务端监听IP地址和端口
-
开启服务端后,需要选择一个本地IP地址和监听端口
-
特别注意不要选择已经被本地主机使用的端口号,可以通过netstat指令查看本机已经使用的端口号
-
建议设置一个较大的端口号以避免冲突
启动客户端
- 启动客户端后,需要选择要连接的服务器IP地址和服务器监听的端口
点击连接
- 在客户端选择好服务器IP地址和端口后,点击连接按钮
相互发送消息
- 连接成功后,服务端和客户端即可相互发送消息
测试环境
-
服务端和客户端都选择了本地环回IP地址127.0.0.1进行测试
-
也可以选择本地的其他IP地址进行测试