Qt 网络编程

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();
}

运行效果如下:
在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值