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号进程


原文地址:https://www.cnblogs.com/chaikefusibushiji/p/6775794.html