在QT里实现网络传输,多线程和自定义信号(用到TCP协议)

本文章的目的:

在QT里实现网络传输。

学习内容:

在QT里实现网络传输与在c++后端有一样的逻辑。回顾一下在c++里实现网络通信(以服务端为例)

1.实现套接字的创建(socket的创建)

2.给socket一个端口号(创建结构体来接收socket)

3.给这个socket开启监听属性(使用listen函数和band函数)

4.等待客户端链接

5.开始通信

6.关闭连接

在qt里已经封装好一个QTcpSocket类用于网络通信。以下我们以简单的客户端开始来理解网络实现的过程。

QT 里TCP的实现(客户端)

首先头文件我们要包括基础的头文件

#include <QMainWindow>
#include<QTcpSocket>//用与客户端与服务端的链接
#include<Qstring>
#include<QHostAddress>//解析IPV4或IPV6的核心工具
#include <QMessageBox>
//#include<chat.h>用于自定义信号
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_cancelButton_clicked();//按钮1

    void on_connectButton_2_clicked();//按钮2

private:
    Ui::MainWindow *ui;
    QTcpSocket* socket;//在主类创建以个指针来接收传来的套接字(socket)
};
#endif // MAINWINDOW_H

在MainWindow类私有成员里创建一个指针来接受socket套接字。

在MIainWindow.cpp里

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    socket=new QTcpSocket;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_cancelButton_clicked()
{
    this->close();
}

使用构造函数来给socket分配内存,

void MainWindow::on_connectButton_2_clicked()
{
    //获取IP地址和端口号
    QString IP=ui->IPlineEdit->text();
    QString port=ui->lineEdit_2->text();
    //链接服务器
    socket->connectToHost(QHostAddress(IP),port.toShort());
    //判断链接成功
    connect(socket,&QTcpSocket::connected,[this]()
            {
        QMessageBox::information(this,"连接提示","连接服务器成功");
        this->hide();//主界面隐藏
        chat* c=new chat(socket);
        c->show();
    });
    connect(socket,&QTcpSocket::disconnected,[this]()
            {
                QMessageBox::warning(this,"连接提示","连接服务器失败,网络断开");
            });
}

当按下按纽2时来处理事件

connectToHost()是实现服务器链接的重要函数有两种方式

//第一种
void QTcpSocket::connectToHost(
    const QString &hostName,     // 主机名或IP地址(字符串形式)
    quint16 port,                // 端口号(0~65535)
    QIODevice::OpenMode mode = ReadWrite,  // 打开模式(通常默认)
    QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol  // 协议类型(IPv4/IPv6)
);
//第二种
void QTcpSocket::connectToHost(
    const QHostAddress &address, // 主机地址对象
    quint16 port,                // 端口号
    QIODevice::OpenMode mode = ReadWrite
);

我们这里使用简单的第二种。第三个参数为默认的可以不写,第二个参数是使用无符号16为整数。

使用connec的lambda表达式来判断是否连接成功,成功后显示socket。(有关chat.h里的函数后边在提)

这样一个简单的客户端就创建而成。

chat文件是用来实现客户端与服务端传递信息的。

#ifndef CHAT_H
#define CHAT_H

#include <QWidget>
#include<QTcpSocket>
namespace Ui {
class chat;
}

class chat : public QWidget
{
    Q_OBJECT

public:
    explicit chat(QTcpSocket *s,QWidget *parent = nullptr);
    ~chat();

private slots:
    void on_clearButton_clicked();

    void on_setButton_clicked();

private:
    Ui::chat *ui;
    QTcpSocket* socket;
};

#endif // CHAT_H

我们构建一个QTcpSocket类型的socket指针用来接受传来的socket套接字。

#include "chat.h"
#include "qtcpsocket.h"
#include "ui_chat.h"
chat::chat(QTcpSocket *s,QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::chat)
{
    ui->setupUi(this);
    socket=s;
}

chat::~chat()
{
    delete ui;
}

void chat::on_clearButton_clicked()
{
    ui->lineEdit->clear();
}


void chat::on_setButton_clicked()//到按钮按下后发送信息
{
    QByteArray ba;
    ba.append(ui->lineEdit->text().toUtf8());//转换成16进制
    socket->write(ba);
}

QT 里TCP的实现(服务端)

有了客户端我i们就可以更容易地实现服务端的构建

实现服务器创建我们要新添一个类#include<QTcpServer>。

#include <QMainWindow>
#include<QTcpServer>//新添的
#include<QTcpSocket>

在主文件里 我们可以创建QTcpServer类型的指针来接收服务器对象

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QTcpServer* server;//创建服务器指针
private slots:
    void newClientHander();
    void threadSlot(QByteArray b);
};
#endif // MAINWINDOW_H

 这里我们new出一个服务器类型的对象用server指针接收

然后将服务器对象设置为监听模式

在listen()函数里第一是IP协议,第二是端口号。

将服务器与 newClientHander()函数链接。


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    server=new QTcpServer;
    //监听
    server->listen(QHostAddress::AnyIPv4,8000);
    //等待客户链接
    connect(server,&QTcpServer::newConnection,this,&MainWindow::newClientHander);
}

下面我们解释 void newClientHander()函数的作用。

void MainWindow::newClientHander()
{
    //建立TCP链接
    QTcpSocket* socket=server->nextPendingConnection();
    //socket->peerAddress();
    //socket->peerPort();
    //显示
    ui->IPlineEdit->setText(socket->peerAddress().toString());
    ui->lineEdit_2->setText(QString::number(socket->peerPort()));
    //服务端收到客户信息
   //connect(socket,&QTcpSocket::readyRead,this,&MainWindow::clientinfoslot);
    //启动线程
    myThread *t=new myThread(socket);
    t->start();//开始线程
    connect(t,&myThread::sendTowiget,this,&MainWindow::threadSlot); 
}

第一列中使用nextPendingConnection()函数用来接收客户端发来的连接请求,在上边构建客户端时我们使用了connectToHost()函数,当这个函数调用完的时候服务端会将这个请求放入队列等待链接,当我们调用nextPendingConnection()时会接收客户端发来的请求并返回一个QTcpSocket类型。

显示完IP地址和端口号。

下面启动线程进行客户端联通,在Qt里使用多线程我们先要会创建一个c++类这里命名为myThread。

我们创建myThread类是想让服务端与多个客户端进行通信,进行通信就需要套接字(socket)的传输所以在构造myThread类时我们需要将套接字传到类里。

所以我们这样写

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include<QTcpSocket>
class myThread : public QThread
{
    Q_OBJECT
public:
    explicit myThread( QTcpSocket* s);
    void run();
signals:
    void sendTowiget(QByteArray b);
public slots:
    void clientinfoSlot();
private:
    QTcpSocket* socket;
};

#endif // MYTHREAD_H

私有成员变量里创立一个QTcpSocket类型的指针socket用来接收newClientHander()传来的socket套接字。

构造函数里我们这样写

#include "mythread.h"
#include "qtcpsocket.h"
myThread::myThread(QTcpSocket *s)
{
    socket = s;
}
void myThread::run()
{
    //接受客户端的信息
    connect(socket,&QTcpSocket::readyRead,this,&myThread::clientinfoSlot);    
}
void myThread::clientinfoSlot()
{
   QByteArray ba =socket->readAll();
    emit sendTowiget(ba);//发送信号
}

 在多线程类里调用start()函数里程序会自动调用函数run(),在run函数中重写线程任务。(run()是静态成员函数)。

最后是信号传递部分,

//connect(t,&myThread::sendTowiget,this,&MainWindow::threadSlot); 
void MainWindow::threadSlot(QByteArray b)
{
    ui->mainlineEdit->setText(QString(b));
}

用connect 函数将myThread类中的sendTowiget和MainWindow类中的threadSlot连接起来。

(这里我们不能将ui直接传到直接写的多线程文件中这里会造成管理混乱,我们可以将myThread文件里的信号传到主文件中。)

在myThread类里我们使用clientinfoSlot()来处理接受的socket

void myThread::clientinfoSlot()
{
   QByteArray ba =socket->readAll();
    emit sendTowiget(ba);//发送信号
}

最后使用threadSlot()函数将socket显示出来传递的参数是QByteArray 类型最后我们将QByteArray类型的b转换成QString类型。调用ui指针来显示socket。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值