QT 网络编程
TCP 编程
模块引入
QT += network
头文件
#include <QTcpServer> // TCP服务器端使用
#include <QTcpSocket> // TCP服务器和客户端都使用
编程流程
服务端
1)实例化 QTcpServer 对象 -----------------------------> socket
2)进入监听状态 ----> listen(QTcpServer类) // 不需要再绑定了----------->bind + listen
3)监测客户端连接 ---- newConnection 信号(QTcpServer类)
----------------> 有新连接过来,server 就能收到 newConnection 信号
4)QTcpSocket *client <---- 获得连接 ---- nextPendingConnection(QTcpServer类) ---->accept
5)连接对端接收信号 ------ readyRead(QTcpSocket类)
---------------------->如果对端有数据发送,server 就能收到 readyRead 信号
6)读取客户端消息 ------ readAll(QTcpSocket类) --------------------------> recv:读取数据
7)发送数据 ------ write(QTcpSocket类) ----> send:发数据
8)关闭连接 ------ disconnectFromHost() -------------------> close
客户端
1)实例化 QTcpSocket 对象;
2)连接服务器 ------ connectToHost ------> 接下来使用 waitForConnected 来判断是否连接成功
3)连接对端接收信号 ------ readyRead 信号
4)发送数据 ------ write()
5)关闭连接 ------ disconnectFromHost()
💡 客户端实现
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>
#include <QTcpSocket>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_connectBtn_clicked();void recvSlot();void on_sendBtn_clicked();
// void whetherConnectedSlot(); // 判断是否连接成功,方法二private:Ui::Widget *ui;QTcpSocket *client;bool flag;
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客户端");client = new QTcpSocket(this);QObject::connect(client, SIGNAL(readyRead()), this, SLOT(recvSlot()));// 判断是否连接成功,方法二
// QObject::connect(client, SIGNAL(connected()), this, SLOT(whetherConnectedSlot()));
}Widget::~Widget()
{delete ui;
}// void Widget::whetherConnectedSlot() // 判断是否连接成功,方法二
// {
// flag = true;
// }void Widget::on_connectBtn_clicked()
{client->connectToHost(ui->ipEdit->text(), ui->portEdit->text().toShort());// 判断是否连接成功,方法一if (!client->waitForConnected(1000)) {qDebug() << "Failed to connect. ";return ;}qDebug() << "Connected successfully! ";ui->connectBtn->setText("断开");
}void Widget::recvSlot()
{QByteArray buffer = client->readAll();ui->recvEdit->setText(QString::fromLocal8Bit(buffer));
}void Widget::on_sendBtn_clicked()
{QString buffer = ui->sendEdit->toPlainText();client->write(buffer.toLocal8Bit());
}
💡 服务器实现
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>
#include <QTcpServer>
#include <QTcpSocket>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_connectBtn_clicked();void connectSlot();void recvSlot();void on_sendBtn_clicked();private:Ui::Widget *ui;QTcpServer *server;QTcpSocket *client;
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);server = new QTcpServer(this);this->setWindowTitle("服务器");// 这个信号触发,代表有客户端连接QObject::connect(server, SIGNAL(newConnection()), this, SLOT(connectSlot()));
}Widget::~Widget()
{delete ui;
}void Widget::on_connectBtn_clicked()
{// 监听是否有客户端连入,不阻塞等待if (!server->listen(QHostAddress::Any, ui->portEdit->text().toUShort())){qDebug() << "Failed to listen. ";return ;}ui->connectBtn->setText("关闭");
}void Widget::connectSlot()
{// 接受新的客户端连接client = server->nextPendingConnection();if (client == 0){qDebug() << "There are no pending connections. ";return ;}// 一定要等到接受连接后,再去做客户端的信号连接QObject::connect(client, SIGNAL(readyRead()), this, SLOT(recvSlot()));
}void Widget::recvSlot()
{QByteArray buffer = client->readAll();// 编码转换:对方必须按照 GBK 格式发送ui->recvEdit->setText(QString::fromLocal8Bit(buffer));
}void Widget::on_sendBtn_clicked()
{QString buffer = ui->sendEdit->toPlainText(); // 从textEdit中获取的内容一定是utf8编码client->write(buffer.toLocal8Bit());
}
💡 服务器与客户端交互
UDP 编程
模块引入
QT += network
头文件
#include
编程流程
1)实例化 QUdpSocket 对象 ------------------------------------------> socket
2)绑定地址、端口 ------ bind(QHostAddress::LocalHost,8888) -----> bind
3)收发报文 ------ readDatagram、writeDatagram ------------------> recvfrom/sendto
// 类和接口
bool QUdpSocket::hasPendingDatagrams() const;
qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0);
qint64 QUdpSocket::writeDatagram(const char * data, qint64 size, const QHostAddress & address, quint16 port);
实现一个聊天功能,使用 UDP 通信。首先通过一个登录界面,输入服务器的 IP 和端口,点击登录后,将 IP 和端口传到聊天界面,然后通过 UDP 进行聊天(服务器)。
💡 服务器实现
chatpage.h
#ifndef CHATPAGE_H
#define CHATPAGE_H#include <QtWidgets>
#include <QUdpSocket>
#include "globalvalue.h"namespace Ui {
class ChatPage;
}class ChatPage : public QWidget
{Q_OBJECTpublic:explicit ChatPage(QWidget *parent = 0);~ChatPage();private:Ui::ChatPage *ui;QUdpSocket *socket;QHostAddress sender; // 定义对端的地址,以便后续发送使用quint16 senderPort;private slots:void readPendingDatagrams();void on_sendBtn_clicked();
};#endif // CHATPAGE_H
chatpage.cpp
#include "chatpage.h"
#include "ui_chatpage.h"ChatPage::ChatPage(QWidget *parent) :QWidget(parent),ui(new Ui::ChatPage)
{ui->setupUi(this);this->setWindowTitle("聊天");// 初始化一个 QUdpSocket 对象socket = new QUdpSocket(this);// 绑定服务器的地址和IP,客户端省略此句socket->bind(QHostAddress(GlobalValue::ipaddr), GlobalValue::port);connect(socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}ChatPage::~ChatPage()
{delete ui;
}void ChatPage::readPendingDatagrams()
{// 如果udp缓冲区有报文数据的话while (socket->hasPendingDatagrams()){QByteArray datagram; // 初始化一个字节流缓冲区datagram.resize(socket->pendingDatagramSize()); // 重设缓冲区的大小socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 对方发送的字节流必须是GBK编码ui->recvEdit->setText(QString::fromLocal8Bit(datagram));}
}
void ChatPage::on_sendBtn_clicked()
{// 如果已经接收过消息,那么sender和senderPort已经有对方的地址了socket->writeDatagram(ui->sendEdit->toPlainText().toLocal8Bit(), sender, senderPort);
}
globalvalue.h
#ifndef GLOBALVALUE_H
#define GLOBALVALUE_H#include <QString>class GlobalValue // 此类仅用于存放静态变量
{
public:GlobalValue();static QString ipaddr;static quint16 port;
};#endif // GLOBALVALUE_H
globalvalue.cpp
#include "globalvalue.h"QString GlobalValue::ipaddr;
quint16 GlobalValue::port;GlobalValue::GlobalValue()
{
}
userver.h
#ifndef USERVER_H
#define USERVER_H#include <QtWidgets>
#include "chatpage.h"
#include "globalvalue.h"namespace Ui {
class UServer;
}class UServer : public QWidget
{Q_OBJECTpublic:explicit UServer(QWidget *parent = 0);~UServer();private slots:void on_pushButton_clicked();private:Ui::UServer *ui;ChatPage *chatting;
};#endif // USERVER_H
userver.cpp
#include "userver.h"
#include "ui_userver.h"UServer::UServer(QWidget *parent) :QWidget(parent),ui(new Ui::UServer)
{ui->setupUi(this);this->setWindowTitle("登录");
}UServer::~UServer()
{delete ui;
}void UServer::on_pushButton_clicked()
{GlobalValue::ipaddr = ui->ipEdit->text();GlobalValue::port = ui->portEdit->text().toUShort();chatting = new ChatPage;chatting->show(); // 初始化一个新的界面,然后进行跳转this->close();
}
运行效果如下: