当前位置: 首页 > article >正文

【QT常用技术讲解】优化网络链接不上导致qt、qml界面卡顿的问题

前言

        qt、qml项目经常会涉及访问MySQL数据库、网络服务器,并且界面打开时的初始化过程就会涉及到链接Mysql、网络服务器获取数据,如果网络不通,卡个几十秒,会让用户觉得非常的不爽,本文从技术调研的角度讲解解决此类问题的socket编程方案。

调研场景一

目标:是否有成熟的第三方工具,可以通过后台命令行判断指定IP的某个端口是否在线。(其他文章分享有多线程启动第三方工具并获取结果的方法,不会导致界面卡顿)

经过调研,发现有两个工具初步满足需求:

nmap工具

格式:nmap -p [端口] [IP地址],如下所示

nmap -p 3306 192.168.1.123

如上效果图,内容较为详细,但不管端口是否在线,都花费5秒钟才能输出结果,比较影响界面的体验。

nc工具

格式:nc -zv [IP地址] [端口] -w 1            -w1是1秒钟超时

nc -zc 192.168.1.123 3306 -w1

如上效果图,加了超时参数-w1之后,确实是1秒中之内出结果,并且succeeded表示端口在线,但是当使用多线程获取此命令行返回的结果时,发现结果是空的,是在国产系统(统信UOS和麒麟kylin系统上进行的测试),然后在后台命令行进行重定向>1.txt,并且cat 1.txt时,发现输出结果也是空的,网上查资料说可能是按照的是特殊的版本,输出结果无法重定向(没有深究,因为可以调研socket编程来实现)。

socket设置超时

1、QT的QsqlDatabase

首先尝试了QsqlDatabase的超时设置方法setConnectOptions("CONNECT_TIMEOUT=1"),代码执行时提示无效。

2、原生socket的setsockopt

当无法链接上指定IP的端口时,3秒才返回,接收数据被阻塞了。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ctime>
#define qint64 std::time_t

qint64 getcurtime(){
    // 获取当前时间戳(秒级)
    std::time_t timestamp = std::time(nullptr);
    return timestamp;
}

bool checkPortOpen(const std::string& ip, int port, int timeout = 1) {
    qint64 bgtime=getcurtime();
    // 创建一个socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        std::cerr << "Socket creation failed!" << std::endl;
        return false;
    }

    // 设置超时参数
    struct timeval tv;
    tv.tv_sec = timeout; // 超时1秒
    tv.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));

    // 设置目标地址
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());

    // 尝试连接指定的IP和端口
    int result = connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if (result == -1) {
        qint64 endtime=getcurtime();
        std::cout << " checksqlonline use time:" << endtime-bgtime << std::endl;
        close(sock); // 连接失败,关闭socket
        return false;
    }
    qint64 endtime=getcurtime();
    std::cout << " checksqlonline use time:" << endtime-bgtime << std::endl;
    close(sock); // 连接成功,关闭socket
    return true;
}

int main(int argc, char **argv) {
    if (argc==1){
        printf("请传入参数:IP");
        return -1;
    }
    std::string ip = argv[1];  // 替换为你要检测的IP
    int port = 3306;                    // MySQL的默认端口3306

    if (checkPortOpen(ip, port)) {
        std::cout << "Port " << port << " on IP " << ip << " is open!" << std::endl;
    } else {
        std::cout << "Port " << port << " on IP " << ip << " is closed!" << std::endl;
    }

    return 0;
}

3、select异步socket

使用了异步模式,1秒以内返回,达到了优化的效果。

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <cstring>
#include <ctime>
#define qint64 std::time_t

qint64 getcurtime(){
    // 获取当前时间戳(秒级)
    std::time_t timestamp = std::time(nullptr);
    return timestamp;
}

bool checkPortOpen(const std::string& ip, int port, int timeout_sec = 1) {
    qint64 bgtime=getcurtime();
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Error creating socket." << std::endl;
        return false;
    }

    // Set socket to non-blocking mode
    fcntl(sockfd, F_SETFL, O_NONBLOCK);

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr);

    // Start connecting to the server (this will be non-blocking)
    int connect_result = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // Check if the connection was successful or pending
    if (connect_result < 0) {
        if (errno != EINPROGRESS) {
            std::cerr << "Connection error: " << strerror(errno) << std::endl;
            close(sockfd);
            return false;
        }
    }

    // Use select() to set a timeout for the connection
    fd_set writefds;
    FD_ZERO(&writefds);
    FD_SET(sockfd, &writefds);

    struct timeval timeout;
    timeout.tv_sec = timeout_sec;
    timeout.tv_usec = 0;

    int select_result = select(sockfd + 1, nullptr, &writefds, nullptr, &timeout);

    if (select_result > 0) {
        // Check if the socket is writable (connection succeeded)
        int so_error;
        socklen_t len = sizeof(so_error);
        getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
        if (so_error == 0) {
            // Port is open
            qint64 endtime=getcurtime();
            std::cout << " checksqlonline use time:" << endtime-bgtime << std::endl;
            close(sockfd);
            return true;
        }
    }
    qint64 endtime=getcurtime();
    std::cout << " checksqlonline use time:" << endtime-bgtime << std::endl;
    // If select result is <= 0, either timed out or failed to connect
    close(sockfd);
    return false;
}

int main(int argc, char **argv) {
    if (argc==1){
        printf("请传入参数:IP");
        return -1;
    }
    std::string ip = argv[1];  // 替换为你要检测的IP
    int port = 3306;                    // MySQL的默认端口3306

    if (checkPortOpen(ip, port)) {
        std::cout << "Port " << port << " on IP " << ip << " is open!" << std::endl;
    } else {
        std::cout << "Port " << port << " on IP " << ip << " is closed!" << std::endl;
    }

    return 0;
}


http://www.kler.cn/a/394263.html

相关文章:

  • Prometheus面试内容整理-Exporters
  • 分享 pdf 转 word 的免费平台
  • DIP switch是什么?
  • git配置远程仓库的认证信息
  • 【C语言】值传递和地址传递
  • uniCloud云对象调用第三方接口,根据IP获取用户归属地的免费API接口,亲测可用
  • Easyui ComboBox 数据加载完成之后过滤数据
  • AutoDL远程连接技巧
  • php preg_match 不到内容,修改pcre.backtrack_limit解决问题
  • elementui el-table中给表头 el-table-column 加一个鼠标移入提示说明
  • Android 关于使用videocompressor库压缩没有声音的问题
  • GOF设计模式中各模式支持的可变方面(封装变化)
  • 远程链接mysql步骤
  • UE5.4 PCG 生成藤蔓墙体
  • Hbase小测
  • 基于Java Springboot快递物流管理系统
  • Leetcode 56-合并区间
  • [JAVAEE] 面试题(五) - HashMap, Hashtable, ConcurrentHashMap
  • 如何确定光伏建设中的最佳安装倾角
  • day08|计算机网络重难点之 DNS查询过程、CDN是什么,有什么作用?、Cookie和Session是什么?有什么区别?
  • 【CSS】opacity 影响 z-index 不生效
  • 【Java语言】String类
  • Spring源码(十二):Spring MVC之Spring Boot
  • PPT技巧:如何合并PPT文件?
  • SpringBoot+MyBatis+MySQL的Point实现范围查找
  • MATLAB用CNN-LSTM神经网络的语音情感分类深度学习研究