QT TCP网络编程
一、前言
在网络编程的世界中,TCP(传输控制协议)是最重要、最常用的协议之一。无论是即时通讯、文件传输还是在线游戏,TCP都扮演着关键角色。Qt作为跨平台的C++框架,提供了强大且易用的网络模块,让TCP编程变得简单高效
二、概念
TCP(Transmission Control Protocol)是一种面向连接、可靠、基于字节流的传输层通信协议。想象一下打电话的过程:
面向连接:拨号 → 对方接听 → 开始通话(建立连接)
可靠传输:确保你说的每一句话对方都能听到(数据不丢失)
顺序保证:按说话顺序传达(数据顺序正确)
三、IP地址
3.2、IP地址的查找
QStringList MainWindow::getLocalIPAddresses() { QStringList ipList; ui->comboBox->clear(); // 清空原有内容 // 1. 添加特殊地址选项 ui->comboBox->addItem("127.0.0.1 (本地回环)"); ui->comboBox->addItem("0.0.0.0 (所有接口)"); // 2. 获取所有网络接口 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); for (const QNetworkInterface &interface : interfaces) { // 跳过非活动接口 if (!(interface.flags() & QNetworkInterface::IsUp)) { continue; } // 跳过回环接口(127.0.0.1) if (interface.flags() & QNetworkInterface::IsLoopBack) { continue; } // 获取接口的所有IP地址 QList<QNetworkAddressEntry> entries = interface.addressEntries(); for (const QNetworkAddressEntry &entry : entries) { QHostAddress ip = entry.ip(); // 只添加IPv4地址 if (ip.protocol() == QAbstractSocket::IPv4Protocol) { QString ipString = ip.toString(); // 格式化显示:IP + 接口名 QString displayText = QString("%1 (%2)") .arg(ipString) .arg(interface.humanReadableName()); ui->comboBox->addItem(displayText, ipString); ipList.append(ipString); } } } // 如果没有找到IP地址,添加默认选项 if (ipList.isEmpty()) { ui->comboBox->addItem("127.0.0.1 (默认)"); ipList.append("127.0.0.1"); } return ipList; // 返回所有找到的IP地址 }
三、QT中的TCP核心类
QTcpServer 监听连接,接受客户端请求 QTcpSocket 建立连接,发送接收数据
3.1、服务器端构建
// server.h #include <QTcpServer> #include <QTcpSocket> #include <QObject> class TcpServer : public QObject { Q_OBJECT public: explicit TcpServer(QObject *parent = nullptr); bool startServer(quint16 port = 8080);//初始值端口赋值为8080 void stopServer(); private slots: void onNewConnection(); void onClientReadyRead(); void onClientDisconnected(); private: QTcpServer *server; QList<QTcpSocket*> clients; // 存储所有客户端连接 }; // server.cpp #include "server.h" #include <QDebug> TcpServer::TcpServer(QObject *parent) : QObject(parent) { server = new QTcpServer(this); // 连接新连接信号 connect(server, &QTcpServer::newConnection, this, &TcpServer::onNewConnection); } bool TcpServer::startServer(quint16 port) { // 监听所有网络接口的指定端口 if (server->listen(QHostAddress::Any, port)) { qDebug() << "✅ 服务器启动成功"; qDebug() << "📡 监听地址: 0.0.0.0:" << port; qDebug() << "🔢 客户端可连接到此服务器的地址:"; // 显示本机所有IP地址 QList<QHostAddress> ipList = QNetworkInterface::allAddresses(); for (const QHostAddress &address : ipList) { //筛选IPv4类型的IP且不为回环IP if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress::LocalHost) { qDebug() << " -" << address.toString() << ":" << port; } } return true; } else { qDebug() << "❌ 服务器启动失败:" << server->errorString(); return false; } } void TcpServer::onNewConnection() { // 处理所有等待的连接 while (server->hasPendingConnections()) { QTcpSocket *client = server->nextPendingConnection(); // 获取客户端信息 QString clientInfo = QString("%1:%2") .arg(client->peerAddress().toString()) .arg(client->peerPort()); qDebug() << "👤 新客户端连接:" << clientInfo; // 添加到客户端列表 clients.append(client); // 连接客户端的信号 connect(client, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead); //读取信号 connect(client, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected); //断开连接信号 // 发送欢迎消息 QString welcome = QString("欢迎连接到服务器!你的ID: %1\n") .arg(client->socketDescriptor()); client->write(welcome.toUtf8()); //转为utf-8编码,兼容性强 } }3.2、客户端构建
// client.h #include <QTcpSocket> #include <QObject> class TcpClient : public QObject { Q_OBJECT public: explicit TcpClient(QObject *parent = nullptr); void connectToServer(const QString &host, quint16 port); void disconnectFromServer(); void sendMessage(const QString &message); bool isConnected() const; signals: void connected(); void disconnected(); void messageReceived(const QString &message); void errorOccurred(const QString &error); private slots: void onConnected(); void onDisconnected(); void onReadyRead(); void onError(QAbstractSocket::SocketError error); private: QTcpSocket *socket; }; // client.cpp #include "client.h" #include <QDebug> TcpClient::TcpClient(QObject *parent) : QObject(parent) { socket = new QTcpSocket(this); // 连接信号 connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected); connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected); connect(socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead); connect(socket, &QTcpSocket::errorOccurred, this, &TcpClient::onError); } void TcpClient::connectToServer(const QString &host, quint16 port) { qDebug() << "🔄 正在连接到服务器" << host << ":" << port; socket->connectToHost(host, port); } void TcpClient::onConnected() { qDebug() << "✅ 连接成功!"; qDebug() << "📍 本地地址:" << socket->localAddress().toString() << ":" << socket->localPort(); qDebug() << "🌍 远程地址:" << socket->peerAddress().toString() << ":" << socket->peerPort(); emit connected(); } void TcpClient::sendMessage(const QString &message) { if (socket->state() == QTcpSocket::ConnectedState) { QByteArray data = (message + "\n").toUtf8(); socket->write(data); } else { qDebug() << "⚠️ 未连接,无法发送消息"; } }
一句话总结:
- 服务器在指定IP和端口监听,客户端连接正确的IP和端口,网络通畅且无防火墙阻挡,两者即可建立TCP连接。
四、QTcpServer信号介绍
4.1、newConnection() - 新连接信号
触发时机:当有新的客户端连接请求到达时
信号特点:
每次有连接到达时触发
可能同时有多个连接到达,需要用 while 循环处理
必须配合
hasPendingConnections()和nextPendingConnection()使用// 基本用法 connect(server, &QTcpServer::newConnection, this, &MyServer::handleNewConnection); void MyServer::handleNewConnection() { // 用while循环处理所有连接 while (server->hasPendingConnections()) { //判断是否还有等待的连接 QTcpSocket *client = server->nextPendingConnection(); if (client) { processClient(client); } } }
4.2、acceptError() - 接受错误信号
触发时机:接受新连接时发生错误
五、QTcpSocket信号介绍
5.1、connected() - 连接建立信号
触发时机:客户端成功连接到服务器,或服务器套接字完成连接
重要特性:
连接完全建立后才触发
适用于初始握手完成后的操作
对于服务器端的客户端套接字,这个信号也会触发
// 客户端使用示例 connect(socket, &QTcpSocket::connected, this, &MyClient::onConnected); //客户端连接成功后触发 void MyClient::onConnected() { qDebug() << "✅ 连接成功!"; qDebug() << "本地端点:" << socket->localAddress().toString() << ":" << socket->localPort(); qDebug() << "远程端点:" << socket->peerAddress().toString() << ":" << socket->peerPort(); // 连接成功后可以开始发送数据 socket->write("Hello Server!"); }
5.2、disconnected() - 连接断开信号
触发时机:连接被正常关闭或异常断开
5.3、readyRead() - 数据可读信号
触发时机:有新的数据到达,可以读取
void MainWindow::socketreaddata() { while(!tcpsocket->atEnd()){ ui->textEdit->append("[in]:" + tcpsocket->readLine() + '\n'); } }

浙公网安备 33010602011771号