热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Qt网络编程QTcpServer和QTcpSocket的理解

前一段时间通过调试Qt源码,大致了解了Qt的事件机制、信号槽机制。毕竟能力和时间有限。有些地方理解的并不是很清楚。开发环境:linux((fedora17),Qt版本(qt-everywh

前一段时间通过调试Qt源码,大致了解了Qt的事件机制、信号槽机制。毕竟能力和时间有限。有些地方理解的并不是很清楚。

开发环境:linux((fedora 17),Qt版本(qt-everywhere-opensource-src-4.7.3)。

Qt网络编程比较常用的两个类:QTcpServer和QTcpSocket。当然还有UDP的类(在这就不介绍了)。

这两个类的操作比较简单。


QTcpServer的基本操作:

1、调用listen监听端口。

2、连接信号newConnection,在槽函数里调用nextPendingConnection获取连接进来的socket。


QTcpSocket的基本能操作:

1、调用connectToHost连接服务器。

2、调用waitForConnected判断是否连接成功。

3、连接信号readyRead槽函数,异步读取数据。

4、调用waitForReadyRead,阻塞读取数据。


在main函数中调用 app.exec()之后会进入事件循环。在Qt的事件循环中回调用QEventLoop::processEvents。在这个函数中又会调用QAbstractEventDispatcher::processEvents。

Qt为不同的平台,提供了不同的EventDispatcher。本人用的是linux。接下来会调用QEventDispatcherUNIX::processEvents。doSelect,select。

通过调试源码可以证实,函数调用的顺序。其实Qt网络编程,底层是通过select实现的。可能跟跨平台有关系吧,采用select。linux有比select更好的API(poll,epoll)。

通过调试,可以发现newConnection和readyRead这两个信号都和select有关系。

因为调用的是select所以服务器端肯定会有监视文件描述符数量问题。select默认的是1024。Qt默认的也是1024。服务器端在调用nextPendingConnection。会创建一个QTcpSocket对象。这个对象在QTcpServer对象删除的时候会自动删除。也可以手动删除,避免浪费内存,可以在该对象断开的时候,删除该对象。该对象在断开的时候,select调用会清除该描述符,不会影响文件描述符的数量。如果内存足够用的话,不需要关心该对象。


接下来介绍readyRead信号。该信号用的比较多。

该信号当有数据要读的时候,会触发该信号。不过在触发该信号之前,Qt会尝试读取bytesToRead数据,存在内部缓冲区中。

下面是Qt源码片段

    qint64 bytesToRead = socketEngine->bytesAvailable();
#ifdef Q_OS_LINUX
if (bytesToRead > 0) // ### See setSocketDescriptor()
bytesToRead += addToBytesAvailable;
#endif
if (bytesToRead == 0) {
// Under heavy load, certain conditions can trigger read notifications
// for socket notifiers on which there is no activity. If we continue
// to read 0 bytes from the socket, we will trigger behavior similar
// to that which signals a remote close. When we hit this condition,
// we try to read 4k of data from the socket, which will give us either
// an EAGAIN/EWOULDBLOCK if the connection is alive (i.e., the remote
// host has _not_ disappeared).
bytesToRead = 4096;
}
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size()))
bytesToRead = readBufferMaxSize - readBuffer.size();

// Read from the socket, store data in the read buffer.
char *ptr = readBuffer.reserve(bytesToRead);
qint64 readBytes = socketEngine->read(ptr, bytesToRead);
if (readBytes == -2) {
// No bytes currently available for reading.
readBuffer.chop(bytesToRead);
return true;
}
readBuffer.chop(int(bytesToRead - (readBytes <0 ? qint64(0) : readBytes)));
qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const{    int nbytes = 0;    // gives shorter than true amounts on Unix domain sockets.    qint64 available = 0;#ifdef Q_OS_SYMBIAN	if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)#else    if (qt_safe_ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)#endif        available = (qint64) nbytes;    return available;}
读取数据的步骤:

1、通过ioctl获取系统能够读取的数据长度。

2、在linux系统中多加4K数据(跟linux域套接字有关系)。

3、如果调用setReadBufferSize()设置缓冲区大小的话。重新计算bytesToRead。从代码中可以看出来,只有bytesToRead大的话,才重新计算。如果readBufferMaxSize设置过大,不会重新计算。

4、调用read函数尝试读取bytesToRead长度数据。返回实际读取的数据长度readBytes。通过调试可以看出readBytes和bytesToRead相差并不是很多。

bytesAvailable()函数返回缓冲区长度。如果没设定readBufferMaxSize,该函数返回值主要取决于ioctl系统调用。该系统调用跟系统当前运行的状态有关系。

可能会出现的问题:

1、当触发readyRead信号,但是缓冲区的长度小于另一端发送的数据。这样就会触发多次readyReady信号。如果一次槽函数里面读取缓冲区的长度,数据就会接受不全,进行数据处理肯定会出问题。Qt的例子中提供了一种方法。在发送数据的头部加上数据的长度。只有当bytesAvailable大于数据的长度时,才读取数据。


2、系统API里面调用read是从系统缓冲区里面读取数据。如果系统缓冲区满的话。以前的就会被覆盖。但是Qt里面也存在缓冲区。如果一端发送数据。另一端并不从Qt缓冲区读取数据。那么Qt就会无限制的从系统缓冲区中读出数据放置自己内部缓冲区。最后肯定会出现堆栈满的情况,系统异常退出。

想进一步了解Qt网络的东西,可以搭建调试环境。自己调试来分析源码,查找原因。



推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
author-avatar
单身王子550130
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有