本文章的目的:
在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。