[QT编程系列-30]: 多进程机制 - QT Socket通信:QTcpSocket、QUdpSocket

目录

第1章 概述

1.1 概述

1.2 QT socket通信的本质

1.3 QUdpSocket相关的信号

1.4 QTcpSocket相关的信号

第2章 UDP通信示例

服务端代码:

客户端代码:

第3章 TCP通信代码示例

服务器端代码:

客户端代码:


第1章 概述

1.1 概述

在Qt中,通过套接字(socket)实现网络通信主要使用的是QTcpSocketQUdpSocket类。QTcpSocket用于基于TCP协议的通信,而QUdpSocket用于基于UDP协议的通信。

在Qt中,使用TCP/IP协议进行网络通信可以使用QTcpSocketQTcpServer类。QTcpServer用于创建服务器,监听连接请求并接受客户端连接,而QTcpSocket用于创建客户端,并与服务器建立连接。

1.2 QT socket通信的本质

QUdpSocket和QTcpSocket这两个类,是QT库提供的,应用程序,无论是服务器还是客户端,socket通信本质上就是创建应用程序的类和类对象,并且在socket库提供的QUdpSocket和QTcpSocket类和类对象之间进行对象间通信,当socket对象中有数据时,自动通过信号+槽机制,自动回调应用程序注册到socket对象中的槽函数。这就是QT socket通信的本质。

1.3 QUdpSocket相关的信号

QUdpSocket类在Qt中用于进行UDP网络通信。下面是QUdpSocket中一些常用的信号:

  1. readyRead()当有新的数据可读取时触发该信号。可以通过调用readDatagram()方法读取数据。

  2. stateChanged(QAbstractSocket::SocketState state):当QUdpSocket的状态发生变化时触发该信号。状态可以是QAbstractSocket::UnconnectedState(未连接状态)、QAbstractSocket::HostLookupState(主机查找状态)、QAbstractSocket::ConnectingState(连接中状态)、QAbstractSocket::ConnectedState(已连接状态)等。

  3. errorOccurred(QAbstractSocket::SocketError socketError):当QUdpSocket发生错误时触发该信号。通过socketError参数可以获取到具体的错误类型,例如QAbstractSocket::ConnectionRefusedError(连接被拒绝错误)、QAbstractSocket::RemoteHostClosedError(远程主机关闭连接错误)等。

这些信号可以通过连接到相关的槽函数来实现相应的逻辑处理。使用示例如下:

// 声明QUdpSocket对象
QUdpSocket *udpSocket;

// 连接信号和槽函数
connect(udpSocket, &QUdpSocket::readyRead, this, &MyClass::readReady);
connect(udpSocket, &QUdpSocket::stateChanged, this, &MyClass::socketStateChanged);
connect(udpSocket, &QUdpSocket::errorOccurred, this, &MyClass::socketErrorOccurred);

// 槽函数实现
void MyClass::readReady()
{
    QByteArray datagram;
    datagram.resize(udpSocket->pendingDatagramSize());
    QHostAddress senderAddress;
    quint16 senderPort;
    udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
    // 处理读取到的数据
}

void MyClass::socketStateChanged(QAbstractSocket::SocketState state)
{
    // 处理状态变化
}

void MyClass::socketErrorOccurred(QAbstractSocket::SocketError socketError)
{
    // 处理错误
}

通过连接这些信号和相应的槽函数,可以实现对QUdpSocket对象的事件响应和处理。

1.4 QTcpSocket相关的信号

在Qt中,使用QTcpSocket类进行TCP网络通信。下面是QTcpSocket类的一些常见信号:

  1. connected()当TCP套接字成功连接到远程主机时发出的信号。

  2. disconnected()当TCP套接字与远程主机断开连接时发出的信号。

  3. readyRead()当有新的数据可供读取时发出的信号。可以通过调用read()方法读取数据。

  4. bytesWritten(qint64 bytes)当已成功写入一定字节数的数据后发出的信号。可以通过bytes参数获取已写入的字节数。

  5. hostFound()当QTcpSocket已成功查找到远程主机时发出的信号。

  6. stateChanged(QAbstractSocket::SocketState state)当QTcpSocket的状态发生变化时发出的信号。状态可以是QAbstractSocket::UnconnectedState(未连接状态)、QAbstractSocket::HostLookupState(主机查找状态)、QAbstractSocket::ConnectingState(连接中状态)、QAbstractSocket::ConnectedState(已连接状态)等。

  7. errorOccurred(QAbstractSocket::SocketError socketError)当QTcpSocket发生错误时发出的信号。通过socketError参数可以获取到具体的错误类型,例如QAbstractSocket::ConnectionRefusedError(连接被拒绝错误)、QAbstractSocket::RemoteHostClosedError(远程主机关闭连接错误)等。

这些信号可以通过连接到相关的槽函数来实现相应的逻辑处理。使用示例如下:

// 声明QTcpSocket对象
QTcpSocket *tcpSocket;

// 连接信号和槽函数
connect(tcpSocket, &QTcpSocket::connected, this, &MyClass::socketConnected);
connect(tcpSocket, &QTcpSocket::disconnected, this, &MyClass::socketDisconnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &MyClass::socketReadyRead);
connect(tcpSocket, &QTcpSocket::bytesWritten, this, &MyClass::bytesWritten);
connect(tcpSocket, &QTcpSocket::hostFound, this, &MyClass::hostFound);
connect(tcpSocket, &QTcpSocket::stateChanged, this, &MyClass::socketStateChanged);
connect(tcpSocket, &QTcpSocket::errorOccurred, this, &MyClass::socketErrorOccurred);

// 槽函数实现
void MyClass::socketConnected()
{
    // 处理连接成功事件
}

void MyClass::socketDisconnected()
{
    // 处理断开连接事件
}

void MyClass::socketReadyRead()
{
    // 处理有数据可读事件
}

void MyClass::bytesWritten(qint64 bytes)
{
    // 处理数据写入事件
}

void MyClass::hostFound()
{
    // 处理主机查找事件
}

void MyClass::socketStateChanged(QAbstractSocket::SocketState state)
{
    // 处理状态变化
}

void MyClass::socketErrorOccurred(QAbstractSocket::SocketError socketError)
{
    // 处理错误
}

通过连接这些信号和相应的槽函数,可以实现对QTcpSocket对象的事件响应和处理。

第2章 UDP通信示例

下面是一个简单的示例代码,展示了如何使用Qt进行UDP通信:

服务端代码:

// Server.h
#ifndef SERVER_H
#define SERVER_H

#include <QObject>
#include <QUdpSocket>

class Server : public QObject
{
    Q_OBJECT
public:
    explicit Server(QObject *parent = nullptr);

public slots:
    void readyRead();

private:
    QUdpSocket *udpSocket;
};



// Server.cpp
#include "Server.h"

Server::Server(QObject *parent) : QObject(parent)
{
    //创建udp socket对象
    udpSocket = new QUdpSocket(this);

    //由于是服务器,需要绑定socket的IP地址和端口号
    udpSocket->bind(QHostAddress::Any, 12345);

    //在socket对象和Server应用程序之间建立信号与回调函数机制
    //当Server中有数据时,socket自动发送信号给Server应用程序,自动调用Server注册到Socket中的回调函数
    connect(udpSocket, &QUdpSocket::readyRead, this, &Server::readyRead);
}

// Server的应用程序
void Server::readyRead()
{
    while (udpSocket->hasPendingDatagrams()) {
        //为读取/接收数据准备缓冲区
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        
        //为获取发送方信息准备对象
        QHostAddress senderAddress;
        quint16 senderPort;
        
        //应用程序从socket中读取数据
        udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
        qDebug() << "Received data:" << datagram << "from" << senderAddress.toString() << ":" << senderPort;

        // 在这里可以对接收到的数据进行处理

        // 回复客户端: 即向socket写数据
        udpSocket->writeDatagram("Server response", senderAddress, senderPort);
    }
}

客户端代码:

// Client.h
#ifndef CLIENT_H
#define CLIENT_H

#include <QObject>
#include <QUdpSocket>

class Client : public QObject
{
    Q_OBJECT
public:
    explicit Client(QObject *parent = nullptr);

public slots:
    void readyRead();

private:
    QUdpSocket *udpSocket;
};



// Client.cpp
#include "Client.h"

Client::Client(QObject *parent) : QObject(parent)
{
    
    //创建udp socket对象
    udpSocket = new QUdpSocket(this);

    //当udpSocket中有数据时,TCP/IP协议栈自动会给该socket发送事件
    //该socket对象自动会发送readyRead的信号,请求应用程序读取数据
    //应用程序只需要把自己对象的槽函数挂接到新创建的socket对象的readyRead事件上即可
    //有socket中有数据时,创建的socket对象自动会发送readyRead事件,这是QT的网络通信协作栈保证的
    connect(udpSocket, &QUdpSocket::readyRead, this, &Client::readyRead);
}

//readyRead是应用程序挂接到socket上,读取socket数据的函数
//本质上是一个回调函数而已
void Client::readyRead()
{
    //client检查socket中是否有数据
    //如果有数据,则一直读数据,然后数据读完
    //如果没有数据,则直接退出
    while (udpSocket->hasPendingDatagrams()) 
    {
        //准备好数据接收buffer
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());

        //获取数据发送端的IP地址和端口号
        QHostAddress senderAddress;
        quint16 senderPort;
 
       //从upd socket读取数据,并获得数据发送端的信息
       udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
       
        qDebug() << "Received data:" << datagram << "from" << senderAddress.toString() << ":" << senderPort;

        // 在这里可以对接收到的数据进行处理
        // .............................
        // .............................
    }
}

第3章 TCP通信代码示例

特别说明:

TCP的socket与UDP通信相似,都是应用程序类对象向socket类对象注册回调函数,当socket对象发生某种事件,socket对象通过先前注册的回调函数(信号与槽),自动调用相应的应用程序处理socket相关信号!!!

下面是一个简单的示例代码,展示了如何使用Qt进行TCP/IP通信:

服务器端代码:

// Server.h
#ifndef SERVER_H
#define SERVER_H

#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>

class Server : public QObject
{
    Q_OBJECT
public:
    explicit Server(QObject *parent = nullptr);

public slots:
    void newConnection();
    void readyRead();
    void disconnected();

private:
    QTcpServer *tcpServer;
    QTcpSocket *clientSocket;
};

// Server.cpp
#include "Server.h"

Server::Server(QObject *parent) : QObject(parent)
{
    tcpServer = new QTcpServer(this);

    if (!tcpServer->listen(QHostAddress::Any, 12345)) {
        qDebug() << "Server could not start!";
    } else {
        qDebug() << "Server started!";
    }

    connect(tcpServer, &QTcpServer::newConnection, this, &Server::newConnection);
}

void Server::newConnection()
{
    clientSocket = tcpServer->nextPendingConnection();
    connect(clientSocket, &QTcpSocket::readyRead, this, &Server::readyRead);
    connect(clientSocket, &QTcpSocket::disconnected, this, &Server::disconnected);
    qDebug() << "Client connected!";
}

void Server::readyRead()
{
    QByteArray data = clientSocket->readAll();
    qDebug() << "Received data:" << data;

    // 在这里可以对接收到的数据进行处理

    // 回复客户端
    clientSocket->write("Server response");
    clientSocket->flush();
}

void Server::disconnected()
{
    qDebug() << "Client disconnected!";
    clientSocket->deleteLater();
}

客户端代码:

// Client.h
#ifndef CLIENT_H
#define CLIENT_H

#include <QObject>
#include <QTcpSocket>

class Client : public QObject
{
    Q_OBJECT
public:
    explicit Client(QObject *parent = nullptr);

public slots:
    void connected();
    void disconnected();
    void readyRead();

private:
    QTcpSocket *tcpSocket;
};

// Client.cpp
#include "Client.h"

Client::Client(QObject *parent) : QObject(parent)
{
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket, &QTcpSocket::connected, this, &Client::connected);
    connect(tcpSocket, &QTcpSocket::disconnected, this, &Client::disconnected);
    connect(tcpSocket, &QTcpSocket::readyRead, this, &Client::readyRead);

    tcpSocket->connectToHost("127.0.0.1", 12345);
}

void Client::connected()
{
    qDebug() << "Connected to Server!";
    tcpSocket->write("Hello from Client!");
    tcpSocket->flush();
}

void Client::disconnected()
{
    qDebug() << "Disconnected from Server!";
}

void Client::readyRead()
{
    QByteArray data = tcpSocket->readAll();
    qDebug() << "Received data:" << data;

    // 在这里可以对接收到的数据进行处理
}

在上面的示例中,服务器监听在IP地址为"Any"(0.0.0.0)的所有网络接口上,端口号为12345的地址上。客户端连接到"127.0.0.1"(本地回环地址)上的12345端口。

当客户端连接到服务器后,服务器会打印"Client connected!"。当服务器接收到客户端发来的数据后,会在控制台打印该数据,然后回复客户端。客户端在接收到服务器的回复后,会在控制台打印该数据。

请确保在客户端连接之前启动服务器,以便能够接收到客户机的连接请求。

这只是一个简单的示例,你可以根据需要进行修改和扩展。请注意,在实际使用中,应该对错误和异

猜你喜欢

转载自blog.csdn.net/HiWangWenBing/article/details/131747081