【Qt】Qt 网络 | TCP Socket

要使用 Qt 网络编程,需要在项目中的 .pro 文件中添加 network 模块
在这里插入图片描述

核心API

核心类有两个:QTcpServerQTcpSocket
QTcpServer用于监听端口,和获取客户端连接

名称类型说明对标原生API
listen(const QHostAddress &address, quint16 port)方法绑定指定的 IP地址和端口号,并开始监听bind 和 listen
nextPendingConnection()方法从系统中获取一个已经建立好的tcp连接
返回一个 QTcpSocket,表示这个客户端的连接
通过这个 socket 对象完成和客户端的通信
accept
newConnection信号有新的客户端建立连接好之后触发无,类似 IO多路复用 中的通知机制

QTcpSocket用于客户端和服务端之间的数据交互

名称类型说明对标原生API
readAll()方法读取当前接收缓冲区中的所有数据
返回 QByteArray 对象
read
write(const QByteArray &byteArray)方法把数据写入 socket 中write
deleteLater方法暂时把 socket 对象标记为无效。Qt 会在下次事件循环中析构释放该对象类似“半自动的垃圾回收机制”
readyRead信号有数据到达并准备就绪时触发类似 IO多路复用的通知机制
disconnected信号连接断开时触发类似 IO多路复用的通知机制

QByteArray 表示一个字节数组,Qt 提供了 QByteArray 和 QString 的转换接口
使用 QString 的赋值重载,即可把 QByteArray 转换成 QString
使用 QString 的 toUtf8()函数 把 QString 转换成 QByteArray

代码示例

通过模拟 客户服务器的TCP通信 熟悉上述方法使用
客户端通过输入栏获取数据,点击按钮发送给服务端
服务端接受客户端数据,并返回相同数据,验证通信无误

服务器

  1. 编写 UI文件,创建界面,包含一个 QListWidget,用于显示收到的数据

在这里插入图片描述
2. 编写 widget.h,添加QTcpServer指针成员,槽函数和业务处理函数

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
//槽函数
public slots:
    void handleConnect();//处理新连接
private:
    QString handleRequest(const QString &request);//处理请求
private:
    Ui::Widget *ui;
    QTcpServer *listensock;
};
  1. 编写widget.cpp,完成QTcpServer实例化和相关初始化。

具体逻辑如下:

  • 设置窗口标题
  • 实例化QTcpServer,创建套接字
  • 连接信号槽,处理客户端建立的新连接
  • 绑定端口号,监听端口
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //1. 设置窗口标题
    this->setWindowTitle("服务端");
    //2. 创建套接字
    listensock = new QTcpServer(this);
    //3. 连接信号槽
    connect(listensock, &QTcpServer::newConnection, this, &Widget::handleConnect);
    //4. 绑定端口号,监听套接字
    bool ret = listensock->listen(QHostAddress::Any, 9090);
    if(!ret)
    {
        QMessageBox::critical(this, "绑定端口号失败", listensock->errorString());
        exit(1);
    }
}
  1. 编写widget.cpp的新连接处理槽函数 handleConnect()

逻辑如下:

  • 获取新连接
  • 连接信号槽,处理客户端数据
  • 连接信号槽,处理客户端断开连接
//处理新连接
void Widget::handleConnect()
{
    //1. 获取连接
    QTcpSocket *clientSocket = listensock->nextPendingConnection();
    //2. 记录客户端信息
    //peerAddress是对端地址的意思
    QString log = "[" + clientSocket->peerAddress().toString()
                + ":" + QString::number(clientSocket->peerPort()) + "] 客户端上线!";
    ui->listWidget->addItem(log);
    //3. 连接读数据的信号槽
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
        //a) 获取TCP字节流内容
        QString request = clientSocket->readAll();
        //b) 业务处理
        const QString &responce = handleRequest(request);
        //c) 返回响应给客户端
        clientSocket->write(responce.toUtf8());
        //d) 记录数据
        QString log = "[" + clientSocket->peerAddress().toString()
                    + ":" + QString::number(clientSocket->peerPort()) + "] request:"
                    + request + "  responce:" + responce;
        ui->listWidget->addItem(log);
    });
    //4. 连接断开后释放资源
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        //a) 记录客户端下线信息
        QString log = "[" + clientSocket->peerAddress().toString()
                    + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";
        ui->listWidget->addItem(log);
        //b) 释放资源
        clientSocket->deleteLater();
    });
}

客户端断开连接,不能直接delete 释放 客户端QTcpSocket,因为可能服务端还有数据传输,所以需要在数据完全传输结束后再释放连接,而Qt 提供的 deleteLater()就是在下一次事件循环时,检测该客户端连接没有数据传输,才释放连接

  1. 编写 widget.cpp,实现业务处理函数,因为是简单的回显服务端,业务处理较为简单
//业务处理
QString Widget::handleRequest(const QString &request)
{
    return request;
}

客户端

  1. 编写 UI文件,创建界面,包含一个 QLineEditQPushButtonQListWidget,并通过布局管理器规范控件

在这里插入图片描述

  1. 编写 widget.h,声明QTcpSocket指针成员和按钮槽函数
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket *socket;
};
  1. 编写 widget.cpp,实例化QTcpSocket,完成相应初始化

具体逻辑如下:

  • 设置窗口标题
  • 实例化QTcpSocket,创建套接字
  • 连接信号槽,处理服务端返回的响应
  • 连接服务端
  • 等待连接,查看连接是否成功
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置窗口标题
    this->setWindowTitle("客户端");
    //1. 创建套接字
    socket = new QTcpSocket(this);
    //2. 连接客户端收到服务端消息的槽函数
    connect(socket, &QTcpSocket::readyRead, this, [=](){
        QString responce = socket->readAll();
        QString log = "服务端响应:" + responce;
        ui->listWidget->addItem(log);
    });
    //3. 连接服务端,该连接不是阻塞的
    socket->connectToHost("127.0.0.1", 9090);
    //4. 查看连接是否成功
    bool ret = socket->waitForConnected();
    if(!ret)
    {
        QMessageBox::critical(this, "连接服务器失败", socket->errorString());
        exit(1);
    }
}
  1. 编写 widget.cpp,实现按钮的槽函数

具体逻辑如下:

  • 获取输入栏数据
  • 清空输入栏
  • 记录数据
  • 发送数据到服务端
void Widget::on_pushButton_clicked()
{
    //1. 获取输入框内容
    QString text = ui->lineEdit->text();
    //2. 清空输入栏
    ui->lineEdit->clear();
    //3. 记录发送
    QString log = "客户端发送:" + text;
    ui->listWidget->addItem(log);
    //4. 发送数据给服务端
    socket->write(text.toUtf8());
}

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

结束语
感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值