一.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等)。
发送端
cpp
#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);
}
接收端
cpp
#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可用于读取客户端发来的数据报,亦可发送数据报。
客户端
cpp
#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()
{
//终止socket
m_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();
}
服务端
cpp
#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进行多线程编程的示例:
cpp
#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();
}