Просмотр исходного кода

V0.2.2
1、修改了LHQLog和LHHTTPAPI的findCMake文件

Apple 9 месяцев назад
Родитель
Сommit
bbe1c5c6fa

+ 21 - 23
Libraries/LHHTTPAPI/FindLHHttpAPI.cmake

@@ -2,11 +2,12 @@
 #查找LHHttpAPI库
 message(STATUS "===== FindLHHTTPAPI.cmake =====")
 
+
 #查找头文件
 unset(INCLUDE_DIR CACHE)
 find_path(INCLUDE_DIR 
     NAMES lhhttpapi.h
-    PATHS ${CMAKE_CURRENT_LIST_DIR}/inc
+    PATHS ${CMAKE_CURRENT_LIST_DIR}/src
     NO_DEFAULT_PATH
 )
 
@@ -23,46 +24,43 @@ find_path(SOURCE_DIR
 list(APPEND LHHTTPAPI_SOURCE_DIRS ${SOURCE_DIR})
 
 #查找库文件,在这里区分平台,Debug和Release,32位和64位
-unset(LIBRARY_DIR CACHE)
+unset(LHHTTPAPI_LIBRARY CACHE)
 
 if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     #64位
     if(CMAKE_SIZEOF_VOID_P MATCHES 8)
         if(CMAKE_BUILD_TYPE MATCHES "Debug")
-            find_library(LIBRARY_DIR 
-                NAMES LHSqlWebInterfaced
-                PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64
-                NO_DEFAULT_PATH
-            )
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64/LHSqlWebInterfaced.dll)
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64/libcurl.dll)
         else()
-            find_library(LIBRARY_DIR 
-                NAMES LHSqlWebInterface
-                PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64
-                NO_DEFAULT_PATH
-            )
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64/LHSqlWebInterface.dll)
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64/libcurl.dll)
         endif()
     #32位
     elseif(CMAKE_SIZEOF_VOID_P MATCHES 4)
         if(CMAKE_BUILD_TYPE MATCHES "Debug")
-            find_library(LIBRARY_DIR 
-                NAMES LHSqlWebInterfaced
-                PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32
-                NO_DEFAULT_PATH
-            )
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32/LHSqlWebInterfaced.dll)
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32/libcurl.dll)
         else()
-            find_library(LIBRARY_DIR 
-                NAMES LHSqlWebInterface
-                PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32
-                NO_DEFAULT_PATH
-            )
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32/LHSqlWebInterface.dll)
+            list(APPEND LHHTTPAPI_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32/libcurl.dll)
         endif()
+    endif()
 endif()
 
-list(APPLEND LHHTTPAPI_LIBRARY ${LIBRARY_DIR})
 
 #定义变量
 if(LHHTTPAPI_INCLUDE_DIRS AND LHHTTPAPI_SOURCE_DIRS AND LHHTTPAPI_LIBRARY)
     set(LHHTTPAPI_FOUND TRUE)
+    set(LHHTTPAPI_VERSION 1.0.9)
+
 else()
     set(LHHTTPAPI_FOUND FALSE)
 endif()
+
+message(STATUS "LHHTTPAPI found: LHHTTPAPI")
+message(STATUS "LHHTTPAPI include: LHHTTPAPI_INCLUDE_DIRS")
+message(STATUS "LHHTTPAPI source: LHHTTPAPI_SOURCE_DIRS")
+message(STATUS "LHHTTPAPI library: LHHTTPAPI_LIBRARY")
+
+message(STATUS "---------------------------------------------")

+ 151 - 0
Libraries/LHHTTPAPI/src/lhhttpapi.h

@@ -0,0 +1,151 @@
+#pragma once
+#include <QString>
+#include <QObject>
+#include <QLibrary>
+#include <QDateTime>
+#include <QMutex>
+#include <QWaitCondition>
+// #include "Common/singleton.h"
+#include "threadcontroller.h"
+
+#define LH_HTTPAPI_SUCC 0
+
+// 用New
+#define LHAPI_BUFFER_SIZE_NEW 2048000
+#define LHAPI_BUFFER_SIZE_ARY 4096
+
+extern int g_nHttpTimeOut;
+
+// 操作类型【1查询 2更新 3删除 4插入 5存储过程】
+enum enDBOperatorType
+{
+    EDBOT_Select = 1,
+    EDBOT_Update,
+    EDBOT_Delete,
+    EDBOT_Insert,
+    EDBOT_Procedure,
+    EDBOT_Batch,    // 单动作批量操作,失败全部回滚
+    EDBOT_BatchTransAction // 多动作批量操作,失败回滚
+};
+
+class lhhttpapi : public QObject
+{
+    Q_OBJECT
+public:
+    typedef int (*FunDBInit)(const char* lpUrl);
+    typedef int (*FunDBLogin)(const char *pLocalIP, const char *pSerid, const char* CType, char *pszUserToken, int nSize);
+    typedef int (*FunDBGetServerList)(char *pszList, int nSize);
+    typedef int (*FunDBGetServerInfo)(const char* pSerid, const char* pszUserToken, char* pServerInfo, int nSize);
+    typedef int (*FunDBGetChannelList)(const char* pSerid,char *pszList, int nSize);
+    typedef int (*FunDBDoInterface)(const char* pSerid,const char* CType,const char* strtoken,int nOperatorType, const char * strParam, char *strRet, int nSize);
+    typedef int (*FunDoGetLastError)(char *pError, int nLen, int *nErrorCode);
+
+    typedef int (*FunSetHttpTimeOut)(int nTimeout);
+
+    // 获取网络文件大小,返回文件大小
+    typedef int64_t (*FunDoGetHttpFileSize)(const char *pszHttpAddr);
+
+    // 获取网络文件内容,返回内容大小
+    typedef int64_t (*FunDoGetHttpFileContent)(const char* pHttpAddr, char *pszContent, int nSize);
+
+    // 获取汉字拼音首字母,空格、数字等其它字符保持不动
+    typedef int (*FunDoWord2Pyjc)(const char* pszWord, char *pszContent, int nSize);
+
+    // 文件上传FTP
+    typedef int (*FunDoUploadFtpFile)(const char* pszLocalFilePath, const char* pszFtpFilePath);
+    typedef int (*FunDoUploadFtpFileContent)(const char* pszFileContent, int size, const char* pszFtpFilePath);
+
+    // FTP
+    typedef int (*FunDoCurlDeleteFtpFile)(const char* user, const char* pwd, const char* pszFtpFile);
+    typedef int (*FunDoCurlUploadFtpFile)(const char* user, const char* pwd, const char* pszLocalFilePath, const char* pszFtpFile);
+    typedef int (*FunDoCurlUploadFtpFileContent)(const char* user, const char* pwd, const char* pszFileContent, int nSize, const char* pszFtpFile);
+
+    // 获取数据库信息
+    typedef int (*FunDoGetDBInfo)(const char* strSerid, const char* strtoken, char* pDBInfo, int nsize);
+public:
+    explicit lhhttpapi(QObject *parent = nullptr);
+    ~lhhttpapi();
+
+public:
+    QLibrary *m_pQLib;
+
+    FunDBInit fnDBInit;
+    FunDBLogin fnDBLogin;
+    FunDBGetServerList fnDBGettServerList;
+    FunDBGetChannelList fnDBGetChannelList;
+    FunDBGetServerInfo fnDBGetServerInfo;
+
+    FunSetHttpTimeOut fnSetHttpTimeOut;
+    FunDoGetHttpFileSize fnGetHttpFileSize;
+    FunDoGetHttpFileContent fnGetHttpFileContent;
+    FunDoGetLastError fnGetLastError;
+    FunDoWord2Pyjc    fnDoWord2Pyjc;
+    FunDoUploadFtpFile fnDoUploadFtpFile;
+    FunDoUploadFtpFileContent fnDoUploadFtpFileContent;
+
+    FunDoCurlDeleteFtpFile  fnDoCurlDeleteFtpFile;
+    FunDoCurlUploadFtpFile  fnDoCurlUploadFtpFile;
+    FunDoCurlUploadFtpFileContent fnDoCurlUploadFtpFileContent;
+
+    FunDoGetDBInfo  fnDoGetDBInfo;
+
+    QMutex m_mutexWorkerDestroy;
+    QWaitCondition m_Condition;
+    FunDBDoInterface fnDBDoInterface;
+
+public:
+    bool Load(QString file);
+    bool UnLoad();
+
+    int SetHttpTimeOut(int nTimeout);
+    QString DoGetLastError(int *nErrorCode);
+    QString GetServerID() const {return m_serID;}
+    QString GetClientType() const {return m_clientType;}
+
+    int DBQInit(QString strUrl);
+    int DBQGetServerList(QString& serverList);
+    int DBQGetServerInfo(const QString& serid, QString& serverInfo);
+    /**
+     * @brief 登录数据库
+     * 
+     * @param LocalIP 
+     * @param Serid 
+     * @param CType 
+     * @param strUserToken 传出参数
+     * @return int 
+     */
+    int DBQLogin(QString LocalIP, QString Serid, QString CType, QString &strUserToken);
+    int DBQDoInterface(int nOperatorType, const QString &strParamXml, QString &strRetXml, bool wait = true);
+
+private slots:
+    void OnWorkerFinished();
+private:
+    QString m_token;
+    QMutex m_mtx;
+    QString m_serID;
+    QString m_clientType;
+    QTimer* m_tUpToken;
+};
+
+class DoInterfaceObject : public ThreadWorker
+{
+    Q_OBJECT
+public:
+    explicit DoInterfaceObject(lhhttpapi *pApi, const QString &strToken, int nOperatorType, const QString &strParamXml);
+    virtual ~DoInterfaceObject() override {}
+    QString GetRetXML() const {return m_strRetXml;}
+    int GetResult() const {return m_nResult;}
+public slots:
+    void DoInit() override {}
+    void DoWork() override;
+signals:
+    void sig_WorkFinished();
+public:
+    lhhttpapi *m_pApi;
+    QString m_strToken;
+    int m_nOperatorType;
+    QString m_strParamXml;
+    QString m_strRetXml;
+    int m_nResult;
+};
+

+ 28 - 6
Libraries/LHQLog/FindLHQLog.cmake

@@ -3,20 +3,35 @@
 
 message(STATUS "===== FindLHQLog.cmake =====")
 
+
 #查找头文件
+unset(INCLUDE_DIR CACHE)
+unset(LHQLog_INCLUDE_DIRS CACHE)
+
+find_path(INCLUDE_DIR 
+    NAMES LHQLogAPI.h
+    PATHS ${CMAKE_CURRENT_LIST_DIR}/src
+    NO_DEFAULT_PATH
+)
+list(APPEND LHQLog_INCLUDE_DIRS ${INCLUDE_DIR})
+
 unset(INCLUDE_DIR CACHE)
 find_path(INCLUDE_DIR 
-    NAMES LHQLog.h
+    NAMES LHLog_file_sink.h
     PATHS ${CMAKE_CURRENT_LIST_DIR}/inc
     NO_DEFAULT_PATH
 )
 
 list(APPEND LHQLog_INCLUDE_DIRS ${INCLUDE_DIR})
 
+# message(STATUS "LHQLog include: ${LHQLog_INCLUDE_DIRS}")
+
 #查找源文件
 unset(SOURCE_DIR CACHE)
+unset(LHQLog_SOURCE_DIRS CACHE)
+
 find_path(SOURCE_DIR 
-    NAMES LHQLog.cpp
+    NAMES LHQLogAPI.cpp
     PATHS ${CMAKE_CURRENT_LIST_DIR}/src
     NO_DEFAULT_PATH
 )
@@ -31,7 +46,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     if(CMAKE_SIZEOF_VOID_P MATCHES 8)
         if(CMAKE_BUILD_TYPE MATCHES "Debug")
             find_library(LIBRARY_DIR 
-                NAMES LHQLog
+                NAMES LHQLogd
                 PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_64/debug
                 NO_DEFAULT_PATH
             )
@@ -46,7 +61,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     elseif(CMAKE_SIZEOF_VOID_P MATCHES 4)
         if(CMAKE_BUILD_TYPE MATCHES "Debug")
             find_library(LIBRARY_DIR 
-                NAMES LHQLog
+                NAMES LHQLogd
                 PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/MinGW_32/debug
                 NO_DEFAULT_PATH
             )
@@ -59,7 +74,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
         endif()
     endif()
 #Linux
-endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
         find_library(LIBRARY_DIR 
             NAMES LHQLog
             PATHS ${CMAKE_CURRENT_LIST_DIR}/lib/uos_x64/release
@@ -72,6 +87,7 @@ list(APPEND LHQLog_LIBRARY ${LIBRARY_DIR})
 #设置变量
 if(LHQLog_INCLUDE_DIRS AND LHQLog_SOURCE_DIRS AND LHQLog_LIBRARY)
     set(LHQLog_FOUND TRUE)
+    set(LHQLog_VERSION 6.0.9.0)
     # CACHE命令用于定义一个名为LHQLog_INCLUDE_DIRS的缓存变量。这个变量用于存储LHQLog的包含目录路径。
     # 通过将其定义为缓存变量,用户可以在CMake配置过程中通过命令行或图形界面设置该变量的值。CACHE INTERNAL部分
     # 指定了缓存变量的类型。在这种情况下,INTERNAL表示该变量只能在CMake脚本内部使用,而不能被外部访问或修改。
@@ -82,5 +98,11 @@ if(LHQLog_INCLUDE_DIRS AND LHQLog_SOURCE_DIRS AND LHQLog_LIBRARY)
     set(LHQLog_LIBRARY ${LHQLog_LIBRARY} CACHE INTERNAL "LHQLog library")
 else()
     set(LHQLog_FOUND FALSE)
-
+    
 endif()
+
+message(STATUS "LHQLog include: LHQLog_INCLUDE_DIRS")
+message(STATUS "LHQLog source: LHQLog_SOURCE_DIRS")
+message(STATUS "LHQLog library: LHQLog_LIBRARY")
+
+message("----------------------------------------------")

+ 0 - 0
Libraries/LHQLog/inc/LHQLogAPI.h → Libraries/LHQLog/src/LHQLogAPI.h


+ 1 - 1
Libraries/LHQLog/src/LogInit.cpp

@@ -2,7 +2,7 @@
 
 #include "spdlog.h"
 #include "spdlog/sinks/stdout_color_sinks.h"
-#include "LHLog_file_sink.h"
+#include "../inc/LHLog_file_sink.h"
 
 
 #include <QString>

+ 0 - 0
Libraries/LHQLog/inc/LogInit.h → Libraries/LHQLog/src/LogInit.h


+ 190 - 0
common/Thread/threadcontroller.cpp

@@ -0,0 +1,190 @@
+#include "threadcontroller.h"
+#include <QDebug>
+
+#if defined(Q_OS_WIN32)
+#include <windows.h>
+#include <tlhelp32.h>
+#endif
+
+#include <QPointer>
+
+/* 获取线程数 */
+int ThreadUtility::GetThreadCount(int processID)
+{
+#if defined(Q_OS_WIN32)
+    PROCESSENTRY32 pe32;
+    pe32.dwSize = sizeof(pe32);
+  
+    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
+    if (hProcessSnap == INVALID_HANDLE_VALUE)
+    {
+      qDebug()<<"CreateToolhelp32Snapshot 调用失败";
+      return -1;
+    }
+    BOOL bMore = ::Process32First(hProcessSnap, &pe32);
+  
+    //printf("%-30s %-20s %-20s %-15s\n","szExeFile","th32ProcessID","th32ParentProcessID","cntThreads");
+    while(bMore)
+    {
+      //printf("%-30s ",pe32.szExeFile);
+      //printf("%-20d ",pe32.th32ProcessID);
+      //printf("%-20d",pe32.th32ParentProcessID);
+      ////显示进程的线程数
+      //printf("%-15d\n",pe32.cntThreads);
+      //int pid = getpid();
+      bMore = Process32Next(hProcessSnap, &pe32);
+      if(static_cast<int>(pe32.th32ProcessID) == processID)
+      {
+          //qDebug()<<"进程名称 = "<<QString::fromWCharArray(pe32.szExeFile);
+          //qDebug()<<"进程ID = "<<pe32.th32ProcessID;
+          //qDebug()<<"线程数量 = "<<pe32.cntThreads;
+          return static_cast<int>(pe32.cntThreads);
+      }
+    }
+    return -1;
+#elif defined(Q_OS_LINUX)
+    return -1;
+#endif
+}
+bool ThreadUtility::BlockExitThread(QThread *thread, unsigned long msecs)
+{
+    thread->quit();
+    thread->requestInterruption();
+    bool isSuccess = false;
+    if(!thread->wait(msecs))
+    {
+        qDebug()<<"线程异常结束"<<thread;
+        thread->terminate();
+        if(!thread->wait(20000)) qDebug()<<"线程terminate异常";
+    }
+    else
+    {
+        qDebug()<<"线程正常结束"<<thread;
+        isSuccess = true;
+    }
+    return isSuccess;
+}
+/* 工作线程 */
+ThreadWorker::ThreadWorker()
+    : QObject(nullptr)
+    , m_pWorkTimer(nullptr)
+    , m_bInited(false)
+    , m_nInterval(1000)
+{
+    m_pWorkTimer = new QTimer(nullptr);
+    connect(m_pWorkTimer, &QTimer::timeout, this, &ThreadWorker::OnWorkTimer, Qt::QueuedConnection);
+    connect(this, &ThreadWorker::sig_StartWorkTimer, this, &ThreadWorker::OnStartWorkTimer, Qt::QueuedConnection);
+    
+    QThread *pHostThread = new QThread(nullptr);
+    pHostThread->start();
+    moveToThread(pHostThread);                      /* 将本实例移动到新线程中,其实就是将事件循环等移过去 */
+    m_pWorkTimer->moveToThread(pHostThread);        /* 将定时器移到新线程中,定时器触发的槽函数就会运行在新线程中 */
+    /*----------------------------------------------------------------
+     * 对线程执行quit会发生: 结束线程的事件循环(线程里的对象不再能接受任何信号, 除了deleteLater), 发送finished信号
+     * 宿主线程对象和工作对象都在同一个线程, 所以用DirectConnection
+     * 这里是一个序列: 对线程执行quit -> 线程发送finished信号 -> 工作对象释放 -> 线程对象释放
+    ----------------------------------------------------------------*/
+    connect(pHostThread, &QThread::finished, this, &ThreadWorker::deleteLater, Qt::DirectConnection);
+    connect(this, &ThreadWorker::destroyed, pHostThread, &QThread::deleteLater, Qt::DirectConnection);
+    
+    WorkerCollector::Instance()->AddWorker(this);
+}
+
+ThreadWorker::~ThreadWorker()
+{
+    
+}
+//在子线程执行(m_pWorkTimer已被移至线程)
+void ThreadWorker::OnStartWorkTimer(int msecs)
+{
+    m_nInterval = (msecs==-1)?10:msecs;
+    if(msecs == -1) m_pWorkTimer->setSingleShot(true);
+    m_pWorkTimer->start(0);
+}
+
+//Start方法被主线程调用, 内容在主线程执行
+void ThreadWorker::Start(int interval)
+{
+    QMutexLocker lockerInited(&m_mutexInited);
+    m_bInited = false;
+    emit sig_StartWorkTimer(interval);
+}
+
+void ThreadWorker::NonBlockStop()
+{
+    QThread *pThread = dynamic_cast<QThread*>(thread());
+    if(pThread == nullptr) return;
+    pThread->quit();
+    pThread->requestInterruption();
+}
+//该方法只能被主线程调用
+//否则报错: Thread tried to wait on itself
+bool ThreadWorker::BlockStop(unsigned long msecs)
+{
+    return ThreadUtility::BlockExitThread(thread(), msecs);;
+}
+
+void ThreadWorker::SetInterval(int interval)
+{
+    m_pWorkTimer->setInterval(interval);
+}
+
+//OnWorkTimer在其他线程执行
+void ThreadWorker::OnWorkTimer()
+{
+    m_pWorkTimer->setInterval(m_nInterval);
+    /* 初始化锁 */
+    m_mutexInited.lock();
+    if(!m_bInited)
+    {
+        m_bInited = true;
+        DoInit();
+    }
+    m_mutexInited.unlock();
+
+    DoWork();
+    emit sig_Ticked(this);
+    //工作对象非循环且内容执行完毕
+    if(m_pWorkTimer->isSingleShot())
+    {
+        NonBlockStop();
+        return;
+    }
+    //主动调用NonBlockStop或BlockStop且未等待超时
+    if(thread()->isInterruptionRequested())
+    {
+        m_pWorkTimer->stop();
+        return;
+    }
+}
+
+WorkerCollector::CollectorGarbo WorkerCollector::garbo;
+WorkerCollector* WorkerCollector::sm_pInstance = new WorkerCollector();     //初始化静态单例(饿汉)
+
+//在这里释放那些: 直到程序结束还没有被释放的工作对象
+WorkerCollector::~WorkerCollector()
+{
+    sm_pInstance = nullptr;
+    auto pred = [](QPointer<ThreadWorker> x){return x != nullptr;};
+    QList<QPointer<ThreadWorker>> remainWorkers;
+    std::copy_if(m_listWorkers.begin(), m_listWorkers.end(), std::back_inserter(remainWorkers), pred);
+    qDebug()<<"线程管理器释放: 待释放工作对象个数"<<remainWorkers.count();
+    int counter = 0;
+    for(QPointer<ThreadWorker> pWorker: remainWorkers)
+    {
+        //注意: 这里如果是停止超时的情况, 说明工作对象正在执行内容, 直接调用delete可能会导致崩溃
+        //而且这里不能使用deleteLater来释放工作对象, 因为线程的事件循环已经退出了
+        if(!pWorker->BlockStop(5000)) continue;
+        delete pWorker;
+        counter++;
+    }
+    qDebug()<<"已释放工作对象: "<<counter;
+    m_listWorkers.clear();
+}
+
+void WorkerCollector::AddWorker(ThreadWorker *worker)
+{
+    QPointer<ThreadWorker> pWorker(worker);
+    m_listWorkers.append(pWorker);
+}
+

+ 112 - 0
common/Thread/threadcontroller.h

@@ -0,0 +1,112 @@
+/*----------------------------------------------------------------
+ *      概述
+ * 1, 使用movetothread的方式执行工作线程(因为这种方式是官方推荐)
+ * 2, 这种方式可以同时实现线程主动运行并且可以接收到Qt的事件循环
+ * ----------------------------------------------------------------
+ *      使用流程
+ * 1, 提供一个ThreadWorker类作为工作对象的基类, 工作对象继承它之后, 实现DoWork纯虚函数, 在函数里编写工作内容
+ * 2, 提供一个ThreadController类作为线程管理器的基类, 管理器继承它之后, 可以使用AddWorker方法传入工作对象, 管理器释放后会自动释放未停止的工作对象
+ * 3, 工作对象调用Start并传入循环间隔来开启线程
+ * 4, 工作对象调用BlockStop/NonBlockStop来阻塞/非阻塞地停止
+ * ----------------------------------------------------------------
+ *      特性
+ * 1, 代码分离, 使用者只要考虑实现工作内容, 工作对象的线程相关操作全部在基类实现
+ * 2, 阻塞/非阻塞两种方式停止线程, 停止时, 先释放工作对象再释放宿主线程
+ * 3, 当工作内容是无限阻塞, 可以使用阻塞方式并设置阻塞超时来停止线程, 但是工作对象不会被释放
+ * 4, 工作对象支持事件循环, 例如在woker实现类里使用tcp对象并监听readyRead信号
+ * 5, 非循环工作的工作对象执行完毕后, 自动释放, 宿主线程也会退出
+ * 6, 程序退出时, 阻塞式(可设置超时)释放所有未被释放的工作对象和宿主线程(不包括异常停止的工作对象)
+ * 7, 工作对象支持循环执行和单次执行
+ * 8, 这两种停止情况都能正常工作: dowork正在执行时; dowork的timer间隔期间
+ * 9, 支持外部监听工作对象被释放事件
+----------------------------------------------------------------*/
+#ifndef THREADUTILITY_H
+#define THREADUTILITY_H
+
+#include <QMutex>
+#include <QObject>
+#include <QPointer>
+#include <QThread>
+#include <QTimer>
+#include <QWaitCondition>
+
+/**
+ * @brief 这个函数是获取一些线程的状态
+ * 
+ */
+class ThreadUtility
+{
+public:
+    static int GetThreadCount(int processID);           /* 获取线程数 */
+    static bool BlockExitThread(QThread *thread, unsigned long msecs = ULONG_MAX);
+};
+
+/**
+ * 工作线程
+*/
+class ThreadWorker : public QObject
+{
+    Q_OBJECT
+public:
+    explicit ThreadWorker();
+    virtual ~ThreadWorker();
+    virtual void Start(int interval = -1);
+    virtual void NonBlockStop();
+    virtual bool BlockStop(unsigned long msecs = ULONG_MAX);
+    void SetInterval(int interval);
+protected:
+    virtual void DoInit() = 0;
+    virtual void DoWork() = 0;              /* 重写此函数,即可运行在新的线程中 */
+private slots:
+    void OnWorkTimer();                     /* OnWorkTimer在其新线程执行 */
+    void OnStartWorkTimer(int msecs);
+signals:
+    void sig_Ticked(ThreadWorker *self);
+    void sig_StartWorkTimer(int msecs);
+private:
+    QTimer *m_pWorkTimer;
+    bool m_bInited;
+    QMutex m_mutexInited;
+    int m_nInterval;
+};
+
+/**
+ * 控制线程
+*/
+class WorkerCollector : public QObject
+{
+    Q_OBJECT
+public:
+    static WorkerCollector* Instance(){return sm_pInstance;}
+    ~WorkerCollector();
+    void AddWorker(ThreadWorker *worker);
+private:
+    WorkerCollector(){}
+    //防拷贝, C++11
+    WorkerCollector(WorkerCollector const&) = delete;
+	WorkerCollector& operator=(WorkerCollector const&) = delete;
+private:
+    static WorkerCollector* sm_pInstance;     //单例实例
+    QList<QPointer<ThreadWorker>> m_listWorkers;    //包含所有的工作对象
+    
+    /*----------------------------------------------------------------
+     * 关于释放全局单例: 
+     * 一个妥善的方法是让这个类自己知道在合适的时候把自己删除; 或者说把删除自己的操作挂在系统中的某个合适的点上, 使其在恰当的时候自动被执行;
+     * 程序在结束的时候, 系统会自动析构所有的全局变量;事实上, 系统也会析构所有的类的静态成员变量, 就像这些静态成员也是全局变量一样;
+     * 利用这个特征, 我们可以在单例类中定义一个这样的静态成员变量, 而它的唯一工作就是在析构函数中删除单例类的实例;
+    ----------------------------------------------------------------*/
+private:
+    class CollectorGarbo
+    {
+    public:
+        ~CollectorGarbo()
+        {  
+            if (WorkerCollector::sm_pInstance != nullptr)
+                delete WorkerCollector::sm_pInstance;
+        }
+    };
+    static CollectorGarbo garbo;
+    /*----------------------------------------------------------------*/
+};
+
+#endif // THREADUTILITY_H

+ 85 - 11
common/http/QtHttps.cpp

@@ -3,6 +3,7 @@
 #include "lightlog.h"
 
 #include <QEventLoop>
+#include "spdlog/spdlog.h"
 
 QtHttps::QtHttps(QObject *parent) :
     QObject(parent)
@@ -11,13 +12,40 @@ QtHttps::QtHttps(QObject *parent) :
     m_manager = new QNetworkAccessManager(this);
 
 
-    /* 链接信号 */
-    // connect(m_manager, &QNetworkAccessManager::finished, this, &QtHttps::do_replyFinished);
+    /* 检查是否支持OpenSSL */
+    bool isSupport = QSslSocket::supportsSsl();
+    QString opensslVersion = QSslSocket::sslLibraryVersionString();
+    QString openSSLBuildVersion = QSslSocket::sslLibraryBuildVersionString();
+    LOG_INFO("OpenSSL is support : " + QString::number(isSupport));
+    LOG_INFO("OpenSSL version : " + opensslVersion);
+    LOG_INFO("OpenSSL build version : " + openSSLBuildVersion);
+    /* 查看支持的协议 */
+    auto list = m_manager->supportedSchemes();
+    for(auto &item : list)
+    {
+        LOG_INFO("supportedSchemes : " + item);
+    }
+    
+
 
 }
 QtHttps::~QtHttps()
 {
     delete m_manager;
+    if(m_file != nullptr)
+    {
+        if(m_file->isOpen())
+        {
+            m_file->close();
+        }
+        delete m_file;
+        m_file = nullptr;
+    }
+    if(m_reply != nullptr)
+    {
+        m_reply->deleteLater();
+        m_reply = nullptr;
+    }
 }
 
 bool QtHttps::Get(const QString &url)
@@ -33,6 +61,13 @@ bool QtHttps::Get(const QString &url)
     
     m_reply = m_manager->get(request);
     connect(m_reply, &QNetworkReply::finished, this, &QtHttps::do_replyFinished);
+    connect(m_reply, &QNetworkReply::readyRead, this, &QtHttps::do_replyReadyRead);
+    connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::do_downloadProgress);
+
+    /* 转发信号 */
+    connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::signal_downloadProgress);
+    connect(m_reply, &QNetworkReply::finished, this, &QtHttps::signal_replyFinished);
+
     return true;
 }
 
@@ -45,6 +80,12 @@ bool QtHttps::GetHead(const QString &url)
         return false;
     }
     QNetworkRequest request;
+
+    // QSslConfiguration conf = request.sslConfiguration();
+    // conf.setPeerVerifyMode(QSslSocket::VerifyNone);
+    // conf.setProtocol(QSsl::TlsV1SslV3);
+    // request.setSslConfiguration(conf);
+
     request.setUrl(url);
 
     m_reply = m_manager->head(request);
@@ -52,6 +93,10 @@ bool QtHttps::GetHead(const QString &url)
     connect(m_reply, &QNetworkReply::readyRead, this, &QtHttps::do_replyReadyRead);
     // connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::do_downloadProgress);
 
+    /* 转发信号 */
+    connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::signal_downloadProgress);
+    connect(m_reply, &QNetworkReply::finished, this, &QtHttps::signal_replyFinished);
+
     return true;
 }
 
@@ -84,10 +129,14 @@ void QtHttps::downloadFile(const QString &url, const QString &filePath)
     QNetworkRequest request;
     request.setUrl(QUrl(url));
     m_reply = m_manager->get(request);
-    /* 链接信号 */
+    /* 链接信号和槽 */
     connect(m_reply, &QNetworkReply::finished, this, &QtHttps::do_replyFinished);
     connect(m_reply, &QNetworkReply::readyRead, this, &QtHttps::do_replyReadyRead);
     connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::do_downloadProgress);
+
+    /* 转发信号 */
+    connect(m_reply, &QNetworkReply::downloadProgress, this, &QtHttps::signal_downloadProgress);
+    connect(m_reply, &QNetworkReply::finished, this, &QtHttps::signal_replyFinished);
 }
 
 
@@ -95,22 +144,41 @@ void QtHttps::downloadFile(const QString &url, const QString &filePath)
 void QtHttps::do_replyFinished()
 {
     LOG_DEBUG("do_replyFinished");
-
+    /* 获取状态码 */
+    auto retCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
     if(m_reply->error() == QNetworkReply::NoError)
     {
-        LOG_DEBUG("reply success");
+        LOG_DEBUG("reply : " + QString::number(retCode));
         auto list = m_reply->rawHeaderList();
         for(auto &item : list)
         {
-            LOG_DEBUG(item + " : " + m_reply->rawHeader(item));
+            SPDLOG_INFO("{} : {}", item.toStdString(), m_reply->rawHeader(item).toStdString());
+            // LOG_DEBUG(item + " : " + m_reply->rawHeader(item));
+        }
+        /* 检查文件是否打开 */
+        if(m_file != nullptr)
+        {
+            if(m_file->isOpen())
+            {
+                m_file->close();
+            }
+            delete m_file;
+            m_file = nullptr;
         }
-
-        auto ret = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-        LOG_DEBUG("Status Code : " + QString::number(ret));
     }
     else
     {
-        LOG_WARN("reply error : " + m_reply->errorString());
+        LOG_WARN("reply error : " + QString::number(retCode) + " " + m_reply->errorString());
+    }
+    /* 检查文件是否打开 */
+    if(m_file != nullptr)
+    {
+        if(m_file->isOpen())
+        {
+            m_file->close();
+        }
+        delete m_file;
+        m_file = nullptr;
     }
     /* 记得设置这个,需要手动释放 */
     m_reply->deleteLater();
@@ -120,7 +188,7 @@ void QtHttps::do_replyFinished()
 /* 读取数据 */
 void QtHttps::do_replyReadyRead()
 {
-    LOG_DEBUG("do_replyReadyRead");
+    // LOG_DEBUG("do_replyReadyRead");
     if(m_file != nullptr)
     {
         m_file->write(m_reply->readAll());
@@ -134,3 +202,9 @@ void QtHttps::do_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
     LOG_DEBUG("bytesReceived : " + QString::number(bytesReceived));
     LOG_DEBUG("bytesTotal : " + QString::number(bytesTotal));
 }
+
+
+void QtHttps::do_error(QNetworkReply::NetworkError code)
+{
+
+}

+ 5 - 0
common/http/QtHttps.h

@@ -20,10 +20,15 @@ public:
 
     void downloadFile(const QString &url, const QString &filePath);
 
+signals:
+    void signal_replyFinished();
+    void signal_downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+
 private slots:
     void do_replyFinished();
     void do_replyReadyRead();
     void do_downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+    void do_error(QNetworkReply::NetworkError code);
 
 private:
     QNetworkAccessManager *m_manager = nullptr;

+ 5 - 2
demo/http/widget.cpp

@@ -21,6 +21,7 @@ Widget::Widget(QWidget *parent)
 
     SPDLOG_INFO("***** Qt Library *****");
 
+    ui->lineEdit->setText("http://www.baidu.com");
 }
 
 Widget::~Widget()
@@ -33,12 +34,14 @@ Widget::~Widget()
 void Widget::on_pBtn_connectHTTP_clicked()
 {
     SPDLOG_INFO("点击了“连接按钮”");
-    m_qtHttps->GetHead("http://192.168.50.100:3000/apple/Embedded_A33.git");
+    // m_qtHttps->GetHead("http://192.168.50.100:3000/apple/Embedded_A33.git");
+    m_qtHttps->GetHead(ui->lineEdit->text());
 }
 
 void Widget::on_pBtn_downloadFile_clicked()
 {
     SPDLOG_INFO("点击了“下载文件”");
-    m_qtHttps->downloadFile("http://192.168.50.100:3000/apple/Embedded_A33.git", "Embedded_A33.zip");
+    m_qtHttps->downloadFile(ui->lineEdit->text(), "Embedded_A33.zip");
+    
 }
 

+ 11 - 8
demo/http/widget.ui

@@ -18,25 +18,28 @@
     <rect>
      <x>30</x>
      <y>30</y>
-     <width>381</width>
-     <height>121</height>
+     <width>681</width>
+     <height>311</height>
     </rect>
    </property>
    <layout class="QGridLayout" name="gridLayout">
-    <item row="0" column="0">
-     <widget class="QPushButton" name="pBtn_connectHTTP">
+    <item row="0" column="1">
+     <widget class="QPushButton" name="pBtn_downloadFile">
       <property name="text">
-       <string>连接HTTP</string>
+       <string>下载文件</string>
       </property>
      </widget>
     </item>
-    <item row="0" column="1">
-     <widget class="QPushButton" name="pBtn_downloadFile">
+    <item row="0" column="0">
+     <widget class="QPushButton" name="pBtn_connectHTTP">
       <property name="text">
-       <string>下载文件</string>
+       <string>连接HTTP</string>
       </property>
      </widget>
     </item>
+    <item row="1" column="0" colspan="2">
+     <widget class="QLineEdit" name="lineEdit"/>
+    </item>
    </layout>
   </widget>
  </widget>