• WINDOWS编程手册


    24. 计时器

    Windows高精度计时器,误差<1us

    LARGE_INTEGER liTimeStart, liTimeEnd, liTimeElapse;
    LARGE_INTEGER liFrequency;
    
    /*置计时时间*/
    QueryPerformanceFrequency(&liFrequency);
    QueryPerformanceCounter(&liTimeStart);
    
    for(int i = 0; i != 10; ++i)
    	;
    
    /*计算时间间隔*/
    QueryPerformanceCounter(&liTimeEnd);
    liTimeElapse.QuadPart = liTimeEnd.QuadPart - liTimeStart.QuadPart;
    double dTimeElapse = static_cast<double>(liTimeElapse.QuadPart) / liFrequency.QuadPart;

    函数:

            BOOL WINAPI QueryPerformanceFrequency(
                  LARGE_INTEGER *lpFrequency
            );

    功能:获取频率,即系统1秒钟滴答多少下

    参数:LARGE_INTEGER型指针,出参,成员QuadPart即结果

    返回值:成功返回非0,失败返回0,GetLastError()获取错误码


    函数:

            BOOL WINAPI QueryPerformanceCounter(
                  LARGE_INTEGER *lpPerformanceCount
            );

    功能:获取自启动以来系统一共滴答了多少下

    参数:LARGE_INTEGER型指针,出参,成员QuadPart即结果

    返回值:成功返回非0,失败返回0,GetLastError()获取错误码


    23. 查看操作系统位数

    cmd打开命令提示符窗口,键入“systeminfo”


    查看“系统类型”


    x86-based PC: 32位操作系统

    x64-based PC: 64位操作系统

     

    22. 编程技巧

    把所有的closesocket(s)放在一个函数里面:在连接异常断开时,在该函数中添加断点,方便定位。


    21. 空格和水平制表符

    空白字符:空格和水平制表符,ASCII码:空格-32; -9
    str = "空格 + ",下面的结果是9 9, str = " + 空格",结果是9 32。
    所以有公式:空格 + = ; + 空格 = + 空格
    总结下来就是“先水平制表符后空格”

    std::string str = "	 ";
    for(int i = 0; i < str.length(); i++)
    	printf("%d
    ", str[i]);

    20.共享内存


    创建共享内存

        函数

            HANDLE WINAPI CreateFileMapping(
                      HANDLE hFile,
                      LPSECURITY_ATTRIBUTES lpAttributes,
                      DWORD flProtect,
                      DWORD dwMaximumSizeHigh,
                      DWORD dwMaximumSizeLow,
                      LPCTSTR lpName
            );

        调用

            #define SHARED_MEMORY_SIZE 1024
            HANDLE hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, SHARED_MEMORY_SIZE, "NameSharedMemory");

        功能

            若NameSharedMemory不存在,则创建NameSharedMemory共享内存。创建后系统默认全置0

            若NameSharedMemory已存在,则打开NameSharedMemory共享内存。置GetLastError()=ERROR_ALREADY_EXISTS。

        返回值

            成功返回共享内存句柄,失败返回NULL。

        使用方式

    HANDLE hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, SHARED_MEMORY_SIZE, "Name");
    if(hSharedMemory == NULL)
    {
        printf("CreateFileMapping == NULL, ErrorCode=%d
    ", GetLastError());
        return false;
    }
    if(ERROR_ALREADY_EXISTS == GetLastError())
    {
        printf("CreateFileMapping Ok, GetLastError == ERROR_ALREADY_EXISTS, Name Already Exist
    ");
    }
    例子

    进程A创建共享内存,读写数据;进程B打开共享内存,读写数据

    公有结构体定义

    #define SHARED_MEMORY_SIZE 1024
    
    typedef struct
    {
            bool bFlagLive;
            char cObjectName[400];
            char cObjectPathName[400];
    }MemBlock;

    A main.cpp

        /*创建共享内存*/
        HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, "Global\TestSharedMemory");
        if (hMapFile == NULL)
        {
            std::cout<<"CreateFileMapping == NULL, ErrorCode="<<GetLastError()<<std::endl;
            return 0;
        }
    
        std::string sObjectName = "他想让你\sObjectName";
        std::string sObjectPathName = "他想让你\sObjectPathName";
    
        /*获取共享内存地址*/
        MemBlock *pMemBlock = (MemBlock*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
        if (pMemBlock == NULL)
        {
            std::cout<<"MapViewOfFile == NULL, ErrorCode="<<GetLastError()<<std::endl;
            CloseHandle(hMapFile);
            return 0;
        }
    	
        /*写共享内存*/
        strcpy(pMemBlock->cObjectName, sObjectName.c_str());
        strcpy(pMemBlock->cObjectPathName, sObjectPathName.c_str());
    
        InstallService();
        StartService2();
    
        /*定期修改共享内存*/
        while(1)
        {
            pMemBlock->bFlag = true;
            Sleep(3*1000);
        }
    B main.cpp

        /*打开共享内存*/
        HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, "Global\TestSharedMemory");
        if (hMapFile == NULL)
        {
            std::cout<<"CreateFileMapping == NULL, ErrorCode="<<GetLastError()<<std::endl;
            return 0;
        }
    
        /*获取共享内存地址*/
        MemBlock *pMemBlock = (MemBlock*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
        if (pMemBlock == NULL)
        {
            std::cout<<"MapViewOfFile == NULL, ErrorCode="<<GetLastError()<<std::endl;
            CloseHandle(hMapFile);
            return 0;
        }
    	
        /*读共享内存*/
        std::string sObjectName = pMemBlock->cObjectName;
        std::string sObjectPathName = pMemBlock->cObjectPathName;
    
        /*定期读共享内存*/
        while(1)
        {
            bool bFlag = pMemBlock->bFlag;
    
            /*修改标志位*/
            pMemBlock->bFlag = false;
    
            Sleep(3*1000);
        }

    遇到的问题

        a.win7下用户进程创建共享内存,服务程序读共享内存失败,原因Session 0隔离

                在Windows Vista之后的操作系统中,服务进程运行于Session 0之中。用户进程运行在用户登陆到系统后创建的Session 0之后的Session中,例如第一个登陆的用户在Session 1中,第二个登陆的用户在Session 2中,以此类推。

                这便造成用户进程与服务进程隔离开来。

                事实上运行在的不同Session中,如果没有放入全局命名空间(并且设置了相应的访问控制配置),是不能共享Kernel对象的。

                综上,确保跨Session访问的Kernel对象是以Global开头,这意味着它们是属于全局Session命名空间中的。

        b.共享内存结构应使用C struct,例:char cObjectName[400]改为std::string sObjectName则出现汉字乱码。

    附加知识

        a. 共享内存用于多进程数据交互,一进程调用CreateFileMapping创建共享内存;其他进程调用OpenFileMapping打开共享内存。

        b. 当最后一个进程退出或CloseHandle(hSharedMemory)关闭共享内存,系统才会释放该内存


    19. 头文件引用顺序错误


    #include <WinSock2.h>
    #include <Windows.h>

    #pragma comment(lib, "Ws2_32.lib")

    Socket编程头文件和库文件标准引用格式。

    #include <Windows.h>在#include <WinSock2.h>之前则报上述错误。


    18.Windows定时器


    A. 创建

    函数原型

            BOOL WINAPI CreateTimerQueueTimer(

                    PHANDLE phNewTimer, 

                    HANDLE TimerQueue,  

                    WAITORTIMERCALLBACK Callback, 

                    PVOID Parameter, 

                    DWORD DueTime, 

                    DWORD Period, 

                    ULONG Flags

            );

    功能:创建定时器队列定时器

    参数

            PHANDLE phNewTimer:出参,类型HANDLE指针,储存生成的Timer句柄

            HANDLE TimerQueue:TimerQueue句柄,若为NULL,则生成的Timer关联到默认TimerQueue

            WAITORTIMERCALLBACK Callback:Timer回调函数,原型void CALLBACK TimerRoutine(PVOID arg, BOOLEAN TimeOrWaitFired);

            PVOID Parameter:回调函数参数

            DWORD DueTime:DueTime毫秒后触发

            DWORD Period:之后每Period毫秒触发一次

            ULONG Flags:暂默认0

    返回值:失败返回0,错误码GetLastError();成功非0

    例:创建Timer,句柄由hTimer返回,3秒后触发,间隔3秒,触发后执行执行TimerRoutine

    HANDLE hTimer = NULL;
    if(!CreateTimerQueueTimer(&hTimer, NULL, TimerRoutine, NULL, 3000, 3000, 0))
    {
            std::cout<<"CreateTimerQueueTimer == 0, ErrorCode="<<GetLastError()<<std::endl;
    }

    B. 修改

    函数原型

            BOOL WINAPI ChangeTimerQueueTimer(

                    HANDLE TimerQueue, 

                    HANDLE Timer, 

                    ULONG DueTime, 

                    ULONG Period

            );

    功能:修改Timer时间

    参数

            HANDLE TimerQueue:TimerQueue句柄

            HANDLE Timer:Timer句柄

            ULONG DueTime:DueTime毫秒后触发

            ULONG Period:之后每Period毫秒触发一次

    返回值失败返回0,错误码GetLastError();成功非0

    例:将hTimer定时器改为7秒后触发,间隔1秒

    if(!ChangeTimerQueueTimer(NULL, hTimer, 7000, 1000))
    {
            std::cout<<"ChangeTimerQueueTimer == 0, ErrorCode="<<GetLastError()<<std::endl;
    }


    C. 删除

    函数原型:

            BOOL WINAPI DeleteTimerQueueTimer(

                    HANDLE TimerQueue,
                    HANDLE Timer,
                    HANDLE CompletionEvent
            );

    参数:

            HANDLE TimerQueue:TimerQueue句柄,若Timer由默认Timer Queue创建,则此参数应为NULL

            HANDLE Timer:Timer句柄

            HANDLE CompletionEvent:若为INVALID_HANDLE_VALUE,函数等待Timer回调结束方才返回

    功能:删除Timer

    返回值:成功返回非0,失败返回0并置错误码

    附加ULONG Period = 0,则Timer只触发一次


    完整代码

    void CALLBACK TimerRoutine(PVOID arg, BOOLEAN TimeOrWaitFired)
    {
    	std::cout<<"TimerRoutine Fired"<<std::endl;
    }
    
    unsigned int __stdcall Func(void *arg)
    {
    	HANDLE *phTimer = static_cast<HANDLE*>(arg);
    
    	if(!CreateTimerQueueTimer(phTimer, NULL, TimerRoutine, NULL, 3000, 3000, 0))
    	{
    		std::cout<<"CreateTimerQueueTimer == 0, ErrorCode="<<GetLastError()<<std::endl;
    	}
    
    	_endthreadex(0);
    	return 0;
    }
    
    unsigned int __stdcall Func3(void *arg)
    {
    	HANDLE hTimer = static_cast<HANDLE>(arg);
    
    	if(!ChangeTimerQueueTimer(NULL, hTimer, 7000, 1000))
    	{
    		std::cout<<"ChangeTimerQueueTimer == 0, ErrorCode="<<GetLastError()<<std::endl;
    	}
    
    	_endthreadex(0);
    	return 0;
    }
    
    int main()
    {
    	HANDLE hTimer = NULL;
    	_beginthreadex(NULL, 0, Func, &hTimer, 0, NULL);
    
    	std::cin.get();
    
    	_beginthreadex(NULL, 0, Func3, hTimer, 0, NULL);
    
    	std::cin.get();
    	return 0;
    }

    17.EOF文件结束符

    Windows下CTRL+Z代表EOF


    16.closesocket

    a.对刚创建却未连接的SOCKET s调用closesock(s),没有任何问题。

    b.对初始化过的SOCKET s重复调用closesocket(s),没有任何问题。

    SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
    closesocket(s);
    closesocket(s);
    std::cout<<"Ok"<<std::endl;

    输出:Ok


    15.网络抓包wireshark

    15.1

     

    15.2 本机抓包

    在进行通信开发的过程中,我们往往会把本机既作为客户端又作为服务器端来调试代码,使得本机自己和自己通信。但是wireshark此时是无法抓取到数据包的,需要通过简单的设置才可以。

          具体方法如下:

          ①:以管理员身份运行cmd

          ②:route add 本机ip mask 255.255.255.255 网关ip

          for example route add 192.168.1.103 mask 255.255.255.255 192.168.1.1 metric 1

         使用完毕后用 route delete 192.168.1.103 mask 255.255.255.255 192.168.1.1 metric 1删除,否则所有本机报文都经过网卡出去走一圈回来很耗性能

          此时再利用wireshark进行抓包便可以抓到本机自己同自己的通信包,这样配置的原因是将发往本机的包发送到网关,而此时wireshark可以捕获到网卡驱动的报文实现抓包。


    15.3 条件设置

    ip.src==192.168.5.3

    ip.dst==192.168.4.18

    tcp.port==4444

    ip.src==192.168.5.3 and tcp.port==4444

     

    14.sockaddrsockaddr_in

    概要:

            1.一开始设置ip和port用的是sockaddr_in

            2.最后执行动作的套接字函数的参数类型均为*sockaddr

    综述:

            sockaddr_in addrin;

            addrin.sin_family = AF_INET; 

            addrin.sin_addr.s_addr = inet_addr("192.168.15.14")

            addrin.sin_port = htons(4444);


            socket_func(...(sockaddr*)&addrin,......)

    知识点:

            1.sizeof(sockaddr_in) == sizeof(sockaddr)

            2.相互转化方式memcpy(&addr_in, &addr, sizeof(addr));

    附加:

            1.绑定本机所有地址标准形式:addrin.sin_addr.s_addr = htonl(INADDR_ANY);

            2.绑定特定地址标准形式:        addrin.sin_addr.s_addr = inet_addr("192.168.4.118");


    13.非阻塞connect

    //设置socket s非阻塞
    unsigned long ul = 1;
    ioctlsocket(s, FIONBIO, &ul);
    
    int ret = connect(s, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    
    //成功
    if(0 == ret)
    {
    	//恢复s为阻塞
    	ul = 0;
    	ioctlsocket(s, FIONBIO, &ul);
    	return true;
    }
    
    //出错
    if(SOCKET_ERROR == ret && GetLastError() != WSAEWOULDBLOCK)
    {
    	closesocket(s);
    	s = INVALID_SOCKET;
    	return false;
    }
    
    //进行中
    if(SOCKET_ERROR == ret && WSAEWOULDBLOCK == GetLastError())
    {
    	timeval tm;
    	tm.tv_sec = 0;
    	tm.tv_usec = 300;
    
    	fd_set set;
    	FD_ZERO(&set);
    	FD_SET(s, &set);
    
    	//s可写代表connect成功
    	ret = select(0, NULL, &set, NULL, &tm);
    	if(SOCKET_ERROR == ret)
    	{
    		std::cout<<"Select Error: "<<GetLastError()<<std::endl;
    		closesocket(s);
    		s = INVALID_SOCKET;
    		return false;
    	}
    
    	if(0 == ret)
    	{
    		std::cout<<"Connect TimeOut"<<std::endl;
    		closesocket(s);
    		s = INVALID_SOCKET;
    		return false;
    	}
    
    	ul = 0;
    	ioctlsocket(s, FIONBIO, &ul);
    	return true;
    }

    12.socket设置非阻塞

    设置为非阻塞:

            unsigned long ul = 1;
            int ret = ioctlsocket(s, FIONBIO, &ul);

    设置为阻塞:

            unsigned long ul = 0;
            int ret = ioctlsocket(s, FIONBIO, &ul);

    返回值:

            if(SOCKET_ERROR == ret)
            {
                    std::cout<<"ioctlsocket Error : "<<GetLastError()<<std::endl;
                    closesocket(s);
                    s = INVALID_SOCKET_VALUE;
                    return false;
            }

    11.semaphore信号量

    创建:

            HANDLE semaphore = CreateSemaphore(NULL, 0, num, NULL);

            参数1:安全属性,NULL即可

            参数2:初始计数值值,必须大于等于0

            参数3:最大计数值

            参数4:名字

            失败返回NULL

    使用:

            WaitForSingleObject(semaphore, INFINITE);

            semaphore计数值大于0,则计数值减一

            semaphore计数值等于0,则线程阻塞

    释放:

            ReleaseSemaphore(semaphore, size, 0);

            将semaphore计数值加size


    10.线程

    创建线程:

            HANDLE h = (HANDLE)_beginthreadex(NULL, 0, F, arg, 0, NULL) 失败返回0

    线程函数:

            unsigned int __stdcall F(void *arg)
            {
                    ..............
                    _endthreadex(0);
                    return(0);
            }

    等待子线程退出:

            std::vector<HANDLE> vec;
            for(int i = 0; i < vec.size(); ++i)
                    WaitForSingleObject(vec[i], INFINITE);

    9.引用成员变量

    初始化

    class A
    {
    public:
    	A(ClassName &obj) : object(obj)
    	{
    
    	}
    
    private:
    	ClassName &object; 
    }

    8.UTC到当前时刻经过的秒数

    UTC,世界标准时间,即1970年1月1日 00:00:00

    #include <time.h>
    
    time_t t = time(NULL);
    
    time_t类型long long



    7.静态成员变量

    class A
    {
    public:
    	A(){std::cout<<"nihao"<<std::endl;}
    	void insert(std::string str, time_t t);
    	void output();//遍历输出map
    private:
    	static std::map<std::string, time_t> map;
    };

    int main()
    {
    	A a1;
    	a1.insert("123", time(NULL));
    	A a2;
    	a2.insert("456", time(NULL));
    	A a3;
    	a3.insert("789", time(NULL));
    	a3.insert("123", 12);//注意:key存在则不插入,不会进行值覆盖
    	A a4;
    	a4.output();
    }
    结果


    综上:

    1.类对象在创建时均调用构造函数,无论局部变量和new

    2.静态成员变量为所有类对象共享且仅此一份


    静态成员变量初始化

    在cpp文件开头:类型 类名::静态成员变量 (= 初始值)

    #include "devOnlineInfo.h"
    
    bool DevOnlineInfo::flag = false;
    HANDLE DevOnlineInfo::hthread = INVALID_HANDLE_VALUE;
    int DevOnlineInfo::timeout = 0;
    HANDLE DevOnlineInfo::mutex = CreateMutex(NULL, false, "");
    std::map<std::string, bool> DevOnlineInfo::mapDevOnlineInfo;
    
    DevOnlineInfo::DevOnlineInfo()
    {
    
    }
    ..............
    


    6. 生成GUID

    #include <string>
    #include <objbase.h>
    
    std::string Guid()
    {
    	char buf[33] = {0};
    	GUID guid;
    	if (S_OK == ::CoCreateGuid(&guid))
    	{
    		_snprintf(buf, sizeof(buf)
    			, "%08X%04X%04x%02X%02X%02X%02X%02X%02X%02X%02X"
    			, guid.Data1
    			, guid.Data2
    			, guid.Data3
    			, guid.Data4[0], guid.Data4[1]
    		, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]
    		, guid.Data4[6], guid.Data4[7]
    		);
    	}
    
    	std::string str = buf;
    	return str;
    }


    5. 工具函数

    工程中创建utils.h文件

    #ifndef UTILS_H
    #define UTILS_H
    
    #include <string>
    #include <objbase.h>
    
    namespace utils
    {
    	static inline std::string Func1()
    	{
    		std::string str = "loser";
    		return str;
    	}
    
    	static inline void Func2()
    	{
    		//
    		//
    	}
    }
    
    #endif


    4. HANDLESOCKET初始值

    HANDLE h = INVALID_HANDLE_VALUE;

    SOCKET s = INVALID_SOCKET;



    CloseHandle关闭线程句柄


    3.  CloseHandle对参数的影响

    int main()
    {
    	HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, NULL);
    
    	std::cout<<h<<std::endl;
    	std::cout<<CloseHandle(h)<<std::endl;
    
    	Sleep(1000);
    	std::cout<<h<<std::endl;
    
    	std::cin.get();
    	return 0;
    }
    结果


    返回1,关闭线程句柄成功。

    综上CloseHandle调用前后参数h值并无改变,则CloseHandle后需h = INVALID_HANDLE_VALUE。

    由CloseHandle参数非引用该能推出参数无变化。


    2. CloseHandle对线程的影响 1

    unsigned int __stdcall ThreadFunc(void *arg)
    {
    	while(1)
    	{
    		Sleep(1000);
    		std::cout<<"woshule"<<std::endl;
    	}
    
    	_endthreadex(0);
    	return 0;
    }
    
    int main()
    {
    	HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, NULL);
    
    	Sleep(1000);
    	std::cout<<CloseHandle(h)<<std::endl;
    
    	std::cin.get();
    	return 0;
    }
    结果


     
    CloseHandle成功返回非0,失败返回0、GetLastError()查看错误码。

    返回1,关闭线程句柄成功。


    综上CloseHandle
    功能:关闭句柄
    实质:资源引用计数减1
    影响:与线程运行无关 (即便引用计数减到0,线程都不会退出)
    引深:实现线程控制还需另辟蹊径 (如用控制标志bool flag来控制线程退出)
    追述:打开的句柄即资源、资源则需关闭、否则发生泄露


    CloseHandle对线程的影响 2

    unsigned int __stdcall ThreadFunc(void *arg)
    {
    	while(true)
    	{
    		std::cout<<"nixingfuma"<<std::endl;
    		Sleep(1000);
    	}
    
    	_endthreadex(0);
    	return 0;
    }
    
    void CloseThreadHandle()
    {
    	HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, NULL);
    	Sleep(2000);
    	std::cout<<CloseHandle(h)<<std::endl;
    }
    
    int main()
    {
    	CloseThreadHandle();
    	std::cin.get();
    	return 0;
    }
    结果


    综上子函数内创建并CloseHandle线程,函数退出,线程不销毁



    1. windows查看端口占用命令


    任务管理器选"进程"-"查看"-"选择列"-勾选"PID"

    进入windows命令提示符窗口,输入netstat -ano

    即可看到所有连接的PID


    应用实例:

    查看80号端口占用

    C:>netstat -ano|findstr "80" 

    Proto   Local Address          Foreign Address        State          PID 
    TCP     127.0.0.1:80            0.0.0.0:0              LISTENING     2448 
    80号端口被2448号进程占用,继续
    C:>tasklist|findstr "2448" 
    thread.exe                    2016 Console                0    16,064 K 
    至此完结,thread.exe占用80号端口、且在80号端口监听连接。
    如果第二步查不到,就查任务管理器,找2448号进程


  • 相关阅读:
    典型用户模版和场景
    第一冲刺阶段——个人工作总结05
    第一冲刺阶段——个人工作总结04
    第一冲刺阶段——个人工作总结03
    第一冲刺阶段——个人工作总结02
    第一冲刺阶段——个人工作总结01
    学习进度条7
    构建之法阅读笔记06
    个人总结
    第十六周进度条
  • 原文地址:https://www.cnblogs.com/chaikefusibushiji/p/7475638.html
Copyright © 2020-2023  润新知