【QT/C++】Qt网络编程进阶:TCP网络编程的基本原理和实际应用(超详细)
- 本文主要详细说明Qt中的TCP网络编程的基本原理和实际应用。
(关注不迷路哈!!!)
文章目录
- 【QT/C++】Qt网络编程进阶:TCP网络编程的基本原理和实际应用(超详细)
-
- [1. 网络基础概念回顾](#1. 网络基础概念回顾)
-
- [1.1 网络类型](#1.1 网络类型)
- [1.2 网络分层模型(4层)](#1.2 网络分层模型(4层))
- [1.3 TCP与UDP协议区别](#1.3 TCP与UDP协议区别)
- [2. Qt TCP网络编程](#2. Qt TCP网络编程)
-
- [2.1 QTcpSocket客户端类](#2.1 QTcpSocket客户端类)
- [2.2 QTcpServer服务器类](#2.2 QTcpServer服务器类)
- [3. 综合示例:TCP聊天程序](#3. 综合示例:TCP聊天程序)
-
- [3.1 服务器端实现](#3.1 服务器端实现)
- [3.2 客户端实现](#3.2 客户端实现)
- [3.3 主函数](#3.3 主函数)
- [3.4 项目文件](#3.4 项目文件)
- 总结
1. 网络基础概念回顾
1.1 网络类型
- 子网网络:局域网,只能进行内网通信
- 公网网络:互联网,支持远程通信
1.2 网络分层模型(4层)
- 应用层:用户自定义的网络数据传输协议(ssh, tftp, ftp, http等)
- 传输层:TCP/UDP传输协议
- 网络层:IPv4/IPv6, 3G, 4G, 5G等网络协议
- 物理层:光纤、路由器、基站、网线、网卡等
1.3 TCP与UDP协议区别
- TCP :可靠传输协议,需要三步握手建立连接,通信时进行数据校验
- 应用场景:文件传输、控制命令传输
- UDP :不可靠传输协议,不需要建立连接,不进行数据校验
- 应用场景:多媒体数据传输(屏幕广播软件、视频网站、直播软件等)
2. Qt TCP网络编程
TCP协议的经典编程模型

2.1 QTcpSocket客户端类
QTcpSocket Class 客户端类
| Header | QTcpSocket 头文件 |
|---|---|
| qmake | QT += network 模块 |
| Inherits | QAbstractSocket 父类 |
| Inherited By | QSctpSocket and QSslSocket 派生类 |

cpp
#include <QTcpSocket>
#include <QHostAddress>
// 在.pro文件中添加: QT += network
// 构造函数
QTcpSocket *socket = new QTcpSocket(this);
// 连接服务器(使用QHostAddress) IP+端口
socket->connectToHost(QHostAddress("192.168.1.100"), 8080);
// 连接服务器(直接使用IP地址字符串)
socket->connectToHost("192.168.1.100", 8080);
// 数据发送
socket->write("Hello Server"); // 发送字符数据
socket->write(data, dataSize); // 发送指定大小的数据
socket->write(QByteArray("Hello")); // 发送QByteArray数据
// 数据接收
qint64 bytesRead = socket->read(buffer, maxSize); // 读取数据到缓冲区
QByteArray data = socket->read(1024); // 读取指定大小数据
QByteArray allData = socket->readAll(); // 读取所有可用数据
// 信号连接
connect(socket, &QTcpSocket::readyRead, this, &MyClass::onReadyRead); // 数据可读信号
connect(socket, &QTcpSocket::connected, this, &MyClass::onConnected); // 连接成功信号
connect(socket, &QTcpSocket::disconnected, this, &MyClass::onDisconnected); // 断开连接信号
connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &MyClass::onError); // 错误信号
2.2 QTcpServer服务器类
QTcpServer Class 服务器类
| Header | QTcpServer 头文件 |
|---|---|
| qmake | QT += network |
| Inherits | QObject |
| Inherited By | QSctpServer |
cpp
#include <QTcpServer>
#include <QTcpSocket>
// 在.pro文件中添加: QT += network
// 构造函数
QTcpServer *server = new QTcpServer(this);
// 绑定并监听端口
bool success = server->listen(QHostAddress::Any, 8080);
// QHostAddress::Any 相当于 INADDR_ANY,监听本地所有地址
// 端口范围:0-65535,建议使用1000以后的端口
// 信号连接
connect(server, &QTcpServer::newConnection, this, &MyClass::onNewConnection); // 新连接信号
// 处理新连接
void MyClass::onNewConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
// 连接客户端socket的信号
connect(clientSocket, &QTcpSocket::readyRead, this, &MyClass::onClientReadyRead);
connect(clientSocket, &QTcpSocket::disconnected, this, &MyClass::onClientDisconnected);
}
// 客户端数据接收
void MyClass::onClientReadyRead()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (clientSocket) {
QByteArray data = clientSocket->readAll();
// 处理接收到的数据
}
}
// 数据发送给客户端
clientSocket->write("Hello Client");
clientSocket->write(data, dataSize);
clientSocket->write(QByteArray("Response"));
3. 综合示例:TCP聊天程序
以下是一个完整的TCP聊天程序示例,包含服务器端和客户端:
3.1 服务器端实现
服务器头文件(tcpserver.h)
cpp
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
class TcpServer : public QMainWindow
{
Q_OBJECT
public:
TcpServer(QWidget *parent = nullptr);
~TcpServer();
private slots:
void startServer();
void stopServer();
void onNewConnection();
void onClientReadyRead();
void onClientDisconnected();
void onSendMessage();
private:
void setupUI();
void broadcastMessage(const QString &message);
QTcpServer *server;
QList<QTcpSocket*> clients;
QTextEdit *logTextEdit;
QTextEdit *messageTextEdit;
QLineEdit *portLineEdit;
QPushButton *startButton;
QPushButton *stopButton;
QPushButton *sendButton;
QLabel *statusLabel;
};
#endif // TCPSERVER_H
服务器实现(tcpserver.cpp)
cpp
#include "tcpserver.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QDateTime>
#include <QMessageBox>
TcpServer::TcpServer(QWidget *parent)
: QMainWindow(parent)
, server(new QTcpServer(this))
{
setupUI();
connect(server, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);
connect(startButton, &QPushButton::clicked, this, &TcpServer::startServer);
connect(stopButton, &QPushButton::clicked, this, &TcpServer::stopServer);
connect(sendButton, &QPushButton::clicked, this, &TcpServer::onSendMessage);
}
TcpServer::~TcpServer()
{
}
void TcpServer::setupUI()
{
logTextEdit = new QTextEdit;
logTextEdit->setReadOnly(true);
messageTextEdit = new QTextEdit;
messageTextEdit->setMaximumHeight(60);
portLineEdit = new QLineEdit("8080");
startButton = new QPushButton("启动服务器");
stopButton = new QPushButton("停止服务器");
sendButton = new QPushButton("发送");
statusLabel = new QLabel("服务器未运行");
stopButton->setEnabled(false);
auto *topLayout = new QHBoxLayout;
topLayout->addWidget(new QLabel("端口:"));
topLayout->addWidget(portLineEdit);
topLayout->addWidget(startButton);
topLayout->addWidget(stopButton);
topLayout->addStretch();
topLayout->addWidget(statusLabel);
auto *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(new QLabel("广播消息:"));
bottomLayout->addWidget(messageTextEdit);
bottomLayout->addWidget(sendButton);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(topLayout);
mainLayout->addWidget(new QLabel("日志:"));
mainLayout->addWidget(logTextEdit);
mainLayout->addLayout(bottomLayout);
auto *centralWidget = new QWidget;
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
setWindowTitle("TCP聊天服务器");
resize(500, 400);
}
void TcpServer::startServer()
{
quint16 port = portLineEdit->text().toUShort();
if (server->listen(QHostAddress::Any, port)) {
logTextEdit->append(QString("[%1] 服务器启动成功,监听端口 %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(port));
startButton->setEnabled(false);
stopButton->setEnabled(true);
statusLabel->setText(QString("服务器运行中 (端口: %1)").arg(port));
} else {
QMessageBox::critical(this, "错误", "无法启动服务器: " + server->errorString());
}
}
void TcpServer::stopServer()
{
// 断开所有客户端连接
for (QTcpSocket *client : clients) {
client->disconnectFromHost();
}
clients.clear();
server->close();
logTextEdit->append(QString("[%1] 服务器已停止").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
startButton->setEnabled(true);
stopButton->setEnabled(false);
statusLabel->setText("服务器未运行");
}
void TcpServer::onNewConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
clients.append(clientSocket);
connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead);
connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);
QString clientInfo = QString("%1:%2")
.arg(clientSocket->peerAddress().toString())
.arg(clientSocket->peerPort());
logTextEdit->append(QString("[%1] 新客户端连接: %2 (当前客户端数: %3)")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(clientInfo)
.arg(clients.size()));
// 发送欢迎消息
clientSocket->write(QString("欢迎连接到服务器! 当前时间: %1\n")
.arg(QDateTime::currentDateTime().toString()).toUtf8());
}
void TcpServer::onClientReadyRead()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket)
return;
QByteArray data = clientSocket->readAll();
QString message = QString::fromUtf8(data);
QString clientInfo = QString("%1:%2")
.arg(clientSocket->peerAddress().toString())
.arg(clientSocket->peerPort());
QString logMessage = QString("[%1] %2: %3")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(clientInfo)
.arg(message);
logTextEdit->append(logMessage);
// 转发消息给其他客户端
QString broadcastMsg = QString("%1: %2")
.arg(clientInfo)
.arg(message);
broadcastMessage(broadcastMsg);
}
void TcpServer::onClientDisconnected()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket)
return;
clients.removeOne(clientSocket);
clientSocket->deleteLater();
logTextEdit->append(QString("[%1] 客户端断开连接 (剩余客户端数: %2)")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(clients.size()));
}
void TcpServer::onSendMessage()
{
QString message = messageTextEdit->toPlainText().trimmed();
if (!message.isEmpty()) {
QString serverMsg = QString("服务器广播: %1").arg(message);
broadcastMessage(serverMsg);
messageTextEdit->clear();
}
}
void TcpServer::broadcastMessage(const QString &message)
{
QByteArray data = message.toUtf8() + "\n";
for (QTcpSocket *client : clients) {
if (client->state() == QAbstractSocket::ConnectedState) {
client->write(data);
}
}
logTextEdit->append(QString("[%1] %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(message));
}
3.2 客户端实现
客户端头文件(tcpclient.h)
cpp
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
class TcpClient : public QMainWindow
{
Q_OBJECT
public:
TcpClient(QWidget *parent = nullptr);
~TcpClient();
private slots:
void connectToServer();
void disconnectFromServer();
void onConnected();
void onDisconnected();
void onReadyRead();
void onError(QAbstractSocket::SocketError socketError);
void onSendMessage();
private:
void setupUI();
void updateConnectionState();
QTcpSocket *socket;
QTextEdit *chatTextEdit;
QTextEdit *messageTextEdit;
QLineEdit *serverIPEdit;
QLineEdit *serverPortEdit;
QPushButton *connectButton;
QPushButton *disconnectButton;
QPushButton *sendButton;
QLabel *statusLabel;
};
#endif // TCPCLIENT_H
客户端实现(tcpclient.cpp)
cpp
#include "tcpclient.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QDateTime>
#include <QMessageBox>
TcpClient::TcpClient(QWidget *parent)
: QMainWindow(parent)
, socket(new QTcpSocket(this))
{
setupUI();
connect(connectButton, &QPushButton::clicked, this, &TcpClient::connectToServer);
connect(disconnectButton, &QPushButton::clicked, this, &TcpClient::disconnectFromServer);
connect(sendButton, &QPushButton::clicked, this, &TcpClient::onSendMessage);
connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);
connect(socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &TcpClient::onError);
updateConnectionState();
}
TcpClient::~TcpClient()
{
}
void TcpClient::setupUI()
{
chatTextEdit = new QTextEdit;
chatTextEdit->setReadOnly(true);
messageTextEdit = new QTextEdit;
messageTextEdit->setMaximumHeight(60);
serverIPEdit = new QLineEdit("127.0.0.1");
serverPortEdit = new QLineEdit("8080");
connectButton = new QPushButton("连接");
disconnectButton = new QPushButton("断开");
sendButton = new QPushButton("发送");
statusLabel = new QLabel("未连接");
auto *topLayout = new QHBoxLayout;
topLayout->addWidget(new QLabel("服务器IP:"));
topLayout->addWidget(serverIPEdit);
topLayout->addWidget(new QLabel("端口:"));
topLayout->addWidget(serverPortEdit);
topLayout->addWidget(connectButton);
topLayout->addWidget(disconnectButton);
topLayout->addStretch();
topLayout->addWidget(statusLabel);
auto *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(new QLabel("消息:"));
bottomLayout->addWidget(messageTextEdit);
bottomLayout->addWidget(sendButton);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(topLayout);
mainLayout->addWidget(new QLabel("聊天记录:"));
mainLayout->addWidget(chatTextEdit);
mainLayout->addLayout(bottomLayout);
auto *centralWidget = new QWidget;
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
setWindowTitle("TCP聊天客户端");
resize(500, 400);
}
void TcpClient::connectToServer()
{
QString host = serverIPEdit->text();
quint16 port = serverPortEdit->text().toUShort();
socket->connectToHost(host, port);
chatTextEdit->append(QString("[%1] 正在连接到 %2:%3...")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(host)
.arg(port));
}
void TcpClient::disconnectFromServer()
{
socket->disconnectFromHost();
}
void TcpClient::onConnected()
{
chatTextEdit->append(QString("[%1] 成功连接到服务器").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
updateConnectionState();
}
void TcpClient::onDisconnected()
{
chatTextEdit->append(QString("[%1] 与服务器断开连接").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
updateConnectionState();
}
void TcpClient::onReadyRead()
{
QByteArray data = socket->readAll();
QString message = QString::fromUtf8(data);
chatTextEdit->append(QString("[%1] %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(message.trimmed()));
}
void TcpClient::onError(QAbstractSocket::SocketError socketError)
{
QString errorString = socket->errorString();
chatTextEdit->append(QString("[%1] 错误: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(errorString));
QMessageBox::critical(this, "连接错误", errorString);
updateConnectionState();
}
void TcpClient::onSendMessage()
{
QString message = messageTextEdit->toPlainText().trimmed();
if (!message.isEmpty() && socket->state() == QAbstractSocket::ConnectedState) {
socket->write(message.toUtf8() + "\n");
messageTextEdit->clear();
}
}
void TcpClient::updateConnectionState()
{
bool connected = (socket->state() == QAbstractSocket::ConnectedState);
connectButton->setEnabled(!connected);
disconnectButton->setEnabled(connected);
sendButton->setEnabled(connected);
if (connected) {
statusLabel->setText(QString("已连接到 %1:%2")
.arg(socket->peerAddress().toString())
.arg(socket->peerPort()));
} else {
statusLabel->setText("未连接");
}
}
3.3 主函数
服务器主函数(main.cpp for server)
cpp
#include "tcpserver.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TcpServer server;
server.show();
return app.exec();
}
#include "main.moc"
客户端主函数(main.cpp for client)
cpp
#include "tcpclient.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TcpClient client;
client.show();
return app.exec();
}
#include "main.moc"
3.4 项目文件
服务器项目文件(tcpserver.pro)
cmake
QT += core gui network widgets
CONFIG += c++11
TARGET = TcpServer
TEMPLATE = app
SOURCES += \
main.cpp \
tcpserver.cpp
HEADERS += \
tcpserver.h
客户端项目文件(tcpclient.pro)
cmake
QT += core gui network widgets
CONFIG += c++11
TARGET = TcpClient
TEMPLATE = app
SOURCES += \
main.cpp \
tcpclient.cpp
HEADERS += \
tcpclient.h
总结
这个综合示例演示了以下Qt TCP网络编程知识点:
-
QTcpServer的使用:
- 服务器的创建和监听
- 处理新连接请求
- 管理多个客户端连接
-
QTcpSocket的使用:
- 客户端连接服务器
- 数据的发送和接收
- 连接状态管理
-
信号与槽机制在网络编程中的应用:
- 连接状态变化信号
- 数据可读信号
- 错误处理信号
-
实际应用技巧:
- 客户端列表管理
- 消息广播机制
- 错误处理和异常情况处理
- 用户界面与网络通信的结合