头像

小小蒟蒻




离线:21小时前


最近来访(166)
用户头像
CodeBoy9527
用户头像
uhgariej
用户头像
寄一心明月
用户头像
WillFan
用户头像
xtxxueyan
用户头像
null_12
用户头像
用户头像
mCal
用户头像
周赛只能A一题
用户头像
.CHY
用户头像
aMAZEzine
用户头像
acwing_8520
用户头像
木棉觉
用户头像
limie
用户头像
dok
用户头像
血梅香
用户头像
青春猪头少年不会梦到AC
用户头像
小鲸鱼
用户头像
奔马
用户头像
王同学.


效果图
1.png
2.png

#include <iostream>
#include <memory>
using namespace std;

template<typename T>
struct Node
{
    T data;
    std::shared_ptr<Node> next = nullptr;

    explicit Node() = delete;  // 不能使用默认构造函数
    explicit Node(T arg, std::shared_ptr<Node> pNext = nullptr)
        : data(arg), next(std::move(pNext))
    {
        cout << "node ctor: " << data << endl;
    }

    virtual ~Node()
    {
        cout << "node dtor: " << data << endl;
    }

    int userCount() const { return next.use_count(); }
};

template<typename T>
class List
{
private:
    std::shared_ptr<Node<T>> head = nullptr;
    std::shared_ptr<Node<T>> tail = nullptr;
    int count = 0;

public:
    virtual ~List()
    {
        count = 0;
    }

    void push_front(const T& data)
    {
        auto ptr = std::shared_ptr<Node<T>>(new Node<T>{ data });
        if (!count)                 // 节点数为0
        {
            head = std::move(ptr);  // 头指针接管ptr指向的节点
            tail = head;            // 尾指针与头指针同时指向唯一的节点
        }
        else                        // 节点个数大于一时
        {
            ptr->next = head;       // 将ptr指向的节点插入到头指针指向的节点的前面 
            head = std::move(ptr);  // 头指针接管最前面的节点
        }
        count++;
    }

    void push_back(const T& data)
    {
        auto ptr = std::shared_ptr<Node<T>>(new Node<T>{ data });
        if (!count)                  // 节点数为0
        {
            tail = std::move(ptr);   // 尾指针接管ptr指向的节点  
            head = tail;             // 头指针与尾指针同时指向唯一的节点
        }
        else                         // 节点个数大于一时  
        {
            tail->next = std::move(ptr);  // 最后一个节点的next域接管ptr指向的节点
            tail = tail->next;            // 尾指针指向最后面的节点
        }
        count++;
    }

    void remove(const T& data)
    {
        for (auto i = head; i != nullptr; )
        {
            if (head == nullptr)   // 空链表
            {
                tail = head;
                count = 0;
                return;
            }

            if (head->data == data)  // 删除头节点
            {
                head = head->next;   // 头节点指向下一个节点
                i = head;
                if (count == 1)      // 节点数为1
                    tail = head;     // 首尾节点都指向nullptr

                count--;
                continue;
            }

            if (i->next && i->next->data == data) // 删除不是头节点的节点(要删除的节点有连续相同,删除时i不会向后移动)
            {
                if (i->next->next)                 // 要被删除的节点不是最后一个节点
                {
                    i->next = i->next->next;       // 将要删除的节点从链表中孤立出来
                    count--;
                }
                else                               // 要被删除的节点是最后一个节点
                {
                    tail = i;                      // 让尾指针指向它前面的节点
                    i->next = nullptr;             // 将要删除的节点从链表中孤立出来           
                    count--;
                }
            }
            else i = i->next;
        }
    }
    // 依靠List自身的析构函数来析构会崩溃(大量节点时,比如3000个节点在析构时一定会崩溃)
    // 可能原因是析构函数执行完成了,但所有智能指针还没来得及释放完成
    // 所以,提供清理函数。将所有的智能指针明确指定释放完成再析构就不会崩溃
    void clear()
    {
        for (; count; )
        {
            head = head->next;   // 头节点指向下一个节点
            if (count == 1)      // 节点数为1
                tail = head = nullptr;     // 首尾节点都指向nullptr

            count--;
        }
    }

    void Travel()
    {
        cout << "all data in list are ";
        for (auto i = head; i != nullptr; i = i->next)
        {
            cout << i->data << " ";
        }
        cout << endl;
    }

    void ShowHeadData()
    {
        if (!count)
            return;

        cout << "Head data: " << head->data << endl;
    }

    void ShowTailData()
    {
        if (!count)
            return;

        cout << "Tail data: " << tail->data << endl;
    }

    void Count()
    {
        if (count < 0)
            return;

        cout << "total data count: " << count << endl;
    }
};

void TestPushFront()
{
    List<int> list;

    cout << "insert elements from head" << endl;
    list.push_front(1);
    list.push_front(1);
    list.push_front(3);
    list.push_front(4);
    list.push_front(1);

    cout << "after insert then travel all elements" << endl;
    list.Travel();
    list.Count();
    list.ShowHeadData();
    list.ShowTailData();

    cout << "remove elements" << endl;
    list.remove(1);
    cout << "after remove then travel all elements" << endl;
    list.Travel();
    list.Count();
    list.ShowHeadData();
    list.ShowTailData();
    list.clear();
}

void TestPushBack()
{
    List<int> list;

    cout << "insert elements from tail" << endl;
    list.push_back(1);
    list.push_back(3);
    list.push_back(4);
    list.push_back(1);
    list.push_back(1);

    cout << "after insert then travel all elements" << endl;
    list.Travel();
    list.Count();
    list.ShowHeadData();
    list.ShowTailData();

    cout << "remove elements" << endl;
    list.remove(1);
    cout << "after remove then travel all elements" << endl;
    list.Travel();
    list.Count();
    list.ShowHeadData();
    list.ShowTailData();
    list.clear();
}

int main()
{
    TestPushFront();
    cout << endl;
    TestPushBack();

    return 0;
}



#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Person {
public:
    int  m_Age;
    string m_Name;
public:
    explicit Person() {
        m_Age = 0;
        m_Name = "";
    };
    explicit Person(int age, const string& name) {
        m_Age = age;
        m_Name = name;
    };
    ~Person() {

    };
    void show() {
        cout << " age \t" << m_Age << "  name \t" << m_Name << endl;
    }
    static void showPerson(Person p) {
        p.show();
    };
    static void showPersonPtr(Person* p) {
        p->show();
    };
};

void Test1()
{
    vector<Person*> vec;
    vec.emplace_back(new Person(21, "Jim"));
    vec.emplace_back(new Person(10, "Tom"));
    vec.emplace_back(new Person(34, "kit"));
    cout << "指针类型循环输出" << endl;
    for_each(vec.begin(), vec.end(), Person::showPersonPtr);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        if (*i) delete (*i);
    vec.clear();
}

void Test2()
{
    vector<Person> vec;
    vec.emplace_back(std::move(Person(21, "Jim")));
    vec.emplace_back(std::move(Person(10, "Tom")));
    vec.emplace_back(std::move(Person(34, "kit")));
    cout << "普通类型循环输出" << endl;
    for_each(vec.begin(), vec.end(), Person::showPerson);
    vec.clear();
}

int main()
{
    Test1();
    Test2();

    return 0;
}


分享 bind的使用

// bind的使用
/*
* std::placeholders::_1 是占位符,标定这个是要传入的参数。
* 所以bind()不仅可以用于二元函数,还可以用于多元函数,
* 可以绑定多元函数中的多个参数,不想绑定的参数使用占位符表示。
*/
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
using std::cout;
using std::endl;
using std::placeholders::_1;

void find_lesser(const vector<int>& vec, int nNumber)
{
    cout << "less than " << nNumber << " are ";
    auto iter = vec.begin();
    for (size_t i = 0; i < vec.size(); ++i)
    {
        iter = find_if(iter, vec.end(), bind(less<int>(), _1, nNumber));
        if (iter != vec.end())
        {
            cout << *iter << " ";
            ++iter;
        }
    }
    cout << endl;
}

void find_not_lesser(const vector<int>& vec, int nNumber)
{
    cout << "not less than " << nNumber << " are ";

    auto iter = vec.begin();
    for (size_t i = 0; i < vec.size(); ++i)
    {
        iter = find_if(iter, vec.end(), bind(less<int>(), nNumber, _1));
        if (iter != vec.end())
        {
            cout << *iter << " ";
            ++iter;
        }
    }
    cout << endl;
}

void find_less_count(const vector<int>& vec, int nNumber)
{
    int res = count_if(vec.begin(), vec.end(), bind(less<int>(), _1, nNumber));  // element < 40 (_1占位代表vec的迭代器)
    cout << "There are " << res << " elements that are less than 40.\n";
}

void find_not_less_count(const vector<int>& vec, int nNumber)
{
    int res = count_if(vec.begin(), vec.end(), bind(less<int>(), nNumber, _1));  // 40 < element  (_1占位代表vec的迭代器)
    cout << "There are " << res << " elements that are not less than 40.\n";
}

int main()
{
    int numbers[]{ 40, 20, 10, 40, 50, 10 };
    vector<int> vec(numbers, numbers + 6);

    for (size_t i = 0; i < vec.size(); ++i)
        cout << vec[i] << " ";
    cout << endl;

    find_less_count(vec, 40);
    find_not_less_count(vec, 40);

    find_lesser(vec, 40);
    find_not_lesser(vec, 40);


    system("pause");
    return 0;
}



template<typename Ret, typename...Args>
struct OpenFrame{

    explicit OpenFrame(const std::function<Ret(Args...)>& f):m_f(f)
    {    }

    Ret start(Args...args){
        return m_f(args...);
    }

    std::function<Ret(Args...)> m_f;
};

double print(int i, double d){
    cout<<"print(int i, double d): "<<i<<","<<d<<endl;
    return i+d;
}

int main(){

    std::function func=print;
    OpenFrame frame{func};
    cout<<frame.start(100,3.14)<<endl;
}



// Global.h
#pragma once

#include <stdio.h>
#include <tchar.h>
#include <iostream>

using namespace std;

class CCoreByIOCP;
class LanThreadPool;

#include "common.h"
#include "Mapper.h"
#include "CoreByIOCP.h"
#include "LanWorkerThread.h"
// common.h
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <WinSock2.h>
#include<memory.h>
#include<windows.h>
#include <MSWSock.h>
#include <MSTcpIP.h>//用于心跳包的头文件
#include <list>
#pragma comment(lib, "ws2_32.lib")

#define PORT 10240
#define SRV_IP "127.0.0.1"

#define MAX_BUFFER_LEN                  1024        // 缓冲区的最大长度
#define WORKER_THREADS_PER_PROCESSOR    2
#define MAX_POST_ACCEPT                 10          // 同时投递AcceptEx的请求数量
#define EXIT_CODE                       NULL            

// 释放指针宏
#define RELEASE_MEMORY(x)               { if(x != NULL) { delete x; x = NULL;} }
// 释放句柄宏
#define RELEASE_HANDLE(x)               { if(x != NULL && x != INVALID_HANDLE_VALUE) { CloseHandle(x); x = NULL; } }
// 释放Socket宏
#define RELEASE_SOCKET(x)               { if(x !=INVALID_SOCKET) { closesocket(x); x = INVALID_SOCKET; } }

////////////////////////////////////////////////////////////////////
#define NC_CLIENT_CONNECT       0x0001  //客户端连接
#define NC_CLIENT_DISCONNECT    0x0002  //客户端断开
#define NC_TRANSMIT             0x0003  //发送数据
#define NC_RECEIVE              0x0004  //接收数据
#define NC_RECEIVE_COMPLETE     0x0005  //完整接收

////////////////////////////////////////////////////////////////////
//在完成端口上投递的I/O操作的类型
typedef enum EM_IOType
{
    EM_IOAccept,                         // 标志投递的 Accept初始化操作
    EM_IOSend,                           // 标志投递的是 发送操作(写)
    EM_IORecv,                           // 标志投递的是 接收操作(读)
    EM_IOIdle                            // 用于初始化,无意义
}IOType;


//====================================================================================
//              单IO数据结构体定义(用于每一个重叠操作的参数)
//====================================================================================

//结构体里可以定义一个指针,设置大小,用来传参!!!!
struct PER_IO_CONTEXT
{
    OVERLAPPED      m_overLapped;                               //用于IOCP的核心数据结构!!!!!!
    IOType          m_IOType;                                   //接收到的IO需要处理的类型
    WSABUF          m_wsaBuf;                                   // WSA类型的缓冲区,用于给重叠操作传参数的
    SOCKET          m_rourceSock;                               // 接收到的连接的套接字 
    SOCKADDR_IN     m_resourceAddr;                             // 发送源套接字地址信息
    char            m_szBuf[MAX_BUFFER_LEN];                    //这个是WSABUF里具体存字符的缓冲区,GetQueuedCompletionStatus函数调用一次的缓存
    char            m_szBufCache[MAX_BUFFER_LEN];               //用于缓存接收一个完整包
    DWORD           m_dwBytesSend;                              // 发送的字节数
    DWORD           m_dwBytesRecv;                              // 接收的字节数

    UINT32          m_desID;                                    // 接收者的ID号(全网唯一的)

    void Init()
    {
        ZeroMemory(&m_overLapped, sizeof(OVERLAPPED));
        ZeroMemory(m_szBuf, MAX_BUFFER_LEN);
        ZeroMemory(m_szBufCache, MAX_BUFFER_LEN);
        ZeroMemory(&m_resourceAddr, sizeof(SOCKADDR_IN));
        ZeroMemory(&m_resourceAddr.sin_zero, 8);
        m_IOType = EM_IOIdle;
        m_rourceSock = INVALID_SOCKET;
        m_wsaBuf.buf = m_szBuf;
        m_wsaBuf.len = MAX_BUFFER_LEN;
        m_dwBytesSend = 0;
        m_dwBytesRecv = 0;
        m_desID = 0;
    }

    void Reset()
    {
        ZeroMemory(&m_overLapped, sizeof(OVERLAPPED));
        ZeroMemory(m_szBuf, MAX_BUFFER_LEN);
        ZeroMemory(m_szBufCache, MAX_BUFFER_LEN);
        ZeroMemory(&m_resourceAddr, sizeof(SOCKADDR_IN));
        ZeroMemory(&m_resourceAddr.sin_zero, 8);
        m_IOType = EM_IOIdle;
        m_rourceSock = INVALID_SOCKET;
        m_wsaBuf.buf = m_szBuf;
        m_wsaBuf.len = MAX_BUFFER_LEN;
        m_dwBytesSend = 0;
        m_dwBytesRecv = 0;
        m_desID = 0;
    }
};

const int nMsgSize = sizeof(char) * sizeof(PER_IO_CONTEXT);

// 完成端口传递的参数
struct IOCP_PARAM                    
{
    SOCKET m_rourceSock;
};


/*
    描述: 客户端发送消息头部解析结构体
    nSelfID:    自己的ID号
    nSendToID:  接收者的ID号
    buf:        发送的信息
*/
const int nMessageBufMaxSize = MAX_BUFFER_LEN - 2 * sizeof(UINT32); //发送消息的文字信息最大的字节数
typedef struct ST_SendToIpInfo
{
    UINT32 nSelfID;     //4字节
    UINT32 nSendToID;   //4字节
    char buf[nMessageBufMaxSize];
}SendToIpInfo;
// mapper.h
#pragma once
#ifndef __IO_MAPPER__
#define __IO_MAPPER__

#define net_msg

/*
__declspec(novtable) 在C++中接口中广泛应用. 不容易看到它是因为在很多地方它都被定义成
为了宏. 比如说ATL活动模板库中的ATL_NO_VTABLE, 其实就是__declspec(novtable).
__declspec(novtable) 就是让类不要有虚函数表以及对虚函数表的初始化代码, 这样可以节省运
行时间和空间.但是这个类一定不允许生成实例, 因为没有虚函数表, 就无法对虚函数进行调用.
因此, __declspec(novtable)一般是应用于接口(其实就是包含纯虚函数的类), 因为接口包含的都
是纯虚函数,不可能生成实例. 我们把 __declspec(novtable)应用到接口类中, 这些接口类就不用
包含虚函数表和初始化虚函数表的代码了. 它的派生类会自己包含自己的虚函数表和初始化代码.
*/

class __declspec(novtable) CIOMessageMap
{
public:
    virtual bool ProcessIOMessage(IOType clientIO, PER_IO_CONTEXT* pIOContext, DWORD dwSize) = 0;
};

#define BEGIN_IO_MSG_MAP() \
public: \
    bool ProcessIOMessage(IOType clientIO, PER_IO_CONTEXT* pIOContext, DWORD dwSize) \
    { \
        bool bRet = false;

#define IO_MESSAGE_HANDLE(msg, func) \
        if(msg == clientIO) \
            bRet = func(pIOContext, dwSize);

#define END_IO_MSG_MAP() \
        return bRet; \
    }

#endif  // !__IO_MAPPER__
// LanCriticalLock.h
#pragma once
#include <Windows.h>
#include <string>

/*
    描述: 用于同一进程中的多线程同步
*/

class LanCriticalLock
{
public:
    LanCriticalLock(CRITICAL_SECTION& cs, const std::string& strFunc);
    ~LanCriticalLock();

protected:
    void Lock();
    void Unlock();

private:
    CRITICAL_SECTION*   m_pcs;
    std::string         m_strFunc;
};
// LanCriticalLock.cpp
#include "Global.h"
#include "LanCriticalLock.h"

LanCriticalLock::LanCriticalLock(CRITICAL_SECTION& cs, const std::string& strFunc)
{
    m_strFunc = strFunc;
    m_pcs = &cs;
    Lock();
}


LanCriticalLock::~LanCriticalLock()
{
    Unlock();
}

void LanCriticalLock::Lock()
{
    EnterCriticalSection(m_pcs);
}

void LanCriticalLock::Unlock()
{
    LeaveCriticalSection(m_pcs);
}
// LanEvent.h
#pragma once
#include<windows.h>
class LanEvent
{
public:
    LanEvent();
    ~LanEvent();

    //线程等待事件触发信号
    void Wait();

    //触发线程事件信号
    void Signal();

private:
    HANDLE  m_hEvent;
};
// LanEvent.cpp
#include "Global.h"
#include "LanEvent.h"


LanEvent::LanEvent()
{
    /*
    //第二个参数:如果是true, 则此时即使WaitForSingleObject函数返回,其句柄也不会变为non-signaled状态,只能通过一下两个函数改变状态
    BOOL ResetEvent(HANDLE hEvent); //to the non-signaled
    BOOL SetEvent(HANDLE hEvent);   //to the signaled
    */

    m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hEvent)
        cout << "创建信号量失败!!!" << endl;
}


LanEvent::~LanEvent()
{
    if (m_hEvent)
    {
        ::CloseHandle(m_hEvent);
        m_hEvent = NULL;
    }
}

void LanEvent::Wait()
{
    ::WaitForSingleObject(m_hEvent, INFINITE);
    ::ResetEvent(m_hEvent);
}

void LanEvent::Signal()
{
    if (m_hEvent)
        ::SetEvent(m_hEvent);
}
// LanThreadPool.h
#pragma once
#include "Global.h"
#include <mutex>
#include <vector>
#include "LanWorkerThread.h"

/*
    1. 基于windows api实现
    2. 基于事件对象同步
    3. 线程池设计成单例模式
    4. 功能描述:
        a. 先初始化一定的线程数量
        b. 当有任务时,使用初始化的线程去执行
        c. 如果任务太多线程不够用时,创建线程
        d. 总的线程数不能超过CPU核心数*2
        e. 使用“事件”和原子操作进行线程同步
*/

//定义任务回调函数指针
typedef void(*Task)(VOID);

class LanThreadPool
{
public:
    ~LanThreadPool();

    void SetThreadPool(int nMinThreadCount, int nMaxThreadCount);

    void Initialize(int nMinThreadCount, int nMaxThreadCount);

    //获取本机的CPU核心数
    int GetNumberOfProcessors();

    //创建工作线程
    bool CreateWorkerThread(int nCount, Task task);

    //执行任务
    void ExecTask(Task task);

    //运行线程
    void Run() 
    {
        for (int i = 0; i < m_vWorkerThread.size(); i++)
        {
            m_vWorkerThread[i]->Run();
        }
    }

    //停止处理任务
    void StopTask();

    //获取内存池单例实例对象
    static LanThreadPool* GetLanThreadPoolInstance();

    //释放内存
    void FreeMommery();

    //设置IOCP对象
    void SetIOCP(CCoreByIOCP* coreByIOCP);

    //获取当前运行的线程的总数
    int GetCurrentThreadCount() { return (int)m_vWorkerThread.size(); }

private:
    LanThreadPool();
    //把复制构造函数和=操作符也设为私有,防止被复制
    LanThreadPool(const LanThreadPool&);
    LanThreadPool& operator=(const LanThreadPool& threadPool);
    //单例对象
    static LanThreadPool* m_lanThreadPool;

    //给单例实例的锁
    static std::mutex* m_mtxForInstance;

    //创建自动释放内存的类(利用进程结束时,系统会自动析构此静态成员的原理)--如果想在进程结束时自动释放内存,可使用此方法
    //class CThreadPoolGC
    //{
    //public:
    //  CThreadPoolGC() {}
    //  ~CThreadPoolGC()
    //  {
    //      if (m_lanThreadPool)
    //      {
    //          delete m_lanThreadPool;
    //          m_lanThreadPool = NULL;
    //      }
    //  }

    //};

    //static CThreadPoolGC m_gc;    //  垃圾回收类的静态成员函数

private:
    //保存内存池
    std::vector<LanWorkerThread*> m_vWorkerThread;

    //初始化线程数量
    int m_minThreadCount;

    //最大线程数
    int m_maxThreadCount;
};
// LanThreadPool.cpp
#include "Global.h"
#include "LanThreadPool.h"

//单例实体的初始化
LanThreadPool* LanThreadPool::m_lanThreadPool = new LanThreadPool();
std::mutex* LanThreadPool::m_mtxForInstance = new std::mutex();
//LanThreadPool::CThreadPoolGC LanThreadPool::m_gc;

LanThreadPool::LanThreadPool()
{
    m_vWorkerThread.clear();
    m_minThreadCount = 0;
    m_maxThreadCount = GetNumberOfProcessors() * 2;
}

LanThreadPool::LanThreadPool(const LanThreadPool&)
{

}

LanThreadPool& LanThreadPool::operator=(const LanThreadPool& threadPool)
{
    if (this != &threadPool)
    {

    }

    return *this;
}

LanThreadPool::~LanThreadPool()
{
    if (m_mtxForInstance)
    {
        delete m_mtxForInstance;
        m_mtxForInstance = NULL;
    }

    for (auto iter = m_vWorkerThread.begin(); iter != m_vWorkerThread.end(); )
    {
        delete *iter;
        iter = m_vWorkerThread.erase(iter);
    }

    m_vWorkerThread.clear();
}

void LanThreadPool::SetThreadPool(int nMinThreadCount, int nMaxThreadCount)
{
    if ((nMinThreadCount < 0) || (nMaxThreadCount < 0) || (nMinThreadCount > nMaxThreadCount))
    {
        cout << "线程数设置的最大或最小值错误!!" << endl;
        return;
    }

    Initialize(nMinThreadCount, nMaxThreadCount);
}

void LanThreadPool::Initialize(int nMinThreadCount, int nMaxThreadCount)
{
    //如果设置的最小或最大线程数超过系统CPU核心数,则将线程数设置为CPU核心数*2
    int nNumberOfProcessors = GetNumberOfProcessors();
    nNumberOfProcessors = nNumberOfProcessors * 2;
    if (nMinThreadCount > nNumberOfProcessors)
    {
        nMinThreadCount = nNumberOfProcessors;
    }

    if (nMaxThreadCount > nNumberOfProcessors)
    {
        nMaxThreadCount = nNumberOfProcessors;
    }

    m_minThreadCount = nMinThreadCount;
    m_maxThreadCount = nMaxThreadCount;

    if (m_vWorkerThread.size() > 0)
    {
        for (auto iter = m_vWorkerThread.begin(); iter != m_vWorkerThread.end(); )
        {
            //停止执行的任务
            (*iter)->Stop();

            //先唤醒所有线程
            ResumeThread((*iter)->GetHandle());

            delete *iter;
            iter = m_vWorkerThread.erase(iter);
        }
    }
    m_vWorkerThread.clear();

    CreateWorkerThread(nMinThreadCount, NULL);
}

int LanThreadPool::GetNumberOfProcessors()
{
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);

    return sysInfo.dwNumberOfProcessors;
}

bool LanThreadPool::CreateWorkerThread(int nCount, Task task)
{
    for (int i = 0; i < nCount; i++)
    {
        if (m_vWorkerThread.size() + 1 <= m_maxThreadCount)
        {
            LanWorkerThread* newLanWorkerThread = new LanWorkerThread();
            if (!newLanWorkerThread)
                return false;

            m_vWorkerThread.push_back(newLanWorkerThread);
            newLanWorkerThread->SetTask(task);
        }
    }

    return true;
}

void LanThreadPool::ExecTask(Task task)
{
    //如果没有合适的线程执行这个任务,则一直循环,直到有线程接收了此任务
    if (task)
    {
        Task tempTask = task;
        //安排线程去执行任务
        bool bIsOk = false;
        do
        {
            bool bIsRunTask = false;
            for (int i = 0; i < m_vWorkerThread.size(); i++)
            {
                if (!m_vWorkerThread[i]->GetRunState())
                {
                    m_vWorkerThread[i]->SetTask(tempTask);
                    bIsRunTask = true;
                    bIsOk = true;
                    break;
                }
            }

            if (!bIsRunTask)
            {
                bIsOk = CreateWorkerThread(1, tempTask);
                if (bIsOk)
                {
                    m_vWorkerThread[m_vWorkerThread.size() - 1]->Run();
                }

            }
        } while (!bIsOk && tempTask);

    }

}

void LanThreadPool::StopTask()
{
    cout << "当前运行的线程数量: " << m_vWorkerThread.size() << endl;
    for (int i = 0; i < m_vWorkerThread.size(); i++)
    {
        m_vWorkerThread[i]->Stop();

        //先唤醒所有线程
        ResumeThread(m_vWorkerThread[i]->GetHandle());
    }
}

LanThreadPool* LanThreadPool::GetLanThreadPoolInstance()
{
    //如果多线程时,同时获取这个单例,但此时单例还没有创建,则有可能创建多个实例,所以加个锁
    if (m_lanThreadPool == NULL)
    {
        m_mtxForInstance->lock();
        if (m_lanThreadPool == NULL)
        {
            m_lanThreadPool = new LanThreadPool();
        }
        m_mtxForInstance->unlock();
    }

    return m_lanThreadPool;
}

void LanThreadPool::FreeMommery()
{
    /*if (m_lanThreadPool)
    {
        delete m_lanThreadPool;
        m_lanThreadPool = NULL;
    }*/

    RELEASE_MEMORY(m_lanThreadPool);
}

void LanThreadPool::SetIOCP(CCoreByIOCP* coreByIOCP)
{
    if (coreByIOCP == NULL)
    {
        cout << "IOCP对象为NULL!!!" << endl;
        return;
    }

    for (int i = 0; i < m_vWorkerThread.size(); i++)
    {
        m_vWorkerThread[i]->SetCoreIOCP(coreByIOCP);
    }
}
// LanWorkerThread.h
#pragma once
#include "Global.h"
#include "LanEvent.h"
#include <atomic>

/*
    实现执行某一个函数
*/

//定义任务回调函数指针
typedef void(*Task)(VOID);

class LanWorkerThread
{
public:
    LanWorkerThread();
    ~LanWorkerThread();

    //开始运行
    void Run();

    //设置coreIOCP对象
    void SetCoreIOCP(CCoreByIOCP* coreIOCP);

    //停止运行
    void Stop();

    //线程执行函数
    static unsigned __stdcall  ThreadExec(void* pThis);

    //线程真正的执行函数
    void DoExec();

    //设置任务的回调函数
    void SetTask(Task task);

    //获取使用状态
    bool GetRunState() 
    { 
        return m_bRunState; 
    }

    //获取工作线程的句柄
    HANDLE GetHandle() { return m_hdlThread; }

private:
    //是否运行
    bool m_bIsStop;

    //使用状态
    std::atomic<bool> m_bRunState;

    //线程句柄
    HANDLE m_hdlThread;

    //本线程的执行的任务
    Task m_task;

    //事件同步对象
    LanEvent m_eventCondition;

    //coreIOCP
    CCoreByIOCP* m_coreIOCP;

};
// LanWorkerThread.cpp
#include "Global.h"
#include "LanWorkerThread.h"
#include <process.h>    //for _beginthreadex

LanWorkerThread::LanWorkerThread()
{
    m_bIsStop = false;
    m_bRunState = false;
    m_task = NULL;
    m_coreIOCP = NULL;

    //第5个参数 0: 立即运行  CREATE_SUSPENDED: 悬挂(挂起) 
    //也可以使用SuspendThread函数来挂起线程
    m_hdlThread = (HANDLE)_beginthreadex(NULL, 0, &LanWorkerThread::ThreadExec, this, CREATE_SUSPENDED, NULL);  
    if (m_hdlThread == 0)
    {
        cout << "创建线程失败!!!" << endl;
    }
}


LanWorkerThread::~LanWorkerThread()
{
    if (m_hdlThread != 0)
    {
        //WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,
        //线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。
        //WaitForSingleObject(m_hdlThread, INFINITE);
        WaitForSingleObject(m_hdlThread, INFINITE);
        cout << "线程ID:" << GetThreadId(m_hdlThread) << "退出!!!" << endl;
        CloseHandle(m_hdlThread);
    }

}

void LanWorkerThread::SetCoreIOCP(CCoreByIOCP* coreIOCP)
{
    if (coreIOCP != NULL)
        m_coreIOCP = coreIOCP;
}

void LanWorkerThread::Run()
{
    if (m_coreIOCP == NULL)
    {
        cout << "IOCP对象为NULL!!!!!" << endl;
        return;
    }

    if (m_hdlThread)
        ResumeThread(m_hdlThread);   //唤醒线程
    else
        cout << "线程HANDLE错误!!!";
}

void LanWorkerThread::Stop()
{
    m_bIsStop = true;
    m_eventCondition.Signal();
}

unsigned __stdcall  LanWorkerThread::ThreadExec(void* m_coreIOCP)
{
    LanWorkerThread* pWorker = (LanWorkerThread*)m_coreIOCP;
    if (!pWorker)
        return 0;

    pWorker->DoExec();
    return 1;
}

void LanWorkerThread::DoExec()
{
    if (m_coreIOCP == NULL)
    {
        cout << "IOCP对象为NULL!!!!!" << endl;
        return;
    }

    OVERLAPPED* pOverlapped = NULL;
    DWORD dwIoSize = 0;
    BOOL bRet = FALSE;
    DWORD dwErr = 0;
    IOCP_PARAM* pIocpParam = NULL;
    PER_IO_CONTEXT* pIoContext = NULL;
    HANDLE hdIOCP = m_coreIOCP->GetIOCPPort();

    while (1)
    {
        //m_eventCondition.Wait();
        //m_bRunState = false;
        bRet = GetQueuedCompletionStatus(//  从完成端口中获取消息
            hdIOCP,
            &dwIoSize,
            (PULONG_PTR)&pIocpParam,
            &pOverlapped,
            INFINITE);

        if (EXIT_CODE == pIocpParam)
            break;

        //每一个socket都有自己独自的PER_IO_CONTEXT!!!!后续发送接收都会继续使用之前同一个PER_IO_CONTEXT
        pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_overLapped);  

        if (!bRet)// 处理错误信息
        {
            dwErr = GetLastError();
            if (WAIT_TIMEOUT == dwErr)// 超时
            {
                // 超时后,通过发送一个消息,判断是否断线,否则在socket上投递WSARecv会出错
                // 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候,服务器端是无法收到客户端断开的通知的
                if (-1 == send(pIocpParam->m_rourceSock, "", 0, 0))
                {
                    m_coreIOCP->MoveToFreeParamPool(pIocpParam);
                    m_coreIOCP->RemoveStaleClient(pIoContext, FALSE);
                }
                continue;
            }
            if (ERROR_NETNAME_DELETED == dwErr)// 客户端异常退出
            {
                m_coreIOCP->MoveToFreeParamPool(pIocpParam);
                m_coreIOCP->RemoveStaleClient(pIoContext, FALSE);
                continue;
            }

            break;// 完成端口出现错误
        }

        // 正式处理接收到的数据 读取接收到的数据
        // CONTAINING_RECORD宏返回给定结构类型的结构实例的 基地址 和包含结构中字段的地址。
        if (bRet && 0 == dwIoSize)
        {
            // 客户端断开连接,释放资源
            m_coreIOCP->MoveToFreeParamPool(pIocpParam);
            m_coreIOCP->RemoveStaleClient(pIoContext, FALSE);
            continue;
        }

        if (bRet && NULL != pIoContext && NULL != pIocpParam)
        {
            try
            {
                m_coreIOCP->ProcessIOMessage(pIoContext->m_IOType, pIoContext, dwIoSize);
            }
            catch (...) { }
        }
    }

    cout << "线程ID:" << GetThreadId(m_hdlThread) << "执行完毕!!" << endl;
}

void LanWorkerThread::SetTask(Task task)
{
    if (task)
    {
        m_task = task;
        m_bRunState = true;
        m_eventCondition.Signal();
    }

}
// CoreByIOCP.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include "Global.h"
#include "LanCriticalLock.h"
#include "LanEvent.h"
#include <map>

typedef void (CALLBACK* NOTIFYPROC)(LPVOID, PER_IO_CONTEXT*, UINT);

typedef std::list<PER_IO_CONTEXT*> IOContextList;
typedef std::list<IOCP_PARAM*>  IocpParamList;

class CCoreByIOCP
{
public:
    explicit CCoreByIOCP();
    ~CCoreByIOCP();

    //启动core层
    bool RunCore(NOTIFYPROC pNotifyProc, const UINT& nPort);

    //关闭core层
    void StopCore();

    //获取完成端口句柄
    HANDLE  GetIOCPPort() { return m_hIOCompletionPort; }

    //内存池的实现
    VOID MoveToFreeParamPool(IOCP_PARAM* pIocpParam);       // 将移除的完成端口参数添加到内存池
    VOID MoveToFreePool(PER_IO_CONTEXT* pIoContext);        // 将移除的客户端套接字添加到内存池
    IOCP_PARAM* AllocateIocpParam();                        // 分配完成端口的结构体

    // 资源的释放
    VOID RemoveStaleClient(PER_IO_CONTEXT* pIoContext, BOOL bGraceful);// 溢出客户端套接字
    VOID ReleaseResource();                                 // 释放资源

    bool PostSend(PER_IO_CONTEXT* pIoContext, bool bIsSendToSource = true); // 投递一个发送操作
    PER_IO_CONTEXT* AllocateClientIOContext();              // 从内存池中分配一个socket结构体

    // 消息映射
    BEGIN_IO_MSG_MAP()
        IO_MESSAGE_HANDLE(EM_IORecv, OnClientReading)
        IO_MESSAGE_HANDLE(EM_IOSend, OnClientWriting)
        IO_MESSAGE_HANDLE(EM_IOAccept, OnClientAccept)
    END_IO_MSG_MAP()

    bool OnClientAccept(PER_IO_CONTEXT* pIOContext, DWORD dwSize = 0);
    bool OnClientReading(PER_IO_CONTEXT* pIOContext, DWORD dwSize = 0);
    bool OnClientWriting(PER_IO_CONTEXT* pIOContext, DWORD dwSize = 0);

protected:
    bool InitNetEnvironment();                              // 初始化网络环境
    bool InitializeIOCP();                                  // 初始化完成端口
    bool InitializeListenSocket();                          // 初始化监听套接字

    // 投递操作
    bool PostAcceptEx(PER_IO_CONTEXT* pAcceptIoContext);    // 投递一个Accept请求
    bool PostRecv(PER_IO_CONTEXT* pIoContext, bool bIsNeedInit = false);        // 投递一个接收的IO操作//如果已接收完一个完整包或重新开始使用,则bIsNeedInit应设置为true

    bool OnAccept(PER_IO_CONTEXT* pIoContext);
    // 将 监听套接字 绑定到完成端口中
    bool AssociateSocketWithCompletionPort(SOCKET socket, DWORD dwCompletionKey);

    //增加客户端信息
    void AddCLientInfo(SOCKET socket, UINT32 id);

    //删除客户端信息
    void RomoveClientInfo(SOCKET socket);

    //根据ID获取客户端SOCKET
    void GetClientSOCKET(UINT32 id, SOCKET& socket);

    //根据客户端SOCKET获取ID
    void GetClientID(UINT32& id, SOCKET socket);

private:
    NOTIFYPROC m_pNotifyProc;                   // 消息回调函数
    CRITICAL_SECTION m_cs;                      // 关键段(临界资源)

    HANDLE m_hShutDownEvent;                    // 系统退出事件通知
    HANDLE m_hIOCompletionPort;                 // 完成端口句柄
    //UINT m_nThreadCnt;                            // 线程数量
    LanThreadPool* m_pWorkThreads;              // 工作者线程者指针

    PER_IO_CONTEXT* m_pListenContext;           // 用于监听的Socket的Context信息
    SOCKET m_socListen;                         // 监听套接字
    UINT m_nPort;                               // 监听的端口

    IOContextList m_listAcceptExSock;           // AcceptEx 预创建的套接字,便于管理

    IOContextList m_listIoContext;              // 已经连接的客户端
    IOContextList m_listFreeIoContext;          // 断开的客户端的链表    共同实现一个内存池
    IocpParamList m_listIocpParam;              // 已经使用的参数链表
    IocpParamList m_listFreeIocpParam;          // 空闲的完成端口参数链表

    IOCP_PARAM* m_pListenIocpParam;             // 监听套接字的完成端口参数

    int m_nKeepLiveTime;                        // 心跳包探测间隔时间

    LPFN_ACCEPTEX  m_lpfnAcceptEx;              // AcceptEx 和 GetAcceptExSockaddrs 的函数指针,用于调用这两个扩展函数
    LPFN_GETACCEPTEXSOCKADDRS   m_lpfnGetAcceptExSockAddrs;

    //客户端socket和客户端IP地址以及端口号
    std::map<SOCKET, UINT32>    m_mapClient;

    //用来判断是否执行了StopCore //用于当coreIOCP已经跑起来时,突然关闭此进程导致资源没有安全释放
    bool    m_bIsExecStopCore;
};
// CoreByIOCP.cpp
#include "Global.h"
#include "CoreByIOCP.h"
#include "LanThreadPool.h"
#pragma warning(disable: 4996)

CCoreByIOCP::CCoreByIOCP()
{
    m_hShutDownEvent = NULL;
    m_hIOCompletionPort = NULL;
    //m_nThreadCnt = 0;
    m_pWorkThreads = NULL;
    m_nPort = 0;
    m_lpfnAcceptEx = NULL;
    m_pListenContext = new PER_IO_CONTEXT();
    m_nKeepLiveTime = 1000 * 60 * 3; // 三分钟探测一次
    m_pListenIocpParam = new IOCP_PARAM;
    m_mapClient.clear();

    //重置此标志
    m_bIsExecStopCore = false;
}


CCoreByIOCP::~CCoreByIOCP()
{
    if (!m_bIsExecStopCore)
    {
        StopCore();
    }
}

bool CCoreByIOCP::RunCore(NOTIFYPROC pNotifyProc, const UINT& nPort)
{
    m_nPort = nPort;
    m_pNotifyProc = pNotifyProc;

    //重置此标志
    m_bIsExecStopCore = false;

    //初始化临界区对象
    InitializeCriticalSection(&m_cs);

    bool bRet = false;
    do
    {
        if (NULL == (m_hShutDownEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
            break;
        if (!InitNetEnvironment())
            break;
        if (!InitializeIOCP())
            break;
        if (!InitializeListenSocket())
            break;
        bRet = true;
    } while (FALSE);

    if (!bRet)
    {
        //TCHAR szErr[32];
        cout << "启动服务器失败!!!!!!!!" << endl;
        //::MessageBox(GetDesktopWindow(), szErr, L"启动服务器失败", MB_OK | MB_ICONHAND);
    }

    return bRet;
}

void CCoreByIOCP::StopCore()
{
    //重置标志
    m_bIsExecStopCore = true;

    if (m_socListen != INVALID_SOCKET)
    {
        SetEvent(m_hShutDownEvent);

        if (m_pWorkThreads)
        {
            for (int i = 0; i <m_pWorkThreads->GetCurrentThreadCount(); i++)
            {
                //PostQueuedCompletionStatus函数,向每个工作者线程都发送—个特殊的完成数据包。该函数会指示每个线程都“立即结束并退出”
                /*
                通过为线程池中的每个线程都调用一次PostQueuedCompletionStatus,我们可以将它们都唤醒。
                每个线程会对GetQueuedCompletionStatus的返回值进行检查,如果发现应用程序正在终止,那么它们就可以进行清理工作并正常地退出。
                "也就是说调用此句会唤醒相应的线程,从而不需要调用额外语句去唤醒相应线程!!!!"
                */
                PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
            }
        }

        ReleaseResource();
    }

}

bool CCoreByIOCP::InitNetEnvironment()
{
    WSADATA wsaData;
    if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
        return false;

    return true;
}

bool CCoreByIOCP::InitializeIOCP()
{
    SYSTEM_INFO systemInfo;

    // 创建完成端口
    m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (NULL == m_hIOCompletionPort)
        return false;

    GetSystemInfo(&systemInfo);
    // 线程数的限制,防止线程上下文切换浪费资源
    int m_nThreadCnt = WORKER_THREADS_PER_PROCESSOR * systemInfo.dwNumberOfProcessors;// 核心数的两倍

    m_pWorkThreads = LanThreadPool::GetLanThreadPoolInstance();
    m_pWorkThreads->SetThreadPool(m_nThreadCnt, m_nThreadCnt);
    m_pWorkThreads->SetIOCP(this);
    m_pWorkThreads->Run();

    return true;
}

bool CCoreByIOCP::InitializeListenSocket()
{
    // AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;

    // 需要使用重叠IO,必须使用WSASocket来建立Socket,才可以支持重叠IO操作
    m_socListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET == m_socListen)
        return false;

    m_pListenIocpParam->m_rourceSock = m_socListen;
    // 将 监听套接字 绑定到完成端口中
    if (!AssociateSocketWithCompletionPort(m_socListen, (DWORD)(m_pListenIocpParam->m_rourceSock)))
    {
        RELEASE_SOCKET(m_socListen);
        return false;
    }

    // 绑定
    SOCKADDR_IN servAddr;
    ZeroMemory(&servAddr, sizeof(SOCKADDR_IN));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(m_nPort);
    if (SOCKET_ERROR == ::bind(m_socListen, (struct sockaddr*)&servAddr, sizeof(servAddr)))
    {
        RELEASE_SOCKET(m_socListen);
        return false;
    }

    // 开始监听
    if (SOCKET_ERROR == listen(m_socListen, SOMAXCONN))
    {
        RELEASE_SOCKET(m_socListen);
        return false;
    }

    // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
    // 所以需要额外获取一下AcceptEx函数的指针
    DWORD dwBytes = 0;
    if (SOCKET_ERROR == WSAIoctl(
        m_socListen,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidAcceptEx, sizeof(GuidAcceptEx),
        &m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx),
        &dwBytes, NULL, NULL))
    {
        this->ReleaseResource();
        return false;
    }

    // 获取GetAcceptExSockAddrs函数指针,也是同理
    if (SOCKET_ERROR == WSAIoctl(
        m_socListen,
        SIO_GET_EXTENSION_FUNCTION_POINTER,
        &GuidGetAcceptExSockAddrs,
        sizeof(GuidGetAcceptExSockAddrs),
        &m_lpfnGetAcceptExSockAddrs,
        sizeof(m_lpfnGetAcceptExSockAddrs),
        &dwBytes,
        NULL,
        NULL))
    {
        this->ReleaseResource();
        return false;
    }

    // 为AcceptEx 准备参数,然后投递AcceptEx I/O请求
    for (int i = 0; i < MAX_POST_ACCEPT; i++)
    {
        PER_IO_CONTEXT* pAcceptIoContext = new PER_IO_CONTEXT();
        pAcceptIoContext->Init();
        if (FALSE == PostAcceptEx(pAcceptIoContext))
        {
            this->RemoveStaleClient(pAcceptIoContext, TRUE);
            this->ReleaseResource();
            return false;
        }
        m_listAcceptExSock.push_back(pAcceptIoContext);
    }

    return true;
}

VOID CCoreByIOCP::MoveToFreeParamPool(IOCP_PARAM* pIocpParam)
{
    LanCriticalLock cs(m_cs, "MoveToFreeParamPool");

    IocpParamList::iterator iter;
    iter = find(m_listIocpParam.begin(), m_listIocpParam.end(), pIocpParam);
    if (iter != m_listIocpParam.end())
    {
        m_listFreeIocpParam.push_back(pIocpParam);
        m_listIocpParam.remove(pIocpParam);
    }
}

VOID CCoreByIOCP::MoveToFreePool(PER_IO_CONTEXT* pIoContext)
{
    LanCriticalLock cs(m_cs, "MoveToFreePool");

    IOContextList::iterator iter;
    iter = find(m_listIoContext.begin(), m_listIoContext.end(), pIoContext);

    if (iter != m_listIoContext.end())
    {
        m_listFreeIoContext.push_back(pIoContext);// 不释放已经创建的,加入到内存池中,下次有新客户端连接就不用再创建
        m_listIoContext.remove(pIoContext);
    }
}

IOCP_PARAM* CCoreByIOCP::AllocateIocpParam()
{
    LanCriticalLock cs(m_cs, "AllocateIocpParam");

    IOCP_PARAM* pIocpParam = NULL;
    if (m_listFreeIocpParam.empty())
    {
        pIocpParam = new IOCP_PARAM;
    }
    else
    {
        pIocpParam = m_listFreeIocpParam.front();
        m_listFreeIocpParam.remove(pIocpParam);
    }

    m_listIocpParam.push_back(pIocpParam);
    if (pIocpParam != NULL)
    {
        pIocpParam->m_rourceSock = INVALID_SOCKET;
    }
    return pIocpParam;
}

VOID CCoreByIOCP::RemoveStaleClient(PER_IO_CONTEXT* pIoContext, BOOL bGraceful/*是否中止连接*/)
{
    LanCriticalLock cs(m_cs, "RemoveStaleClient");

    // 如果我们要中止连接,设置延时值为 0 (优雅的关闭连接)
    if (!bGraceful)
    {
        //.l_onoff=1;(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
        // 如果.l_onoff=0;则功能和2.)作用相同;
        LINGER linger;
        linger.l_onoff = 1; //开关,零或者非零 
        linger.l_linger = 0;  //优雅关闭最长时限(允许逗留的时限 秒)

        setsockopt(pIoContext->m_rourceSock, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
    }


    //将该客户端的信息清除
    RomoveClientInfo(pIoContext->m_rourceSock);

    // 释放 PER_SOCKET_CONTEXT 中的数据
    std::list<PER_IO_CONTEXT*>::iterator iter;
    iter = find(m_listIoContext.begin(), m_listIoContext.end(), pIoContext);
    if (iter != m_listIoContext.end())
    {
        // CancelIo 取消挂起的IO操作 优雅的关闭这个套接字句柄
        CancelIo((HANDLE)pIoContext->m_rourceSock);

        RELEASE_SOCKET(pIoContext->m_rourceSock);
        // closesocket(pIoContext->m_rourceSock);
        // pIoContext->m_rourceSock = INVALID_SOCKET;

        // 判断是否还在进行IO操作 等待上一个IO操作完成再关闭
        while (!HasOverlappedIoCompleted((LPOVERLAPPED)pIoContext))
            Sleep(0);

        // 回调函数,发送退出消息
        m_pNotifyProc(NULL, pIoContext, NC_CLIENT_DISCONNECT);

        MoveToFreePool(pIoContext);
    }
}

VOID CCoreByIOCP::ReleaseResource()
{
    // 删除关键段
    DeleteCriticalSection(&m_cs);

    // 释放 系统退出事件句柄
    RELEASE_HANDLE(m_hShutDownEvent);

    // 释放工作者线程句柄指针
    if (m_pWorkThreads)
        m_pWorkThreads->FreeMommery();

    // 关闭IOCP句柄
    RELEASE_HANDLE(m_hIOCompletionPort);

    // 关闭监听套接字
    RELEASE_SOCKET(m_socListen);

    // 删除监听套接字的完成端口参数
    //delete m_pListenIocpParam;
    RELEASE_MEMORY(m_pListenIocpParam);

    IOContextList::iterator iter;

    // 清理空闲的套接字
    iter = m_listFreeIoContext.begin();
    while (iter != m_listFreeIoContext.end())
    {
        //delete *iter;
        RELEASE_MEMORY(*iter);
        ++iter;
    }
    m_listFreeIoContext.clear();

    // 清理连接的套接字
    iter = m_listIoContext.begin();
    while (iter != m_listIoContext.end())
    {
        //closesocket((*iter)->m_rourceSock);
        //delete *iter;
        RELEASE_SOCKET((*iter)->m_rourceSock);
        RELEASE_MEMORY(*iter);
        ++iter;
    }
    m_listIoContext.clear();

    // 清理预创建的套接字
    iter = m_listAcceptExSock.begin();
    while (iter != m_listAcceptExSock.end())
    {
        //delete *iter;
        RELEASE_MEMORY(*iter);
        ++iter;
    }

    //清空客户端IP信息
    m_mapClient.clear();
}

// 接收到客户端的连接
bool CCoreByIOCP::OnAccept(PER_IO_CONTEXT* pIoContext)
{
    SOCKADDR_IN* RemoteSockAddr = NULL;
    SOCKADDR_IN* LocalSockAddr = NULL;
    int nLen = sizeof(SOCKADDR_IN);

    ///////////////////////////////////////////////////////////////////////////
    // 1. m_lpfnGetAcceptExSockAddrs 取得客户端和本地端的地址信息 与 客户端发来的第一组数据
    // 如果客户端只是连接了而不发消息,是不会接收到的
    this->m_lpfnGetAcceptExSockAddrs(
        pIoContext->m_wsaBuf.buf,                       // 第一条信息
        pIoContext->m_wsaBuf.len - ((nLen + 16) * 2),
        nLen + 16, nLen + 16,
        (sockaddr**)&LocalSockAddr, &nLen,              // 本地信息
        (sockaddr**)&RemoteSockAddr, &nLen);            // 客户端信息

    PER_IO_CONTEXT* pNewIoContext = AllocateClientIOContext();
    pNewIoContext->m_rourceSock = pIoContext->m_rourceSock;
    pNewIoContext->m_resourceAddr = *RemoteSockAddr;

    //保存数据
    memcpy_s(pNewIoContext->m_szBufCache, pIoContext->m_dwBytesRecv, pIoContext->m_szBuf, pIoContext->m_dwBytesRecv);
    pNewIoContext->m_dwBytesRecv = pIoContext->m_dwBytesRecv;   //记录接收到的字节数

    // 处理消息,此处为连接上,第一次接受到客户端的数据
    m_pNotifyProc(NULL, pIoContext, NC_CLIENT_CONNECT);

    IOCP_PARAM* pIocpParam = AllocateIocpParam();
    pIocpParam->m_rourceSock = pNewIoContext->m_rourceSock;
    // 将新连接的客户端的socket,绑定到完成端口
    if (!AssociateSocketWithCompletionPort(pNewIoContext->m_rourceSock, (DWORD)pIocpParam->m_rourceSock))
    {
        // closesocket(m_socListen);
        // closesocket(pNewIoContext->m_rourceSock);

        /*delete pNewIoContext;
        delete pIocpParam;
        pNewIoContext = NULL;
        pIocpParam = NULL;*/

        RELEASE_SOCKET(m_socListen);
        RELEASE_SOCKET(pNewIoContext->m_rourceSock);
        RELEASE_MEMORY(pNewIoContext);
        RELEASE_MEMORY(pIocpParam);
        return false;
    }

    SendToIpInfo ipInfo;
    ZeroMemory(&ipInfo, MAX_BUFFER_LEN);
    memcpy_s(&ipInfo, MAX_BUFFER_LEN, pIoContext->m_szBuf, MAX_BUFFER_LEN);

    //增加客户端信息
    AddCLientInfo(pNewIoContext->m_rourceSock, ipInfo.nSelfID);

    // Set KeepAlive 设置心跳包,开启保活机制,用于保证TCP的长连接(它会在底层发一些数据,不会传到应用层)
    unsigned long chOpt = 1;
    if (SOCKET_ERROR == setsockopt(pNewIoContext->m_rourceSock, SOL_SOCKET, SO_KEEPALIVE, (char*)&chOpt, sizeof(char)))
    {
        // 心跳激活失败
        MoveToFreeParamPool(pIocpParam);
        RemoveStaleClient(pNewIoContext, TRUE);
        return false;
    }

    // 设置超时详细信息
    tcp_keepalive klive;
    klive.onoff = 1; // 启用保活
    klive.keepalivetime = m_nKeepLiveTime;
    klive.keepaliveinterval = 1000 * 10; // 重试间隔为10秒 Resend if No-Reply
    WSAIoctl
    (
        pNewIoContext->m_rourceSock,
        SIO_KEEPALIVE_VALS,
        &klive,
        sizeof(tcp_keepalive),
        NULL,
        0,
        (unsigned long *)&chOpt,
        0,
        NULL
    );

    // 给新连接的套接字投递接收操作
    if (!PostRecv(pNewIoContext))
    {
        MoveToFreeParamPool(pIocpParam);
    }

    LanCriticalLock cs(m_cs, "OnAccept");

    pIoContext->Reset();        // 再次初始化,便于再次利用
    return PostAcceptEx(pIoContext);
}



bool CCoreByIOCP::OnClientAccept(PER_IO_CONTEXT* pIOContext, DWORD dwSize /*= 0*/)
{
    bool bRet = false;
    try
    {
        pIOContext->m_dwBytesRecv = dwSize;
        // ... 处理一些接收到的操作
        bRet = OnAccept(pIOContext);
    }
    catch (...) { }

    return bRet;
}

bool CCoreByIOCP::OnClientReading(PER_IO_CONTEXT* pIOContext, DWORD dwSize /*= 0*/)
{
    LanCriticalLock cs(m_cs, "OnClientReading");
    bool bRet = false;
    try
    {
        //保存数据
        if (pIOContext->m_dwBytesRecv != MAX_BUFFER_LEN)
        {
            char* p = pIOContext->m_szBufCache + pIOContext->m_dwBytesRecv;
            memcpy(p, pIOContext->m_szBuf, dwSize); //刷新接收到的一个完整包中的缓存
        }

        pIOContext->m_dwBytesRecv += dwSize;    //记录接收到的字节数

        // 处理接收到的数据
        m_pNotifyProc(NULL, pIOContext, NC_RECEIVE);

        if (pIOContext->m_dwBytesRecv == MAX_BUFFER_LEN)
        {
            // 处理接收到的数据
            cout << "完整接收数据!!!!!!!!!" << endl;
            m_pNotifyProc(NULL, pIOContext, NC_RECEIVE_COMPLETE);

            // 再投递一个异步接收消息
            bRet = PostRecv(pIOContext, true);
        }
        else
        {
            // 再投递一个异步接收消息
            //因为此时一个包没有接收完,所以,其一些信息不能被初始化
            bRet = PostRecv(pIOContext, false);
        }


    }
    catch (...) { }

    return bRet;
}

bool CCoreByIOCP::PostAcceptEx(PER_IO_CONTEXT * pAcceptIoContext)
{
    pAcceptIoContext->m_IOType = EM_IOAccept;// 初始化 IO类型 为接收套接字

    // 为以后新连入的客户端准备好Socket(这是与传统Accept最大的区别)
    // 实际上创建一个 网络连接池 ,类似于 内存池,我们先创建一定数量的socket,然后直接使用就是了
    pAcceptIoContext->m_rourceSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET == pAcceptIoContext->m_rourceSock)
        return false;

    // 投递异步 AcceptEx
    if (FALSE == m_lpfnAcceptEx(
        m_socListen,                    //侦听套接字。服务器应用程序在这个套接字上等待连接
        pAcceptIoContext->m_rourceSock,     //将用于连接的套接字。此套接字必须不能已经绑定或者已经连接。
        pAcceptIoContext->m_wsaBuf.buf, //指向一个缓冲区,该缓冲区用于接收新建连接的所发送数据的第一个块、该服务器的本地地址和客户端的远程地址。接收到的数据将被写入到缓冲区0偏移处,而地址随后写入。 该参数必须指定,如果此参数设置为NULL,将不会得到执行,也无法通过GetAcceptExSockaddrs函数获得本地或远程的地址
        pAcceptIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2),  //这一大小应不包括服务器的本地地址的大小或客户端的远程地址,他们被追加到输出缓冲区。如果dwReceiveDataLength是零,AcceptEx将不等待接收任何数据,而是尽快建立连接。
        sizeof(SOCKADDR_IN) + 16,   //为本地地址信息保留的字节数。此值必须比所用传输协议的最大地址大小长16个字节
        sizeof(SOCKADDR_IN) + 16,   //为远程地址的信息保留的字节数。此值必须比所用传输协议的最大地址大小长16个字节。 该值不能为0。
        &(pAcceptIoContext->m_dwBytesRecv), //指向一个DWORD用于标识接收到的字节数。此参数只有在同步模式下有意义。如果函数返回ERROR_IO_PENDING并在迟些时候完成操作,那么这个DWORD没有意义,这时你必须获得从完成通知机制中读取操作字节数
        &(pAcceptIoContext->m_overLapped))  //一个OVERLAPPED结构,用于处理请求。此参数必须指定,它不能为空
        )
    {
        if (WSA_IO_PENDING != WSAGetLastError())
            return false;
    }

    return true;
}

bool CCoreByIOCP::PostRecv(PER_IO_CONTEXT* pIoContext, bool bIsNeedInit)
{
    if (bIsNeedInit)
    {
        // 清空缓冲区,再次投递
        ZeroMemory(&pIoContext->m_overLapped, sizeof(OVERLAPPED));
        ZeroMemory(pIoContext->m_szBuf, MAX_BUFFER_LEN);
        ZeroMemory(pIoContext->m_szBufCache, MAX_BUFFER_LEN);
        pIoContext->m_dwBytesRecv = 0;
    }

    pIoContext->m_IOType = EM_IORecv;
    DWORD dwNumBytesOfRecvd;

    ULONG ulFlags = MSG_PARTIAL;
    UINT nRet = WSARecv(
        pIoContext->m_rourceSock,
        &(pIoContext->m_wsaBuf),
        1,
        &dwNumBytesOfRecvd,// 接收的字节数,异步操作的返回结果一般为0,具体接收到的字节数在完成端口获得
        &(ulFlags),
        &(pIoContext->m_overLapped),
        NULL);
    if (SOCKET_ERROR == nRet && WSA_IO_PENDING != WSAGetLastError())
    {
        RemoveStaleClient(pIoContext, FALSE);
        return false;
    }

    return true;
}


bool CCoreByIOCP::OnClientWriting(PER_IO_CONTEXT* pIOContext, DWORD dwSize /*= 0*/)
{
    bool bRet = false;
    try
    {
        // 异步发送的返回的传输成功的结果是否 少于 要求发送的数据大小(未发送完成),此时重发
        // 最好使用CRC校验之内的,更加严谨性(可以在结构体中放一个计算的CRC值),但是计算会更消耗性能
        pIOContext->m_dwBytesSend += dwSize;
        if (MAX_BUFFER_LEN > pIOContext->m_dwBytesSend)
        {
            bRet = PostSend(pIOContext);
        }
        else// 已经发送成功,将结构体放回内存池
        {
            m_pNotifyProc(NULL, pIOContext, NC_TRANSMIT);
            pIOContext->m_dwBytesSend = 0;
            MoveToFreePool(pIOContext);
        }
    }
    catch (...) { }

    return bRet;
}


bool CCoreByIOCP::PostSend(PER_IO_CONTEXT* pIoContext, bool bIsSendToSource)
{
    /*测试代码---start*/
    //在消息头部增加发送者ID
    char buf[MAX_BUFFER_LEN]{ 0 };
    char cResourceID[12]{ 0 };
    UINT32 ID = 0;
    GetClientID(ID, pIoContext->m_rourceSock);
    itoa(ID, cResourceID, 10);
    memcpy(buf, cResourceID, 4);
    char* p = buf + 4;
    memcpy(p, pIoContext->m_szBuf, MAX_BUFFER_LEN - 4);

    memcpy(pIoContext->m_szBuf, buf, MAX_BUFFER_LEN);
    /*测试代码---end*/

    pIoContext->m_wsaBuf.buf = pIoContext->m_szBuf;     //坑!!以‘\0’结束!!!
    pIoContext->m_wsaBuf.len = MAX_BUFFER_LEN;//static_cast<ULONG>(strlen(pIoContext->m_szBuf));
    pIoContext->m_IOType = EM_IOSend;
    ULONG ulFlags = MSG_PARTIAL;

    //给目标地址发消息
    SOCKET  socket = INVALID_SOCKET;
    if (bIsSendToSource)
    {
        socket = pIoContext->m_rourceSock;
    }
    else
    {
        GetClientSOCKET(pIoContext->m_desID, socket);
        if (socket == INVALID_SOCKET)
        {
            return false;
        }
    }

    INT nRet = WSASend(
        socket,
        &pIoContext->m_wsaBuf,
        1,
        &(pIoContext->m_wsaBuf.len),
        ulFlags,
        &(pIoContext->m_overLapped),
        NULL);

    if (SOCKET_ERROR == nRet && WSA_IO_PENDING != WSAGetLastError())
    {
        RemoveStaleClient(pIoContext, FALSE);
        return false;
    }

    return true;
}

bool CCoreByIOCP::AssociateSocketWithCompletionPort(SOCKET socket, DWORD dwCompletionKey)
{
    // 绑定套接字绑定到完成端口
    // 第二个参数为完成端口句柄时,返回值为完成端口。为空时,返回新的完成端口句柄
    HANDLE hTmp = CreateIoCompletionPort((HANDLE)socket, m_hIOCompletionPort, dwCompletionKey, 0);
    return hTmp == m_hIOCompletionPort;
}

void CCoreByIOCP::AddCLientInfo(SOCKET socket, UINT32 id)
{
    cout << "增加客户端信息: " << socket << ":" << id << endl;
    m_mapClient[socket] = id;
}

void CCoreByIOCP::RomoveClientInfo(SOCKET socket)
{
    m_mapClient.erase(socket);
}

void CCoreByIOCP::GetClientSOCKET(UINT32 id, SOCKET & socket)
{
    //可以使用map自带的find函数,map底层的实现是“红黑树”,所以,用其自带的函数效率会更高!!
    std::map<SOCKET, UINT32>::iterator iter = m_mapClient.begin();
    for (; iter!=m_mapClient.end(); iter++)
    {
        if (iter->second == id)
        {
            socket =  iter->first;
            return;
        }
    }

    socket = INVALID_SOCKET;
}

void CCoreByIOCP::GetClientID(UINT32& id, SOCKET socket)
{
    //可以使用map自带的find函数,map底层的实现是“红黑树”,所以,用其自带的函数效率会更高!!
    std::map<SOCKET, UINT32>::iterator iter = m_mapClient.begin();
    for (; iter != m_mapClient.end(); ++iter)
    {
        if (iter->first == socket)
        {
            id = iter->second;
            return;
        }
    }

    id = 0;
}

PER_IO_CONTEXT* CCoreByIOCP::AllocateClientIOContext()
{
    LanCriticalLock cs(m_cs, "AllocateSocketContext");

    PER_IO_CONTEXT* pIoContext = NULL;
    if (!m_listFreeIoContext.empty())
    {
        pIoContext = m_listFreeIoContext.front();
        m_listFreeIoContext.remove(pIoContext);
    }
    else
    {
        pIoContext = new PER_IO_CONTEXT();
    }

    m_listIoContext.push_back(pIoContext);
    if (pIoContext != NULL)
    {
        pIoContext->Init();
    }

    return pIoContext;
}
// ServerByIOCP.cpp
#include "Global.h"
#include "CoreByIOCP.h"
#include <WS2tcpip.h>
#include <stdlib.h>
#include<string.h>

CCoreByIOCP* pIOCP;

/*
    此服务器就相当于一个中转器!!!
*/
void CALLBACK NotifyProc(LPVOID lparam, PER_IO_CONTEXT* pIoContext, UINT uFlag)
{
    switch (uFlag)
    {
    case NC_CLIENT_CONNECT:
    {
        SendToIpInfo ipInfo;
        memset(&ipInfo, 0, MAX_BUFFER_LEN);
        memcpy(&ipInfo, pIoContext->m_szBuf, MAX_BUFFER_LEN);
        cout << "[" << inet_ntoa(pIoContext->m_resourceAddr.sin_addr) << "-" << htons(pIoContext->m_resourceAddr.sin_port)
            << "]->连接,第一条数据【" << ipInfo.nSelfID << ":" << ipInfo.nSendToID << ":" << ipInfo.buf << "】" << endl;

        break;
    }
    case NC_CLIENT_DISCONNECT:
    {
        cout << "[" << inet_ntoa(pIoContext->m_resourceAddr.sin_addr) << "-" << htons(pIoContext->m_resourceAddr.sin_port)
            << "]->断开连接" << endl;
        break;
    }
    case NC_TRANSMIT:
    {
        cout << "[" << inet_ntoa(pIoContext->m_resourceAddr.sin_addr) << "-" << htons(pIoContext->m_resourceAddr.sin_port)
            << "]->数据发送成功【" << pIoContext->m_szBuf << "】" << endl;
        break;
    }
    case NC_RECEIVE:
    {
        SendToIpInfo ipInfo;
        ZeroMemory(&ipInfo, MAX_BUFFER_LEN);
        memcpy(&ipInfo, pIoContext->m_szBuf, MAX_BUFFER_LEN);
        cout << "接收到[" << inet_ntoa(pIoContext->m_resourceAddr.sin_addr) << "-" << htons(pIoContext->m_resourceAddr.sin_port)
            << "]->【" << ipInfo.nSelfID << ":" << ipInfo.nSendToID << ":" << ipInfo.buf << "】" << endl;

        break;
    }
    case NC_RECEIVE_COMPLETE:
    {
        SendToIpInfo ipInfo;
        ZeroMemory(&ipInfo, MAX_BUFFER_LEN);
        memcpy(&ipInfo, pIoContext->m_szBufCache, MAX_BUFFER_LEN);
        cout << "服务器已完整接收到您发的信息[" << inet_ntoa(pIoContext->m_resourceAddr.sin_addr) << "-" << htons(pIoContext->m_resourceAddr.sin_port)
            << "]->【" << ipInfo.nSelfID << ":" << ipInfo.nSendToID << ":" << ipInfo.buf << "】" << endl;

        if (ipInfo.nSendToID <= 0)
        {
            break;
        }

        // 收到后投递一个异步数据
        PER_IO_CONTEXT* pSendIoContext = pIOCP->AllocateClientIOContext();
        pSendIoContext->m_rourceSock = pIoContext->m_rourceSock;
        pSendIoContext->m_resourceAddr = pIoContext->m_resourceAddr;

        pSendIoContext->m_desID = ipInfo.nSendToID;
        memcpy(pSendIoContext->m_szBuf, ipInfo.buf, strlen(ipInfo.buf));
        pSendIoContext->m_dwBytesSend = 0;
        if (pIOCP->PostSend(pSendIoContext, false))
        {
            char szSend[] = "目标地址已成功接收!!!";
            ZeroMemory(pSendIoContext->m_szBuf, MAX_BUFFER_LEN);
            memcpy(pSendIoContext->m_szBuf, szSend, strlen(szSend));
            pSendIoContext->m_dwBytesSend = static_cast<WORD>(strlen(szSend));
            pIOCP->PostSend(pSendIoContext, true);
        }
        else
        {
            //发送给目标地址不成功,服务器回复客户端目标地址IP或Port不正确
            char szSend[] = "接收者ID不存在或已下线!!!";
            ZeroMemory(pSendIoContext->m_szBuf, MAX_BUFFER_LEN);
            memcpy(pSendIoContext->m_szBuf, szSend, strlen(szSend));
            pSendIoContext->m_dwBytesSend = static_cast<WORD>(strlen(szSend));
            pIOCP->PostSend(pSendIoContext, true);
        }

        break;
    }

    default:
        break;
    }
    return;
}

int main()
{
    pIOCP = new CCoreByIOCP;
    pIOCP->RunCore(NotifyProc, PORT);
    char szIn[32];
    while (1)
    {
        printf("输入 quit 退出IOCP服务器: \n");
        memset(szIn, 0, sizeof(szIn));
        scanf_s("%s", szIn, 32);
        if (strcmp(szIn, "quit") == 0)
        {
            pIOCP->StopCore();
            break;
        }
    }

    system("pause");

    return 0;
}


分享 串口类

// Global.h
#pragma once

#define DK_SUCCESS          (0)  // 成功
#define ERR_DK_FAILURE      (-1) // 失败
#define ERR_COM_NO          (-2) // 端口号错误

const int MAXRECVBUF = 4096; // 串口接收缓冲区
const int MAXSENDBUF = 4096; // 串口发送缓冲区

typedef unsigned char  uchar;
// CUART.h
#pragma once

#include "Global.h"

//一般串口的表述是"COM1","COM2",但是openport接口输入的端口都是用0表示COM1,1表示COM2
#define  MAP2PORT(port)   (port + 1)

class CUART
{
public:
    typedef int (*CALLFUNC)(void* pMsg, DWORD dwLength, void* pThis);  //CALLFUNC-回调函数  

    enum EnumPortNum
    {
        COM1,
        COM2,
        COM3,
        COM4,
        COM5
    };

    enum EnumFlowControl     // 串口流控制
    {
        No_FlowControl,      // 无流控制
        Cts_Rts_FlowControl, // 请求发送/清除发送流控制
        Cts_Dtr_FlowControl,
        Dsr_Rts_FlowControl,
        Dsr_Dtr_FlowControl, // 数据终端就绪/数据设置就绪流控制
        Xon_Xoff_FlowControl // 软件流控制
    };

    enum EnumRecvMode
    {
        Call_Func_Mode, // 回调函数模式
        Event_Mode      // 事件驱动模式
    };

public:
    CUART(); // 构造函数
    virtual ~CUART(); // 析构函数
    static unsigned _stdcall RecvThread(void* param); // 接收任务发送线程

public:
    int OpenPort(int iPort, int iRecvMode = Call_Func_Mode); // 打开一个端口,并初始化,默认CallFuncMode,调用时可以省略
    int ConfigPort(int baudRate = 0,
        int parity = 0,
        int dataBits = 3,
        int stopBits = 0,
        EnumFlowControl fc = No_FlowControl
    ); // 配置端口
    void RegisterCallFunc(CALLFUNC callFunc, void* pThis); // 注册函数(回调函数必须为static) 
    int  RecvTaskProc(); // 接收事件消息函数
    int  SendData(LPCVOID pData, DWORD dwLen); // 发送数据
    int  RecvData(LPVOID  pData, DWORD dwLen); // 接收数据
    int  WaitComm(); // 等待串口事件
    void ClosePort(); // 关闭端口
    BOOL IsOpen() const; // 判断端口是否打开
    HANDLE mCommHandle; // 端口句柄

private:
    OVERLAPPED mLSend;           // 端口发送OVERLAPPED结构
    OVERLAPPED mLRecv;           // 端口接收OVERLAPPED结构
    CALLFUNC   mRecvMsgCallFunc; // 接收回调函数指针
    void*      mRegisterPoint;   // 注册模块指针
    int        mRecvMode;        // 接收模式 
    HANDLE     mRecvMsgEvent;    // 接收消息通知事件句柄(回调模式用)
    HANDLE     mRecvThread;      // 接收线程句柄(回调模式用)
    BOOL       mRunThread;       // 接收任务运行标志
};
// UART.cpp
#include "pch.h"
#include "UART.h"
#include <process.h>
#include <iostream>

using namespace std;

/*********************************************************************************************************
* 函数名称: CUART
* 函数功能: 构造函数
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2021年01月22日
* 注    意:
*********************************************************************************************************/
CUART::CUART()
    : mCommHandle(INVALID_HANDLE_VALUE),
    mRecvMsgCallFunc(NULL),
    mRegisterPoint(NULL),
    mRecvMode(Call_Func_Mode),
    mRecvMsgEvent(NULL),
    mRecvThread(NULL),
    mRunThread(FALSE)
{
    ZeroMemory(&mLSend, sizeof(OVERLAPPED));
    ZeroMemory(&mLRecv, sizeof(OVERLAPPED));
}

/*********************************************************************************************************
* 函数名称: CUART
* 函数功能: 析构函数
* 输入参数: void
* 输出参数: void
* 返 回 值: void
*********************************************************************************************************/
CUART::~CUART()
{
    ClosePort(); // 关闭串口

    if(mRecvMsgEvent != NULL)
    {
        CloseHandle(mRecvMsgEvent); // 关闭接收信息通知事件线程句柄
        mRecvMsgEvent = NULL;
    }

    mRunThread = FALSE; // 接收任务运行标志为FALSE

    if(mRecvThread != NULL)
    {
        WaitForSingleObject(mRecvThread, INFINITE); // 等待接收线程函数退出
        CloseHandle(mRecvThread);                   // 关闭接收线程句柄
        mRecvThread = NULL;
    }

    mRecvMsgCallFunc = NULL; // 接收回调函数指针为NULL
    mRegisterPoint = NULL;   // 注册模块指针为NULL
}

/*********************************************************************************************************
* 函数名称: OpenPort
* 函数功能: 打开一个端口,并初始化
* 输入参数: iPort-端口号,iRecvMode-接收模式
* 输出参数: void
* 返 回 值: 成功返回DK_SUCCESS, 失败返回ERR_DK_FAILURE
* 注    意: 创建了一个接收线程,但是并不马上执行;而是挂起到mRecvMsgEvent事件上;
*           必须等调用ConfigPort后才能执行;
*********************************************************************************************************/
int CUART::OpenPort(int iPort, int iRecvMode)
{
    CString sPort;

    if (iPort < 10)
    {
        sPort.Format(_T("COM%d"), iPort);
    }
    else // 扫描串口号超过10的串口
    {
        sPort.Format(_T("\\\\.\\COM%d"), iPort);
    }

    //创建串口句柄,重叠方式
    mCommHandle = CreateFile(sPort,    // 创建串口
        GENERIC_WRITE | GENERIC_READ,  // 使用读/写方式
        0,                             // 不允许共享的设备
        NULL,                          // 安全属性设置
        OPEN_EXISTING,                 // 创建方式,只打开已有串口
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // 使用重叠方式
        NULL                           // 读操作时,指向模板文件句柄
    );

    if(mCommHandle == INVALID_HANDLE_VALUE) // 串口句柄无效的句柄值
    {
        return ERR_DK_FAILURE;
    }

    // 为重叠写创建事件对象,手工设置,初始化为无信号的
    ZeroMemory(&mLSend, sizeof(OVERLAPPED)); // 将指定的内存清零,一般在使用结构前清零
    mLSend.hEvent = CreateEvent(NULL,        // 该句柄不能被继承
        TRUE,                                // 人工重设
        FALSE,                               // 事件初始化状态为无信号状态
        NULL                                 // 指向事件名
    );
    if (mLSend.hEvent == INVALID_HANDLE_VALUE) // mLSend的hEvent为无效的句柄值
    {
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 为重叠读创建事件对象,手工设置,初始化为无信号的
    ZeroMemory(&mLRecv, sizeof(OVERLAPPED)); // 将指定的内存清零,一般在使用结构前清零
    mLRecv.hEvent = CreateEvent(NULL,        // 该句柄不能被继承
        TRUE,                                // 人工重设
        FALSE,                               // 事件初始化状态为无信号状态
        NULL                                 // 指向事件名
    );
    if(mLRecv.hEvent == INVALID_HANDLE_VALUE) // mLRecv的hEvent为无效的句柄值
    {
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 设置事件能用
    // SetCommMask-指定串口允许响应的事件为EV_RXCHAR,即接收到一个字符,并放入接收缓冲区
    if(!SetCommMask(mCommHandle, EV_RXCHAR))
    {
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 设置串口收发缓冲区
    if(!SetupComm(mCommHandle, MAXRECVBUF, MAXSENDBUF)) // 初始化串口的通信参数,0-失败,非零-成功
    {
        // 关闭串口
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 清空串口缓冲区
    if(!PurgeComm(mCommHandle, PURGE_RXABORT | PURGE_TXABORT |
        PURGE_RXCLEAR | PURGE_TXCLEAR))
    {
        // 关闭串口
        ClosePort();
        return ERR_DK_FAILURE;
    }

    COMMTIMEOUTS commtimeouts;

    // 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作
    commtimeouts.ReadIntervalTimeout = MAXDWORD; // 读间隔超时设置为最大
    commtimeouts.ReadTotalTimeoutMultiplier = 0; // 读时间系数为0
    commtimeouts.ReadTotalTimeoutConstant = 0;   // 读时间常量为0

    // 设置写超时以指定WriteComm成员函数中的GetOverlappedResult函数的等待时间
    commtimeouts.WriteTotalTimeoutMultiplier = 50; // 写时间系数为50
    commtimeouts.WriteTotalTimeoutConstant = 200;  // 写时间常数为200
    SetCommTimeouts(mCommHandle, &commtimeouts);   // 设置串口设备读写时的超时参数

    // 设置接收模式
    mRecvMode = iRecvMode;

    switch(mRecvMode)
    {
    case Call_Func_Mode:
        mRecvMsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建接收消息时间句柄,自动重设
        if(mRecvMsgEvent == INVALID_HANDLE_VALUE)
        {
            ClosePort();
            return ERR_DK_FAILURE;
        }

        mRunThread = TRUE; // 接收任务运行标志为TRUE

        mRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvThread, this, 0, NULL); //创建接收线程,返回接收线程句柄
        if(mRecvThread == NULL)
        {
            ClosePort();
            return ERR_DK_FAILURE;
        }
        break;

    case Event_Mode:
        break;
    default:
        break;
    }

    return DK_SUCCESS;
}

/*********************************************************************************************************
* 函数名称: ConfigPort
* 函数功能: 配置端口,并启动接收线程
* 输入参数: 都是根据在ComboBox框中选择的顺序编号,编号从0开始
            baudRate-波特率, parity-奇偶校验,dataBits-端口数据位
*           stopBits-端口停止位数,fc-端口控制流程
* 输出参数: void
* 返 回 值: 成功返回DK_SUCCESS
*********************************************************************************************************/
int CUART::ConfigPort(int baudRate, int parity, int dataBits,
    int stopBits, EnumFlowControl fc)
{
    // 获得端口先前的状态
    DCB dcb; // 定义一个dcb结构存放串口通信参数
    dcb.DCBlength = sizeof(DCB); // DCB结构的大小
    if(!GetCommState(mCommHandle, &dcb)) // 打开串口后,获取串口的初始化配置
    {
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 通过修改DCB结构,来配置串口的各项参数
    // 设置二进制模式
    dcb.fBinary = TRUE;

    // 设置波特率
    switch(baudRate)
    {
    case 0: // 波特率4800
        dcb.BaudRate = 4800;
        break;
    case 1: // 波特率9600
        dcb.BaudRate = 9600;
        break;
    case 2: // 波特率14400
        dcb.BaudRate = 14400;
        break;
    case 3: // 波特率19200
        dcb.BaudRate = 19200;
        break;
    case 4: // 波特率38400
        dcb.BaudRate = 38400;
        break;
    case 5: // 波特率57600
        dcb.BaudRate = 57600;
        break;
    case 6: // 波特率115200
        dcb.BaudRate = 115200;
        break;
    default:
        break;
    }

    // 设置允许奇偶校验
    dcb.fParity = TRUE;

    // 设置奇偶校验
    switch(parity)
    {
    case 0: // 无校验
        dcb.Parity = NOPARITY;
        break;
    case 1: // 奇数校验
        dcb.Parity = ODDPARITY;
        break;
    case 2: // 偶校验
        dcb.Parity = EVENPARITY;
        break;
    case 3: // 标志校验
        dcb.Parity = MARKPARITY;
        break;
    case 4: // 空格校验
        dcb.Parity = SPACEPARITY;
        break;
    default:
        break;
    }

    // 设置端口当前使用的数据位
    // dcb.ByteSize = byDataBits;

    switch(dataBits)
    {
    case 0:
        dcb.ByteSize = 5;
        break;
    case 1:
        dcb.ByteSize = 6;
        break;
    case 2:
        dcb.ByteSize = 7;
        break;
    case 3:
        dcb.ByteSize = 8;
        break;
    default:
        break;
    }

    // 设置端口当前使用的停止数据位
    switch(stopBits)
    {
    case 0: // 1位
        dcb.StopBits = ONESTOPBIT;
        break;
    case 1: // 1.5位
        dcb.StopBits = ONE5STOPBITS;
        break;
    case 2: // 2位
        dcb.StopBits = TWOSTOPBITS;
        break;
    default:
        break;
    }

    // 设定对DTR信号线是否敏感,如果为TRUE,那么DSR为OFF,接收的任何字节将被忽略
    dcb.fDsrSensitivity = FALSE;
    switch(fc)
    {
    case No_FlowControl:
    {
        dcb.fOutxCtsFlow = FALSE;
        dcb.fOutxDsrFlow = FALSE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    }
    case Cts_Rts_FlowControl:
    {
        dcb.fOutxCtsFlow = TRUE;
        dcb.fOutxDsrFlow = FALSE;
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    }
    case Cts_Dtr_FlowControl:
    {
        dcb.fOutxCtsFlow = TRUE;
        dcb.fOutxDsrFlow = FALSE;
        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    }
    case Dsr_Rts_FlowControl:
    {
        dcb.fOutxCtsFlow = FALSE;
        dcb.fOutxDsrFlow = TRUE;
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    }
    case Dsr_Dtr_FlowControl:
    {
        dcb.fOutxCtsFlow = FALSE;
        dcb.fOutxDsrFlow = TRUE;
        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    }
    case Xon_Xoff_FlowControl:
    {
        dcb.fOutxCtsFlow = FALSE;
        dcb.fOutxDsrFlow = FALSE;
        dcb.fOutX = TRUE;
        dcb.fInX = TRUE;
        dcb.XonChar = 0x11;
        dcb.XoffChar = 0x13;
        dcb.XoffLim = 100;
        dcb.XonLim = 100;
        break;
    }
    default:
        break;
    }

    // 重新设置当前端口状态
    if(!SetCommState(mCommHandle, &dcb))
    {
        ClosePort();
        return ERR_DK_FAILURE;
    }

    // 启动接收事件
    if(Call_Func_Mode == mRecvMode)
    {
        SetEvent(mRecvMsgEvent); // 将Event的状态设置为有信号状态
    }
    return DK_SUCCESS;
}

/*********************************************************************************************************
* 函数名称: RegisterCallFunc
* 函数功能: 队列重传子模块注册函数,注册好用户用于处理接收数据的线程函数
* 输入参数: callFunc-注册函数指针,pThis-注册对象指针
* 输出参数: void
* 返 回 值: void
*********************************************************************************************************/
void CUART::RegisterCallFunc(CALLFUNC callFunc, void* pThis)
{
    if (NULL == callFunc || NULL == pThis)
    {
        return;
    }

    mRecvMsgCallFunc = callFunc; // 接收回调函数指针为形参callFunc
    mRegisterPoint = pThis;      // 注册模板函数中位置为形参pThis
}

/*********************************************************************************************************
* 函数名称: RecvThread
* 函数功能: 接收任务发送的线程调用执行的函数地址,名称就表示地址
* 输入参数: pParam-当前对象指针,
* 输出参数: void
* 返 回 值: DK_SUCCESS
*********************************************************************************************************/
unsigned _stdcall CUART::RecvThread(void* param)
{
    CUART* myComm = NULL;

    if (NULL != param)
    {
        myComm = (CUART*)param;
        (void)myComm->RecvTaskProc();
    }
    return DK_SUCCESS;
}

/*********************************************************************************************************
* 函数名称: RecvTaskProc
* 函数功能: 接收事件消息函数
* 输入参数: void
* 输出参数: void
* 返 回 值: 成功返回DK_SUCCESS, 失败返回ERR_DK_FAILURE
*********************************************************************************************************/
int CUART::RecvTaskProc()
{
    COMSTAT ComStat; // 通讯设备参数结构体
    DWORD dwErrorFlags = 0;
    int nError;
    HANDLE hMyComm = NULL;

    // 获取后台读命令的操作结果
    WaitForSingleObject(mRecvMsgEvent, INFINITE); // 使得线程自愿进入等待状态,直到变为已通知状态
    CloseHandle(mRecvMsgEvent);
    mRecvMsgEvent = NULL;

    while(mRunThread) // 接收任务运行标志为TRUE
    {
        if(mCommHandle == INVALID_HANDLE_VALUE)
        {
            break;
        }

        ClearCommError(mCommHandle, &dwErrorFlags, &ComStat); // 清除错误标志

        if(ComStat.cbInQue)
        {
            char szRecvBuf[MAXRECVBUF + 1] = { 0 };

            int len = 0;
            len = RecvData((void*)szRecvBuf, MAXRECVBUF); // 串口接收数据,放在szRecvBuf中

            if(len > 0)
            {
                mRecvMsgCallFunc(szRecvBuf, len, mRegisterPoint); // 回调函数
            }
            continue;
        }

        nError = WaitComm();
        if(ERR_DK_FAILURE == nError)
        {
            break;
        }
    }
    return ERR_DK_FAILURE;
}

/*********************************************************************************************************
* 函数名称: SendData
* 函数功能: 发送数据
* 输入参数: pData-发送的数据指针,dwLen-发送数据长度
* 输出参数: void
* 返 回 值: 成功返回发送成功的字节数, 失败返回ERR_DK_FAILURE
*********************************************************************************************************/
int CUART::SendData(LPCVOID pData, DWORD dwLen)
{
    // 判断入口参数
    if (pData == NULL)
    {
        return ERR_DK_FAILURE;
    }

    BOOL bSuccess;
    DWORD dwErrorFlags;
    COMSTAT Comstat;
    DWORD dwSendCount = 0;
    DWORD dwError;

    ClearCommError(mCommHandle, &dwErrorFlags, &Comstat);

    // 发送数据
    bSuccess = WriteFile(mCommHandle, pData, dwLen, &dwSendCount, &mLSend);
    /*WriteFile函数:
    mCommHandle : 写入文件的句柄
    pData   : 写入文件的数据缓冲区指针
    dwLen   : 写入的字节数
    dwSendCount : 已发送的字节数
    mLSend: 重叠方式使用的结构体
    */

    if (!bSuccess)
    {
        // 获得IO错误码
        dwError = GetLastError();
        if (ERROR_IO_PENDING == dwError) // 表示数据尚未发送完毕
        {
            // 一直等待发送成功
            bSuccess = GetOverlappedResult(mCommHandle, &mLSend, &dwSendCount, TRUE);
            if (!bSuccess)
            {
                return ERR_DK_FAILURE;
            }
        }
        else
        {
            return ERR_DK_FAILURE;
        }
    }
    return dwSendCount;
}

/*********************************************************************************************************
* 函数名称: RecvData
* 函数功能: 接收数据
* 输入参数: pData - 接收的数据指针,dwLen - 接收数据长度
* 输出参数: void
* 返 回 值: 成功返回接收的字节数, 失败返回ERR_DK_FAILURE
*********************************************************************************************************/
int CUART::RecvData(LPVOID  pData, DWORD dwLen)
{
    // 判断入口参数
    if (pData == NULL)
    {
        return ERR_DK_FAILURE;
    }

    BOOL bSuccess = FALSE;
    DWORD dwErrorFlags;
    COMSTAT Comstat;
    DWORD dwRecvCount = 0;
    DWORD dwError;

    ClearCommError(mCommHandle, &dwErrorFlags, &Comstat);
    dwRecvCount = min(dwLen, Comstat.cbInQue); // 取实际接收的数据长度与接收缓冲长度之间的最小值

    // 读事件发生
    bSuccess = ReadFile(mCommHandle, pData, dwLen, &dwRecvCount, &mLRecv);

    if (!bSuccess)
    {
        // 获得IO错误码
        dwError = GetLastError();
        if (ERROR_IO_PENDING == dwError) // 表示数据尚未发送完毕
        {
            // 一直等待接收成功
            bSuccess = GetOverlappedResult(mCommHandle, &mLRecv, &dwRecvCount, TRUE);
            if (!bSuccess)
            {
                return ERR_DK_FAILURE;
            }
        }
        else
        {
            return ERR_DK_FAILURE;
        }
    }
    return dwRecvCount;
}

/*********************************************************************************************************
* 函数名称: WaitComm
* 函数功能: 等待串口事件
* 输入参数: void
* 输出参数: void
* 返 回 值: 成功返回DK_SUCCESS, 失败返回ERR_DK_FAILURE
*********************************************************************************************************/
int CUART::WaitComm()
{
    OVERLAPPED os;
    DWORD dwMark = 0;
    DWORD dwTrans = 0;

    os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if(os.hEvent == NULL)
    {
        return ERR_DK_FAILURE;
    }

    if(!WaitCommEvent(mCommHandle, &dwMark, &os)) // 重叠操作
    {
        if(GetLastError() == ERROR_IO_PENDING)
        {
            // 无限等待重叠操作结果
            GetOverlappedResult(mCommHandle, &os, &dwTrans, TRUE);
        }
        else
        {
            CloseHandle(os.hEvent);
            return ERR_DK_FAILURE;
        }
    }

    CloseHandle(os.hEvent);
    return DK_SUCCESS;
}

/*********************************************************************************************************
* 函数名称: ClosePort
* 函数功能: 关闭端口,同时,串口关闭将引发接收线程结束
* 输入参数: void
* 输出参数: void
* 返 回 值: void
*********************************************************************************************************/
void CUART::ClosePort()
{
    BOOL open = FALSE;

    try
    {
        open = IsOpen();
        mRunThread = FALSE;
        if (TRUE == open)
        {
            // 关闭当前打开的端口句柄
            (void)CloseHandle(mCommHandle);
            mCommHandle = INVALID_HANDLE_VALUE;

            // 关闭当前事件句柄
            if(mLSend.hEvent != NULL)
            {
                CloseHandle(mLSend.hEvent);
            }

            if(mLRecv.hEvent != NULL)
            {
                CloseHandle(mLRecv.hEvent);
            }

            ZeroMemory(&mLSend, sizeof(OVERLAPPED));
            ZeroMemory(&mLRecv, sizeof(OVERLAPPED));
        }
    }
    catch (...)
    {
        cout << "close port failed!" << endl;
    }
}

/*********************************************************************************************************
* 函数名称: IsOpen
* 函数功能: 检查是否已经打开一个端口
* 输入参数: void
* 输出参数: void
* 返 回 值: 成功返回TRUE, 失败返回FALSE
*********************************************************************************************************/
BOOL CUART::IsOpen() const
{
    return (INVALID_HANDLE_VALUE != mCommHandle);
}



涉及到的类

CStdioFile
CFileFind
CFile
CFileStatus
CTime
CTimeSpan

相关函数

CTime::GetCurrentTime
CFile::Open
CFile::Write
CFileFind::FindFile
CFileFind::FindNextFile
CFileFind::IsDirectory
CFileFind::GetFilePath

WideCharToMultiByte
CreateFile
setlocale
remove

char* Unicode2Ansi(const CString& str, int& nSize)
{
    int size = 0;
    char* buf = nullptr;
    try
    {
        size = WideCharToMultiByte(CP_ACP, 0, str, -1, nullptr, 0, nullptr, nullptr);
        buf = new char[size];
        nSize = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, size, nullptr, nullptr);
    }
    catch (...)
    {
        return nullptr;
    }
    return buf;
}

#include <locale.h>
void CxlsTestDlg::OnBnClickedButton1()
{
    // 测试1:CFile方法生成xls文件
    CTime sysTime = CTime::GetCurrentTime();
    CString strTime = sysTime.Format(_T("%Y%m%d%H%M%S"));
    CString FileName = _T("d:/") + strTime + _T(".xls");
    CFile file;
    file.Open(FileName, CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate);
    CString text = _T("型号:\tMCB01-V1.1\r时间:\t2021年11月20号 11:12:30\r条码:""\t43234njreb332322\r\r序号\t压力\n01\t32.3\n02\t40.1\n");

    // unicode转ansi
    int size = 0;
    char* buf = Unicode2Ansi(text, size);
    file.Write(buf, size);
    delete[]buf;
}

CString ExcelName;

void CxlsTestDlg::OnBnClickedButton2()
{
    // 测试2 CStdioFile方法生成xls文件
    CString ProName = _T("BCM-07-01V");
    CString strBarCode = _T("2893XF438200");

    CString m_strWriteData;
    CStdioFile File;
    CTime sysTime = CTime::GetCurrentTime();
    CString strTime = sysTime.Format(_T("%Y%m%d%H%M%S"));
    ExcelName = _T("d:/") + strTime + _T(".xls");

    strTime = sysTime.Format(_T("%Y年%m月%d日 %H:%M:%S"));

    HANDLE hFile = CreateFile(ExcelName, FILE_ALL_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        HRESULT hr = GetLastError();
        if (hr == ERROR_FILE_NOT_FOUND)
        {
            m_strWriteData.Format(_T("程序名:\t%s\r时间:\t%s\r条码:\t%s\r\r序号\t压力\r"), ProName, strTime, strBarCode);

            File.Open(ExcelName, CFile::modeCreate | CFile::modeReadWrite);
            setlocale(LC_ALL, "chs");
            File.WriteString(m_strWriteData);
            setlocale(LC_ALL, "C");
            m_strWriteData = _T("");
            File.Close();
        }
    }

    CloseHandle(hFile);
}

CArray<double> Pizometerdata;

void CxlsTestDlg::OnBnClickedButton3()
{
    // 测试3 在测试2生成的文件中,填充随机数据
    CStdioFile File;
    CString m_strWriteData;
    CString strData;
    int m_idataNum = 100;

    for (int i = 0; i < m_idataNum; i++)
    {
        if (Pizometerdata.GetAt(i) != 0)
        {
            strData.Format(_T("%d\t%f\r"), i, Pizometerdata.GetAt(i));
            m_strWriteData += strData;
        }
        else
        {
            strData.Format(_T("%d\t\r"), i);
            m_strWriteData += strData;
        }
    }

    File.Open(ExcelName, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite);
    File.SeekToEnd();
    File.WriteString(m_strWriteData);
    File.Close();
}


void CxlsTestDlg::OnBnClickedButton4()
{
    // 测试4  生成一些随机数据
    srand(time(nullptr));

    for (int i = 0; i < 100; ++i)
    {
        Pizometerdata.Add((rand() % 100) * 0.01 + (rand() % 30) + 60);
    }
}

#include <vector>
void CxlsTestDlg::OnBnClickedButton5()
{
    // 5 删除过期数据
    std::vector<CString> vFilePathList;

    CFileFind finder;
    BOOL isNotEmpty = finder.FindFile(_T("d:\\*.*"));//总文件夹,开始遍历 
    CFileStatus fileStatus;
    CTime today = CTime::GetCurrentTime();
    CTimeSpan spanTime;

    while (isNotEmpty)
    {
        isNotEmpty = finder.FindNextFile();//查找文件 
        CString filename = finder.GetFilePath();//获取文件的路径,可能是文件夹,可能是文件 
        if (!(finder.IsDirectory()))
        {
            CFile::GetStatus(filename, fileStatus);
            spanTime = today - fileStatus.m_ctime;
            // 如果文件创建时间超过一分钟,被认为是无效文件
            if (spanTime.GetTotalMinutes() > 1)
            {
                // 如果是文件则加入文件列表 
                vFilePathList.push_back(filename);// 将一个文件路径加入容器 
            }
        }
    }

    for (auto str : vFilePathList)
    {
        USES_CONVERSION;

        int size = 0;
        char* pStrName = Unicode2Ansi(str, size);

        if(pStrName)
            remove(pStrName);

        delete []pStrName;
    }
}



#include <io.h>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>

using namespace std;

#pragma region // 从文件夹中读取lib文件
vector<string>batchFetchingLib(string& folder_path)
{
    string filename_suffix = "/*lib"; // lib文件的文件名后缀(.lib)
    vector<string>files;
    struct _finddata_t fileinfo;
    intptr_t handle;
    handle = _findfirst((folder_path + filename_suffix).data(), &fileinfo);
    if (handle == -1)
    {
        printf("输入的路径有错误");
        exit(-1);
    }
    else
    {
        files.push_back(fileinfo.name);

        while (_findnext(handle, &fileinfo) == 0)
        {
            if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
                continue;
            files.push_back(fileinfo.name);
        }
    }
    _findclose(handle);

    return files;
}
#pragma endregion

int main()
{
    string pcl_lib_path("D://PCL 1.11.1//lib");                // 读取lib文件的路径
    string vtk_lib_path("D://PCL 1.11.1//3rdParty//VTK//lib"); // 读取VTK-lib文件的路径

    vector<string>PCL = batchFetchingLib(pcl_lib_path);        // 获取lib文件名
    vector<string>VTK = batchFetchingLib(vtk_lib_path);        // 获取VTK-lib文件名

    ofstream release, debug;// 保存的文件
    release.open("PCL1.11.1_Release附加依赖项.txt", ios::app);
    debug.open("PCL1.11.1_Debug附加依赖项.txt", ios::app);

    for (size_t i = 0; i < PCL.size(); ++i)
    {
        (i % 2 == 0) ? release << PCL[i] << endl : debug << PCL[i] << endl;
    }

    for (size_t i = 0; i < VTK.size(); ++i)
    {
        (i % 2 != 0) ? release << VTK[i] << endl : debug << VTK[i] << endl;
    }

    release.close();
    debug.close();

    cout << "附加依赖项获取完毕!!!" << endl;

    return 0;
}





1.png
2.png
3.png
4.png
5.png
6.png

测试代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <string>
using std::string;

void processInput(GLFWwindow* window);
void cbFramebufferSize(GLFWwindow* window, int width, int height);

void InitOpenGL();
GLFWwindow* CreateOpenGLWindow(int width, int height, const string& title);
bool LoadFunctionPointers();
void RenderLoop(GLFWwindow* window);

int main()
{
    InitOpenGL();

    GLFWwindow* window = CreateOpenGLWindow(800, 600, "Test OpenGL");
    if (!window)
        return -1;

    if (!LoadFunctionPointers())
        return -1;

    RenderLoop(window);

    return 0;
}

void processInput(GLFWwindow* window)
{
    if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE))
        glfwSetWindowShouldClose(window, true);
}

void cbFramebufferSize(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

void InitOpenGL()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
}

GLFWwindow* CreateOpenGLWindow(int width, int height, const string& title)
{
    GLFWwindow* window = glfwCreateWindow(800, 600, title.c_str(), NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return NULL;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, cbFramebufferSize);
    return window;
}

bool LoadFunctionPointers()
{
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        glfwTerminate();
        return false;
    }
    return true;
}

void RenderLoop(GLFWwindow* window)
{
    while (!glfwWindowShouldClose(window))
    {
        processInput(window);

        glClearColor(0.1f, 0.2f, 0.3f, 0.1f);
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
}



@echo off

echo *********************提示********************
echo 自动解析当前运行路径下的文件和文件夹组成,
echo 并将结果存放在当前路径的TXT文档中。
echo *********************************************
echo.

echo ***********************************
echo 1.只打印文件名。
echo 2.打印详细信息。
echo ***********************************
set /p list_config1=请输入参数(1或2):
echo.

echo ***********************************
echo 1.遍历所有文件和文件夹(仅当前目录)。
echo 2.遍历所有文件和文件夹(包括子文件夹)。
echo ***********************************
set /p list_config2=请输入参数(1或2):
echo.

if %list_config1%==1 (
    if %list_config2%==1 (
        dir %cd% /b > .\1.当前路径的文件名.txt
        echo 已生成文件:%cd%\1.当前路径的文件名.txt
        echo.
    )
    if %list_config2%==2 (
        dir %cd% /b/s > .\2.当前路径和子文件夹的文件名.txt
        echo 已生成文件:%cd%\2.当前路径和子文件夹的文件名.txt
        echo.
    )
)

if %list_config1%==2 (
    if %list_config2%==1 (
        dir %cd% > .\3.当前路径的文件详细信息.txt
        echo 已生成文件:%cd%\3.当前路径的文件详细信息.txt
        echo.
    )
    if %list_config2%==2 (
        dir %cd% /s > .\4.当前路径和子文件夹的文件详细信息.txt
        echo 已生成文件:%cd%\4.当前路径和子文件夹的文件详细信息.txt
        echo.
    )
)

::加“/b”表示只记录文件名,不显示详细信息
::加“/s”表示递归查看到子文件夹

pause