一.UDP介绍
UDP(User Datagram Protocol,用户数据报协议)
UDP是一个轻量级、不可靠、面向数据报的、无连接的协议,多用于可靠性要求不严格,不是非常重要的传输。
QUdpSocket类继承自QAbstractSocket,用来发送和接收UDP数据报,”Socket”即套接字,套接字即IP地址+端口号。其中IP地址指定了网络中的一台主机,二端口号则指定了该主机上的一个网络程序,使用套接字即可实现网络上的两个应用程序之间的通信。
QUdpSocket支持IPv4广播,要广播数据报,则只需发送到一个特殊的地址QHostAddress::Broadcast(即255.255.255.255),数据报一般建议发送字节数小于512字节。端口号选择1024-65535(1024以下的常用作保留端口号,如FTP常用端口号21,Telnet常用端口号23,DNS域名服务器常用端口53等)。
发送端
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_sender=new QUdpSocket(this);}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{QByteArray dataGram=ui->lineEdit->text().toUtf8();m_sender->writeDatagram(dataGram.data(),dataGram.size(),QHostAddress::Broadcast,6666);}
接收端
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_receiver=new QUdpSocket(this);m_receiver->bind(6666,QUdpSocket::ShareAddress);connect(m_receiver,&QUdpSocket::readyRead,this,&MainWindow::processData);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::processData()
{QString strData;//有未处理的数据包while(m_receiver->hasPendingDatagrams()){QByteArray dataGram;dataGram.resize(m_receiver->pendingDatagramSize());m_receiver->readDatagram(dataGram.data(),dataGram.size());ui->label_2->setText(dataGram);}
}
二.TCP介绍
TCP(Transmission Control Protocol,传输控制协议)
TCP是一个用于数据传输的地城网络协议,多个网络协议包括(HTTP和FTP都是基于TCP协议),TCP是面向数据流和连接的可靠的传输协议。
QTcpSocket继承自QAbstractSocket,与QUdpSocket传输的数据报不同的是,QTcpSocket传输的是连续的数据流,尤其适合连续的数据传输,TCP一般分为客户端和服务端,即C/S (Client/Server模型)。
QTcpSocket代表了两个独立的数据流,一个用来读取数据,一个用来写入数据,分别采用QTcpSocket::read()及QTcpSocket::write()操作,读取数据前先调用QTcpSocket::bytesAvailable来确定已有足够的数据可用。
QTcpServer处理客户端的连接,可通过QTcpServer::listen()监听客户端发来的连接请求,每当有客户端连接时会发射newConnection()信号,QTcpSocket可用于读取客户端发来的数据报,亦可发送数据报。
客户端
#include "mainwindow.h"
#include "ui_mainwindow.h"const int gTcpPort =8888;MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_tcpSocket=new QTcpSocket(this);//socket有数据来了,做处理connect(m_tcpSocket,&QTcpSocket::readyRead,this,&MainWindow::onReadMessage);connect(m_tcpSocket,SIGNAL(SocketError(QAbstractSocket::SocketError)),this,SLOT(onDisplayError(QAbstractSocket::SocketError)));ui->lineEdit_host->setText("127.0.0.1");ui->lineEdit_port->setText(QString::number(gTcpPort));}void MainWindow::onReadMessage()
{//读取信息QByteArray bt;bt.resize( m_tcpSocket->bytesAvailable());m_tcpSocket->read(bt.data(),bt.size());ui->label_receive->setText(bt);
}void MainWindow::onDisplayError(QAbstractSocket::SocketError error)
{//显示错误信息qDebug()<<"socketError:"<<error<<endl<<m_tcpSocket->errorString();
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_connect_clicked()
{//终止socketm_tcpSocket->abort();//connectToHost(IP,Port)m_tcpSocket->connectToHost(ui->lineEdit_host->text(),ui->lineEdit_port->text().toInt());}void MainWindow::on_pushButton_send_clicked()
{m_tcpSocket->write(ui->lineEdit_send->text().toUtf8());m_tcpSocket->flush();
}
服务端
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QDebug>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_tcpserver=new QTcpServer(this);//开始监听客户端发来的请求if(!m_tcpserver->listen(QHostAddress::Any,8888)){qDebug()<<m_tcpserver->errorString();close();}connect(m_tcpserver,&QTcpServer::newConnection,this,&MainWindow::onNewConnect);connect(m_tcpserver,&QTcpServer::newConnection,this,&MainWindow::onsendMessage);}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::onNewConnect()
{//客户端新的连接处理//当前连接的对象m_tcpsocket=m_tcpserver->nextPendingConnection();//如果断开连接自动销毁资源connect(m_tcpsocket,&QTcpSocket::disconnected,this,&QTcpSocket::deleteLater);//socket有数据时,会发射readRead信号connect(m_tcpsocket,&QTcpSocket::readyRead,this,&MainWindow::onReadMessage);
}void MainWindow::onsendMessage()
{//给客户端回馈一个信息QString str="你好,客户端!";m_tcpsocket->write (str.toUtf8());ui->label->setText("发送数据成功");}void MainWindow::onReadMessage()
{//有数据可以读时//字节数组QByteArray bt = m_tcpsocket->readAll();ui->label_Read->setText(bt);}
三.多线程编程
在Qt中进行多线程编程时,通常需要使用锁来保证共享资源的同步访问,防止多个线程同时访问导致数据的不一致性或者竞争条件的发生。Qt提供了QMutex、QMutexLocker、QReadWriteLock等类来实现锁的功能。
QMutex是最基本的锁类,可以使用lock()方法进行锁定,使用unlock()方法进行解锁。QMutexLocker是一个自动解锁类,可以避免因忘记解锁而导致的死锁问题。QReadWriteLock提供了读写锁的功能,可以允许多个线程同时读取数据,但只允许一个线程写数据。
以下是一个使用QMutex进行多线程编程的示例:
#include <QThread>
#include <QMutex>
#include <QDebug>class Worker : public QThread
{
public:void run() override{mutex.lock();qDebug() << "Thread ID:" << currentThreadId() << " is working...";mutex.unlock();}private:QMutex mutex;
};int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);Worker worker1;Worker worker2;worker1.start();worker2.start();worker1.wait();worker2.wait();return app.exec();
}