0 Star 11 Fork 2

百十七 / 树莓派4b_数据采集与上传

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
bugs.txt 11.07 KB
一键复制 编辑 原始数据 按行查看 历史
百十七 提交于 2023-05-13 21:12 . update multi_client_test
1.epoll初始化新连接的客户端套接字性能底下(epoll模型调用顺序, 使用注意事项)
解决: 客户端套接字设置为非阻塞态 UUID协商过程移出初始化, 放在下一个事件循环中处理
SSL握手过程移出初始化(SSL_accept 需要多次读写套接字), 放在下一个事件循环中处理
2.thread_pool内存性能优化
解决:预先分配好内存,而不是用一次分配一次
3.树莓派客户端每发送一个数据包就断开一次连接重现建立连接
解决: 服务器发送心跳数据包为客户端保活,防止客户端高频复位
4.UUID协商, 服务器非阻塞SSL_write函数先于客户端阻塞SSL_read函数调用, 发生死锁
解决: (烂办法)调用客户端调用sleep函数等待一段时间后再调用SSL_read函数
对于服务器而言,接收uuid数据和鉴别,分配,发送uuid处于同一个事件循环,因此理论上很快就会调用SSL_write
5.客户端由于断电,SIGKILL信号等事件发生急停,服务器由于不能收到心跳数据包"FIN"而释放客户端信息
解决:利用TCP的KeepAlive机制和 epoll 函数提供的 EPOLLHUP,EPOLLRDHUP, EPOLLERR 等事件完成客户端的释放
6.客户端的心跳数据包"FIN"慢了一步,被服务器判定为客户端非正常退出
解决:移除该心跳数据包机制, 客户端信息释放全部由epoll管理, 降低程序复杂性
7.sql_pool_conn_release函数归还连接后原指针仍然可以继续访问sql连接
解决: 修改函数定义, 运用二级指针, 归还连接后将指针归为NULL
8.客户端拿到UUID向本地文件记录时总是写全0
解决: memset函数将数据清零了, 将memset函数位置进行调整
9.客户端软件多开压力测试中, 发现连接池分配出现漏洞, 很多客户端在初始化时由于池满不能得到有效连接
解决: 不再在初始化时分配sql连接, 更改为真正数据交互时 需要写入数据库时申请数据库连接并且用完立即归还连接
10.压力测试中, 服务器分配的客户端套接字都是奇数, 但如果客户端一个一个慢慢连接就没事
解释: 在SSL/TLS握手过程中, 会涉及到大量的随机数生成操作, 这些随机数通常是使用操作系统提供的随机数生成器生成的
在Linux中, 这个随机数生成器通常是通过读取/dev/urandom设备实现的
而/dev/urandom设备使用熵池来生成随机数, 当系统启动时熵池通常会初始化为一些随机的值
然后在运行过程中不断从各种事件(如键盘,鼠标,磁盘I/O等)中收集熵, 用于生成更多的随机数
因此当有大量的客户端连接时, SSL握手过程需要频繁地使用随机数生成器, 这可能会消耗系统中的熵池
如果熵池中的熵不足, /dev/urandom设备可能会被阻塞, 直到足够的熵被积累
这可能会导致一些套接字文件描述符被暂时保留, 以便随机数生成器在/dev/urandom设备中生成足够的随机数
补充: 非SSL连接也会出现同样的问题, 经过分析, 每个客户端连接都会被服务器分配两个套接字
进一步排查: 只有使用本人开发的 "客户端多开" 压力测试软件才会出现这个问题, 即使多开的数量为 1, 也会被分配两个套接字
解决: [忽略] 非服务器主体功能问题, 即使被分配了两个套接字, 但经过测试这它们都会被正常释放
11.代码建议性调整, 在处理树莓派客户端连接时时使用了线程不安全的函数
解决: 将 localtime 函数修改为 localtime_r 函数
另外 strftime 函数在C11中被定义为线程安全函数, 因此这里不需要修改为 strftime_l 函数
12.HTTP服务器与客户端浏览器建立SSL安全连接发生错误, ERR_SSL_PROTOCOL_ERROR
暂未查明原因
bug已经消失, 但我不知道它是怎么出现和怎么解决的, 玄学事件了属于是
13.浏览器访问网页时发出ERR_EMPTY_RESPONSE错误, 服务器发生程序段错误
推测http_request生成时访问了无效内存
问题排查: 用于存储网页数据的栈区字符数组分配过小, 导致内存越界
14.向网页发送二进制数据(图片)时,总是不能发送完全
问题排查: 二进制数据不能使用字符串函数处理如 strlen, strcpy, sprintf
解决: 使用 memcpy 内存拷贝函数代替, 数据长度使用头尾指针的差值
15.web服务器部分HTTP报文回复不完善, 即使客户端的请求不合法, 也应当回复一个相应的报文
问题排查: 增加默认消息, 除了规定好的GET请求以外, 一律返回非 200 OK
16.客户端产生实时数据会写入哈希表中, 但是客户端断开连接时数据似乎不能从哈希表中清理掉
推测: hash_table_info_delete 函数没有正常工作
解决: 在raspiEventPoll函数中,当发生EPOLLRDHUP事件时需要清理客户端信息
hash_table_info_delete函数和hash_table_client_del函数调用顺序反了
导致前者得到的参数值是无效的
17.[不稳定触发] 服务器传输完背景图片后,浏览器标识请求未传输完毕,(浏览器标签持续转圈圈,Esc后背景图片消失)
暂未发现可能存在的原因
18.在为httpEventPoll中为任务启用线程池技术时,客户端浏览器网页访问使得服务器软件稳定发生段错误
问题排查: 经过调试, 发生段错误的位置总是在 SSL_accept 函数调用时发生
猜测: 浏览器尝试建立SSL连接时发送数据很快,导致第一个线程在第一次 SSL_accept 过程中又被 epoll
安排了另外一个线程处理同一个客户端连接, 即客户端结构体信息资源没有做到互斥访问(同一时间只能有一个线程处理同一个客户端)
解决: 为客户端结构体添加 pthread_mutex_t 字段, 任何线程在任何时间调用 processHttpClient 时都必须尝试获取锁
19.[不稳定触发] 客户端浏览器在访问服务器时偶尔使得服务器报了一长串SSL握手失败的消息
猜测: 浏览器在短时间内和服务器连接过, 可能保存了 SSL 会话
导致浏览器访问服务器时直接跳过了 SSL 握手过程, 直接发送 HTTP 报文导致服务器 SSL_accept 失败
解决: [暂时未计划为服务器添加 SSL 会话机制], 所以等待一段时间(TCP连接断开后) 重新访问即可
20.服务器web模块压力测试成绩不理想, 并且在约等于 50次/秒 的请求速率下最终发生程序段错误
"毫无疑问, 内存错误, 必然是 malloc/free 引起的"
"没办法, 毕竟使用了动态内存分配的函数不再是线程安全函数"
解决: 首先将调用最频繁的perr日志函数中的动态内存分配去除掉, 改为栈区数组
将runTimeArgus,preRunTimeArgus等参数处理函数中动态内存分区去除掉
将web_http中用于处理HTTP请求和回复的相关函数动态内存的使用去除
针对于syslog引发的内存错误, 可能原因是: perr 函数中存在openlog和closelog函数
频繁开启和关闭与syslogd之间的连接,造成不必要的开销
在调用 openlog 和 closelog 函数时,可能会存在竞争条件
因为这些函数使用了全局变量来存储日志的状态,多个线程同时调用可能导致日志状态出现混乱
改进: openlog移动到程序开始时调用, closelog移动到清理程序时调用
hash_table 全面启用同步锁, hash_table_client_t 和 hash_table_info_t
使用读写锁, 保护哈希表结构不被破坏
频繁调用SSL_new和SSL_free可能发生段错误,创建一个SSL连接池预先分配一定数量地SSL连接
使用ssl_pool_node_fetch + SSL_set_fd + ssl_pool_node_release方式完成ssl数据交互
实践调试后发现: SSL 套接字不能自由复用, SSL_set_fd 不能完全得知SSL套接字的全部状态
改进: 新建连接时使fetch, 销毁连接时release, 而不是每次IO执行这个操作
bug追加: fetch有可能得到NULL使得SSL_set_fd引发段错误
解决: 设置一个超时检查, 在一定限度下尝试重新获取SSL套接字
// TODO
努力优化代码一段时间后, 压力测试时已经不会发生段错误了
但是在这种压力下, epoll_wait由于未知原因不能再触发事件
即不能响应正常的请求了
21.processHttpClient/processRaspiClient仍然存在竞争关系
曾尝试为每一个客户端结构体设计互斥锁保护客户端连接资源不被破坏
但是这样仍然存在问题: 假设某客户端数据发送很频繁,epoll分配了两个(或以上)任务线程
率先调用readClient的线程可能取走了缓冲区内所有的数据,虽然后续线程在read返回0后直接退出了不会触发段错误之类的
然而取走了所有数据的第一个线程并不会处理所有数据,相反它只会处理一个单位的数据
因此造成了请求丢失的问题
解决: processRaspiClient比较简单, 每次限制它读取固定字节数, 防止多读数据
// TODO
processHttpClient需要设定一个循环每次读取保存一个字符, 直到出现HTTP报文结束标记
22.服务器在对客户端套接字调用SSL_shutdown时发生了SIGPIPE信号
推测: 为了让服务器主动产生EPOLLHUP事件, 手动调用了close
解决1: 忽略SIGPIPE信号
解决2: 在需要关闭客户端时复制性地再写一遍释放套接字地过程
23.HTTPS网页SSL握手无限跳SSL_accept failed (现象同19)
分析: SSL_accept失败后缓冲区应该仍然存在数据, 为了手动触发EPOLLHUP事件
我们调用recv+MSG_PEEK去尝试读取数据,再加上当时使用的是水平触发, 因此产生该错误
解决: 去除相关代码
24.httpEventPoll将客户端套接字设置为边沿触发+(启用/禁用Nagle算法)不能稳定地触发客户端套接字的可读事件
推测: 客户端浏览器数据发送事件间隔太短, 客户端发送了请求当没能触发事件(epoll等待缓冲区发送变化)
但是客户端一直等待响应,发生了死锁
解决: 更换为边沿触发,悲~~
25.ssl连接池释放(归还)连接后,未恢复SSL套接字的初始状态
SSL套接字生命周期:
状态1: 调用了SSL_new出生
状态2: 调用了SSL_set_fd绑定了Unix套接字
状态3: 调用了SSL_accept接受了客户端连接
状态4: 调用了SSL_read/SSL_write产生了I/O
状态5: 调用了SSL_shutdown关闭了客户端连接
状态6: 调用了SSL_free释放了套接字
调用SSL_clear后会恢复到状态1之前, SSL_CTX信息也会一同被清理
因此需要继续调用SSL_set_SSL_CTX函数
理论是这样, 但是我实际运行却不是这样
26.当服务器以非SSL模式运行收到浏览器HTTPS的请求时发生段错误
分析: SSL握手信息被当成HTTP报文进行解析引发错误
// TODO
27.树莓派上传的实时数据的时间戳由服务器打上,不能排除网络延迟造成时间不正确
分析: 树莓派应当自己产生时间戳,然后随硬件数据一并上传
//TODO
C
1
https://gitee.com/Einc/raspi.git
git@gitee.com:Einc/raspi.git
Einc
raspi
树莓派4b_数据采集与上传
master

搜索帮助