QT学习(20):QTcpSocket和QAbstractSocket

目录

    • QAbstractSocket
      • 建立连接的函数和信号
      • 阻塞功能
      • QTcpSocket
    • QAbstractSocketPrivate
    • QAbstractSocketEngine和QNativeSocketEngine

QAbstractSocket

QAbstractSocket 是QTcpSocket和QUdpSocket的基类,并且包含这两个类的所有通用功能。如果需要套接字,有两种选择:

  • 实例化一个QTcpSocket或QUdpSocket对象
  • 创建一个本机套接字描述符,实例化 QAbstractSocket,然后调用setSocketDescriptor()来包装本机套接字。

TCP(传输控制协议)是一种可靠的、面向流的、面向连接的传输协议。UDP(用户数据报协议)是一种不可靠的、面向数据报的无连接协议。在实践中,这意味着 TCP 更适合连续传输数据,而当可靠性不重要时,可以使用更轻量级的 UDP。

建立连接的函数和信号

QAbstractSocket 的 API 统一了两种协议之间的大部分差异。例如,尽管 UDP 是无连接的,但connectToHost()会为 UDP 套接字建立虚拟连接,从而能够以或多或少相同的方式使用 QAbstractSocket,而不管底层协议如何。在内部,QAbstractSocket 会记住传递给connectToHost()的地址和端口,而read()write()等函数会使用这些值。

在任何时候,QAbstractSocket 都有一个状态(由state()返回)。初始状态为UnconnectedState。调用connectToHost()后,套接字首先进入HostLookupState。如果找到主机,QAbstractSocket 将进入ConnectingState并发出hostFound()信号。建立连接后,它会进入ConnectedState并发出connected()。如果在任何阶段发生错误,则会发出errorOccurred()。每当状态发生变化时,都会发出stateChanged()。为方便起见,如果套接字已准备好进行读取和写入,则返回isValid(),但请注意,在读取和写入发生之前套接字的状态必须是ConnectedState

// ### Qt6: make the first one virtualbool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform);// ### Qt6: de-virtualize connectToHost(QHostAddress) overloadvirtual void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);virtual void disconnectFromHost();void abort();

通过调用read()write()读取或写入数据,或使用便利函数readLine()和write()。QAbstractSocket 还继承了getChar()putChar()ungetChar(),它们适用于单个字节。当数据写入套接字时,会发出btyeWritten()信号。请注意,Qt不限制写入缓冲区的大小。您可以通过收听此信号来监控其大小。

每当有新的数据块到达时,都会发出readyRead()信号。bytesAvailable()然后返回可用于读取的字节数。通常,会将readyRead()信号连接到插槽并读取其中的所有可用数据。如果您没有一次读取所有数据,则剩余数据稍后仍将可用,并且任何新的传入数据都将附加到 QAbstractSocket 的内部读取缓冲区中。要限制读取缓冲区的大小,请调用setReadBufferSize()

要关闭套接字,请调用disconnectFromHost()。QAbstractSocket进入ClosingState。将所有挂起的数据写入套接字后,QAbstractSocket 实际上会关闭套接字,进入UnconnectedState,然后发出disConnected()。如果要立即中止连接,丢弃所有挂起的数据,请改为调用abort()。如果远程主机关闭连接,QAbstractSocket 将发出errorOccurred(),在此期间套接字状态仍为ConnectedState,然后发出disConnected()信号。

Q_SIGNALS:void hostFound();void connected();void disconnected();void stateChanged(QAbstractSocket::SocketState);void error(QAbstractSocket::SocketError);
#ifndef QT_NO_NETWORKPROXYvoid proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
#endif

通过调用peerPort()peerAddress()获取连接对象的端口和地址。peerName()返回传递给connectToHost()的连接对象的主机名。localPort()localAddress()返回本地套接字的端口和地址。

阻塞功能

QAbstractSocket 提供了一组函数,用于挂起调用线程,直到发出某些信号。这些函数可用于实现阻塞套接字:

  • waitForConnected()阻塞,直到建立连接。
  • waitForReadyRead()阻塞,直到有新数据可供读取。
  • waitForBytesWritten()阻塞,直到将一个数据有效负载写入套接字。
  • waitForDisconnected()阻塞,直到连接关闭。
// for synchronous accessvirtual bool waitForConnected(int msecs = 30000);bool waitForReadyRead(int msecs = 30000) override;bool waitForBytesWritten(int msecs = 30000) override;virtual bool waitForDisconnected(int msecs = 30000);

使用阻塞套接字进行编程与使用非阻塞套接字进行编程截然不同。阻塞套接字不需要事件循环,通常会导致代码更简单。但是,在 GUI 应用程序中,阻塞套接字应仅在非 GUI 线程中使用,以避免冻结用户界面。

注意:不鼓励将阻塞功能与信号一起使用。应使用其中之一。

class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice
{Q_OBJECT
public:enum SocketType {TcpSocket,UdpSocket,SctpSocket,UnknownSocketType = -1 };//enum NetworkLayerProtocol,SocketError,SocketState,SocketOption,BindFlag,PauseModeQAbstractSocket(SocketType socketType, QObject *parent);virtual ~QAbstractSocket();//bind+connectToHost+disConnected//localPort\localAddress\peerPort\peerAddress\peerName\canReadLine\readBufferSize、bytesAvailable、bytesToWrite\setReadBufferSize//.................
protected:qint64 readData(char *data, qint64 maxlen) override;qint64 readLineData(char *data, qint64 maxlen) override;qint64 writeData(const char *data, qint64 len) override;
//setSocketState\setSocketError\setLocalPort\setLocalAddress\setPeerPort\setPeerAddress\setPeerNameQAbstractSocket(SocketType socketType, QAbstractSocketPrivate &dd, QObject *parent = nullptr);
private:
//..............
};

QTcpSocket

从QTcpSocket和其私有类的定义中可以知道QTcpSocket只是简单继承了QAbstractSocket。实例化对象调用的方法都是父类版本的虚函数。

class Q_NETWORK_EXPORT QTcpSocket : public QAbstractSocket
{Q_OBJECT
public:explicit QTcpSocket(QObject *parent = nullptr);virtual ~QTcpSocket();
protected:QTcpSocket(QTcpSocketPrivate &dd, QObject *parent = nullptr);QTcpSocket(QAbstractSocket::SocketType socketType, QTcpSocketPrivate &dd,QObject *parent = nullptr);
private:Q_DISABLE_COPY(QTcpSocket)Q_DECLARE_PRIVATE(QTcpSocket)
};
class QTcpSocketPrivate : public QAbstractSocketPrivate
{Q_DECLARE_PUBLIC(QTcpSocket)
};
QTcpSocket socket;
socket.bind(address, port);

QTcpSocket绑定本地IP地址和端口时调用的是QAbstractSocket版本的虚函数bind(),在内部调用d指针的bind(),QAbstractSocketPrivate版本。

*/
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
{Q_D(QAbstractSocket);return d->bind(address, port, mode);
}

在d指针的bind()函数内部,调用了QAbstractSocketEngine类型成员变量socketEngine的bind(),将socketEngine中的描述符赋值给cachedSocketDescriptor。并更改连接状态state,将socketEngine中的地址和端口赋值给d指针,发出状态改变的信号。

bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode)
{//...............................bool result = socketEngine->bind(address, port);cachedSocketDescriptor = socketEngine->socketDescriptor();
//........................state = QAbstractSocket::BoundState;localAddress = socketEngine->localAddress();localPort = socketEngine->localPort();emit q->stateChanged(state);//.............
}
d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(qintptr socketDescripter, QObject *parent)
{//.......................................return new QNativeSocketEngine(parent);
}

QAbstractSocketEngine基类指针对象socketEngine实际上保存的是一个QNativeSocketEngine子类对象。socketEngine的bind()函数为QNativeSocketEngine::bind(),在该函数内调用d指针的nativeBind()函数,为QNativeSocketEnginePrivate::nativeBind()

class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine{};
bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
{
//......................if (!d->nativeBind(d->adjustAddressProtocol(address), port))return false;
//.............................
}

QNativeSocketEnginePrivate::nativeBind()在windows系统和unix系统中有不同的定义。以windows系统为例,在该函数中调用windows的API:setsockoptbind

bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port)
{setPortAndAddress(port, address, &aa, &sockAddrSize);//..................::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) );//.........................int bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize);
}

connectToHost()等函数的定义和bind类似。比如readData()是定义在QIODevice中的纯虚函数,在QAbstractSocket中实现,在QAbstractSocket::readData()d->socketEngine->read(data, maxSize)调用QNativeSocketEngine::read(),在该函数中通过d指针调用QNativeSocketEnginePrivate::nativeRead(),在windows环境下最终通过调用windows的API:WSARecv完成读操作。在unix环境中调用qt_safe_read()完成读操作,在该函数中通过doWhile循环调用::read()

QAbstractSocketPrivate

QAbstractSocketPrivate定义的成员变量包括IP地址、端口等信息,还有套接字描述符、连接定时器和套接字引擎。其中套接字引擎socketEngine负责完成端口和IP地址绑定、连接等核心功能。

class QAbstractSocketPrivate : public QIODevicePrivate, public QAbstractSocketEngineReceiver
{Q_DECLARE_PUBLIC(QAbstractSocket)
public:QAbstractSocketPrivate();virtual ~QAbstractSocketPrivate();//................QString hostName;quint16 port;QHostAddress host;QList<QHostAddress> addresses;quint16 localPort;quint16 peerPort;QHostAddress localAddress;QHostAddress peerAddress;QString peerName;QAbstractSocketEngine *socketEngine;qintptr cachedSocketDescriptor;
//........................................QTimer *connectTimer;
//.....................................................
};

QAbstractSocketEngine和QNativeSocketEngine

QAbstractSocketEngine基类指针对象套接字引擎socketEngine实际上保存的是一个QNativeSocketEngine子类对象。QAbstractSocketEngine是一个抽象类,定义了bind、connectToHost、listen等一系列纯虚函数。

class Q_AUTOTEST_EXPORT QAbstractSocketEngine : public QObject
{Q_OBJECT
public:static QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent);static QAbstractSocketEngine *createSocketEngine(qintptr socketDescriptor, QObject *parent);
//有省略.......................virtual bool connectToHost(const QHostAddress &address, quint16 port) = 0;virtual bool connectToHostByName(const QString &name, quint16 port) = 0;virtual bool bind(const QHostAddress &address, quint16 port) = 0;virtual bool listen() = 0;
//有省略.......................virtual qint64 read(char *data, qint64 maxlen) = 0;virtual qint64 write(const char *data, qint64 len) = 0;
//有省略.......................
};

QAbstractSocketEngine中定义的纯虚函数在QNativeSocketEngine中实现,在函数内部通过d指针调用私有类QNativeSocketEnginePrivate中对应的函数。

class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
{Q_OBJECT
public://有省略.......................bool connectToHost(const QHostAddress &address, quint16 port) override;bool connectToHostByName(const QString &name, quint16 port) override;bool bind(const QHostAddress &address, quint16 port) override;//有省略.......................qint64 read(char *data, qint64 maxlen) override;qint64 write(const char *data, qint64 len) override;
//有省略.......................
};
class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
{Q_DECLARE_PUBLIC(QNativeSocketEngine)
public://有省略.......................bool nativeConnect(const QHostAddress &address, quint16 port);bool nativeBind(const QHostAddress &address, quint16 port);bool nativeListen(int backlog);int nativeAccept();
//有省略.......................qint64 nativeRead(char *data, qint64 maxLength);qint64 nativeWrite(const char *data, qint64 length);
//有省略.......................
};

QAbstractSocketEnginePrivate中只保存了IP地址等信息,套接字引擎的私有类继承了这些成员变量,赋值给QAbstractSocket的成员变量,返回给QAbstractSocket的函数。

class QAbstractSocketEnginePrivate : public QObjectPrivate
{Q_DECLARE_PUBLIC(QAbstractSocketEngine)
public:QAbstractSocketEnginePrivate();mutable QAbstractSocket::SocketError socketError;mutable bool hasSetSocketError;mutable QString socketErrorString;QAbstractSocket::SocketState socketState;QAbstractSocket::SocketType socketType;QAbstractSocket::NetworkLayerProtocol socketProtocol;QHostAddress localAddress;quint16 localPort;QHostAddress peerAddress;quint16 peerPort;int inboundStreamCount;int outboundStreamCount;QAbstractSocketEngineReceiver *receiver;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/222721.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WT588F02B-8S语音芯片:灵活应用的语音播放利器,实现多重优势

在智能语音交互领域&#xff0c;唯创知音WT588F02B-8S可重复擦写&#xff08;Flash型&#xff09;语音芯片声音播放提示IC凭借其出色的特性&#xff0c;为用户提供了更灵活、高效的语音解决方案。本文将聚焦于该芯片的应用优势&#xff0c;包括实现用户自主更新语音文件、节约打…

WTN6xxx系列OTP语音芯片:智能语音解决方案的可靠之选

在智能语音交互领域&#xff0c;唯创知音的WTN6xxx系列OTP语音芯片以其独特的特性成为声音播放提示IC的可靠之选。本文将深入探讨WTN6xxx系列OTP语音芯片的应用优势&#xff0c;展示其在各个方面的卓越性能。 一、低成本、高性能 1.经济实惠&#xff1a; WTN6xxx系列OTP语音芯…

AGI魔盒,会放出冥王PLUTO还是阿童木?

人机共生&#xff0c;是科幻作品永恒的主题。其中&#xff0c;《冥王PLUTO》可能是最早探讨人类与机器人如何在冲突中共存的漫画作品。 如果说阿童木是人机共生的“和平使者”&#xff0c;启蒙了几代人对机器人的信任和热爱,那么冥王PLUTO就是阿童木的反面&#xff0c;一个心怀…

基于ssm网络安全宣传网站设计论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网络安全宣传网站就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

2023-12-14 使用Qt画一条曲线(AI辅助)

点击 <C 语言编程核心突破> 快速C语言入门 使用Qt画一条曲线 前言一、Qchart简介二、代码总结 前言 要解决问题: 有一个函数, 生成一些点, 想画一条曲线. 想到的思路: 这个用Qchart比较简单. 其它的补充: 需要稍许配置 一、Qchart简介 QChart是Qt中的一个图表控件&a…

记录 | mac打开终端时报错:login: /opt/homebrew/bin/zsh: No such file or directory [进程已完成]

mac打开终端时报错&#xff1a;login: /opt/homebrew/bin/zsh: No such file or directory [进程已完成]&#xff0c;导致终端没有办法使用的情况 说明 zsh 没有安装或者是安装路径不对 可以看看 /bin 下有没有 zsh&#xff0c;若没有&#xff0c;肯定是有 bash 那就把终端默…

三层交换机原理与配置

文章目录 三层交换机原理与配置一、三层交换技术概述二、传统的 MLS三、基于CEF 的MLS1、转发信息库&#xff08;FIB&#xff09;2、邻接关系表3、工作原理&#xff1a; 四、三层交换机的配置1、三层交换机配置命令2、三层交换机配置步骤 三层交换机原理与配置 一、三层交换技…

【Hadoop】

Hadoop是一个开源的分布式离线数据处理框架&#xff0c;底层是用Java语言编写的&#xff0c;包含了HDFS、MapReduce、Yarn三大部分。 组件配置文件启动进程备注Hadoop HDFS需修改需启动 NameNode(NN)作为主节点 DataNode(DN)作为从节点 SecondaryNameNode(SNN)主节点辅助分…

如何学习Kubernetes,学习K8S入门教程

学习 Kubernetes&#xff08;K8s&#xff09;确实不容易 你的硬件资源有限时&#xff0c;不过别担心&#xff0c;我帮你理清思路&#xff0c;让你在学习 K8s 的路上更加从容。 1、资源限制下的学习方法 当硬件资源有限时&#xff0c;一个好的选择是使用云服务提供的免费层或者…

✺ch2——OpenGL图像管线

目录 基于C图形应用&#xff06;管线概览OpenGL类型第一个C/OpenGL应用程序◍API (1) GLSL类型着色器——画一个点的程序◍API (2)◍API (3) 栅格化像素操作——Z-buffer算法检测 OpenGL 和 GLSL 错误◍API (4) 从顶点来构建一个三角形场景动画◍API (5) OpenGL某些方面的数值—…

C语言第四十九弹----模拟使用strcpy函数

使用C语言模拟使用strcpy函数 定义&#xff1a;strcpy 函数是 C 标准库中用于字符串复制的函数。它接受两个参数&#xff0c;第一个参数 dest 是目标字符串的指针&#xff0c;第二个参数 src 是源字符串的指针&#xff0c;函数的功能是将源字符串复制到目标字符串中&#xff0…

音频筑基:总谐波失真THD+N指标

音频筑基&#xff1a;总谐波失真THDN指标 THDN含义深入理解 在分析音频信号中&#xff0c;THDN指标是我们经常遇到的概念&#xff0c;这里谈谈自己的理解。 THDN含义 首先&#xff0c;理解THD的定义&#xff1a; THD&#xff0c;Total Harmonic Distortion&#xff0c;总谐波…

C语言—每日选择题—Day48

第一题 1. 已知宏定义&#xff1a; #define M y*y3*y &#xff0c; 则表达式 s3*M4*My*M 预处理阶段后的结果是 A&#xff1a;s3*(y*y3*y)4*(y*y3*y)y*(y*y3*y) B&#xff1a;s3*(y*y)3*y4*(y*y)3*yy*(y*y)3*y C&#xff1a;s3*y*y3*y4*y*y3*yy*y*y3*y D&#xff1a;s3*(y*y)(3…

ArcGIS导入excel中的经纬度信息,绘制矢量

1.首先整理坐标信息 2.其次转成2003格式的excel文件 3.导入arcgis&#xff0c;点击右键添加excel数据 4.显示xy数据 5.显示经度和纬度信息 6&#xff1a;点击【地理坐标系】->【World】->【WGS 1984】->【确定】 7.投影带的确定方式&#xff1a; 因为自己一直…

webpack总结

我们从 4 个角度对 webpack 和代码进行了优化&#xff1a; 提升开发体验 使用 Source Map 让开发或上线时代码报错能有更加准确的错误提示。 提升 webpack 提升打包构建速度 使用 HotModuleReplacement 让开发时只重新编译打包更新变化了的代码&#xff0c;不变的代码使用缓…

使用pyinstaller将python代码打包成exe文件

首先使用pyinstaller生成spec文件&#xff0c;然后配置spec文件重新打包 pyinstaller -D app.py pyinstaller参数 -F &#xff1a;打包单个文件 -D &#xff1a;打包多个文件&#xff0c;在dist中生成很多依赖文件 -c:使用控制台执行&#xff0c;只对windows有效 -n : 生…

spring 笔记五 SpringMVC的数据响应

文章目录 SpringMVC的数据响应SpringMVC的数据响应方式回写数据 SpringMVC的数据响应 SpringMVC的数据响应方式 页面跳转 直接返回字符串通过ModelAndView对象返回 回写数据 直接返回字符串返回对象或集合 返回字符串形式 直接返回字符串&#xff1a;此种方式会将返回的字符…

腾讯云Elasticsearch Service产品体验

基本介绍 产品概述 腾讯云 Elasticsearch Service&#xff08;ES&#xff09;是云端全托管海量数据检索分析服务&#xff0c;拥有高性能自研内核&#xff0c;集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群&#xff0c;也支持免运维、自动弹性、按需…

Clickhouse RoaringBitmap

https://blog.csdn.net/penriver/article/details/119736050 https://juejin.cn/post/7179956435806076988 BitMap适合连续密集的正整数存储&#xff0c;对于稀疏的正整数存储&#xff0c;其性能在很多时候是没办法和int数组相比的&#xff0c;尤其是正整数跨度较大的场景&…

架构设计系列之常见架构(一)

本部分对常见架构进行简单的说明。 一、三层架构之经典 MVC 经典的 MVC 架构&#xff08;Model-View-Controller&#xff09;架构是软件系统架构设计中的经典&#xff0c;它将应用程序分为三个主要部分&#xff1a; 模型&#xff08;Model&#xff09;视图&#xff08;View&…