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_OBJECT
public:
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_OBJECT
public:
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_OBJECT
public:
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_OBJECT
public:
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();
}
运行效果如下: