0%

Windows系统编程01

从整体上看待这个技术

Windows编程基础知识

VA的安装以及常用快捷键

​ ·快捷键

- ALT + G 调到定义
- ALT + SHIFT + F 查找所有引用
- ALT + 左箭头/右箭头:回退/前进
- 批量注释

Win32 窗口程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// 1. 掌握C++ 面向对象的思想,   2,理解消息机制  3.多态性
#include <windows.h>
#include <stdio.h>
LPCTSTR clsName = "My";
LPCTSTR msgName = "欢迎学习";
// 回调函数
LRESULT CALLBACK MyWinProc(
HWND hwnd, // 句柄
UINT uMsg, // 消息 提供的消息列表
WPARAM wParam, //其他消息信息 word
LPARAM lParam // 其他消息信息 long
);
// a. 设计一个窗口类 b. 注册窗口类 c. 创建窗口 d. 显示以及更新窗口
// e. 循环等待消息
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nShowCmd
)
{
/*UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;*/

//a 设计一个窗口类
// 1. 定义和配置窗口对象
WNDCLASS wndcls;
wndcls.cbClsExtra = NULL;
wndcls.cbWndExtra = NULL;
// 窗口的背景颜色
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
// 光标
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
// 图标
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInstance;
// 定义交互响应
wndcls.lpfnWndProc = MyWinProc; // 回调
// 定义窗口代号
wndcls.lpszClassName = clsName;
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;

//b 注册窗口类
RegisterClass(&wndcls);

//c 创建窗口
HWND hwnd;
// WS_OVERLAPPED 只是创建一个重叠窗口 本身没有最大化、 最小化、关闭按钮
hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
//d 显示和刷新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);

//e. 消息循环 GetMessage 只有在接收到WM_QUIT才会返回0
// TranslateMessage翻译消息 WM_KEYDOWN和WM_KEYUP合并为WM_CHAR
// 得到消息 翻译消息 分派消息
MSG msg;
while (GetMessage(&msg,NULL,NULL,NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

LRESULT CALLBACK MyWinProc(
HWND hwnd, // 句柄
UINT uMsg, // 消息 提供的消息列表
WPARAM wParam, //其他消息信息 word
LPARAM lParam // 其他消息信息 long
)
{
HDC hdc;
// uMsg 消息类型
int ret;
switch (uMsg)
{
case WM_CHAR:
char szChar[20];
sprintf_s(szChar,"您刚才按下了: %c", wParam);
MessageBox(hwnd,szChar,"char",NULL);
break;

case WM_LBUTTONDOWN:
MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
break;

case WM_PAINT:
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
EndPaint(hwnd, &ps);
MessageBox(hwnd, "重绘", "msg", NULL);
break;

//点击关闭直接break 没有返回值,注释掉 执行default
case WM_CLOSE:
ret = MessageBox(hwnd, "是否真的结束", "msg", MB_YESNO);
if (ret == IDYES)
{
DestroyWindow(hwnd);
}
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

程序骨架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  int WinMain(){
// 设计窗口外观及交互响应,注册,申请专利
RegisterClass(...)
// 生产窗口
CreateWindow(...)
// 展示窗口
ShowWindow(...)
// 粉刷窗口
UpdateWindow(...)
// 进入消息循环
while (GetMessage(...)) {
// 消息转换
TranslateMessage(...);
// 消息分发
DispatchMessage(...);
}
}

窗口与句柄/窗口类对象

C++窗口类对象

CWnd:MFC库中用于表示窗口的基类。它封装了Windows API 中与窗口有关的操作,提供了一系列成员函数来创建、操作和管理窗口。

C++ 窗口类对象与窗口并不是一回事,它们之间惟一的关系是 C++ 窗口类对象内部定义了一个窗口句柄变量,保存了与这个 C++ 窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束。但C++窗口类对象销毁时,与之相关的窗口也将销毁。

生命周期 :窗口类对象周期 》 窗口 2、窗口类内部定义了m_wnd。

Windows网络编程

socket 概念

网络数据传输用的软件设备。它可以看作是网络通信的端点,应用程序可以通过 Socket 在网络上发送和接收数据,实现不同主机之间的通信。

Socket 的类型

  • 流式 Socket(TCP Socket)
    • 基于 TCP 协议,提供可靠的、面向连接的字节流服务。
    • 数据传输过程中,TCP 协议会保证数据的完整性、顺序性和可靠性。
    • 适用于需要可靠数据传输的应用,如文件传输、网页浏览等。
  • 数据报 Socket(UDP Socket)
    • 基于 UDP 协议,提供不可靠的、无连接的数据报服务。
    • 数据传输过程中,UDP 协议不保证数据的完整性、顺序性和可靠性。
    • 适用于对实时性要求较高,但对数据可靠性要求不高的应用,如视频流、在线游戏等。

C/S模式

面向连接与面向消息(TCP/UDP)

地址和端口

套接字类型与协议设置

SOCK_STREAM[流套接字] TCP

​ 面向连接、可靠的数据传输 适合传输大量的数据,不支持广播、多播

SOCK_DGRAM[数据包套接字] UDP

​ 无连接 支持广播、多播

SOCK_RAW[原始套接字]

​ 可以读写内核没有处理的IP数据报

避开TCP/IP处理机制,被传送的数据报可以被直接传送给需要它的的应用程序

-引用头文件winsock2.h
- 导入ws2_32.lib库
- window下socket编程都要首先进行Winsock的初始化

函数

TCP套接字

服务器端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>

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

int main()
{
printf("Server\n");
//1 初始化网络库
// 加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD(2, 2);
// 1、初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 2 安装电话机
// 新建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockSrv)
{
printf("socket errorNum = %d\n", GetLastError());
return -1;
}
//给变量配置电话号码 IP 任何 端口6000
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
// 3 分配电话号码
// 绑定套接字到本地IP地址,端口号6000
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("bind errorNum = %d\n", GetLastError());
return -1;
}

// 4、监听 listen
if (SOCKET_ERROR == listen(sockSrv, 5))
{
printf("listen errorNum = %d\n", GetLastError());
return -1;
}

// 5、拿起话筒,准备通话
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);

while (TRUE)
{
//6、分配一台分机去服务
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
char sendBuf[100] = { 0 };
sprintf_s(sendBuf, 100, "Welcome %s to bingo!", inet_ntoa(addrCli.sin_addr));
//发送数据
int iLen = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
if (iLen < 0)
{
printf("send errorNum = %d\n", GetLastError());
return -1;
}
char recvBuf[100] = {0};
//接收数据
iLen = recv(sockConn, recvBuf, 100, 0);
if (iLen < 0)
{
printf("recv errorNum = %d\n", GetLastError());
return -1;
}
//打印接收的数据
printf("recvBuf = %s\n", recvBuf);
closesocket(sockConn);
}
//7 关闭总机
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>

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

int main()
{
printf("Client\n");
char sendBuf[] = "hello,world";
//1 初始化网络库
// 加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD(2, 2);
// 1、初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 2 安装电话机
// 新建套接字
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockCli)
{
printf("socket errorNum = %d\n", GetLastError());
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.8.253");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);

// 3 连接服务器
if (SOCKET_ERROR == connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("connect errorNum = %d\n", GetLastError());
return -1;
}
// 4 接收和发送数据
char recvBuf[100] = {0};
int iLen = recv(sockCli, recvBuf, 100, 0);
if (iLen < 0)
{
printf("recv errorNum = %d\n", GetLastError());
return -1;
}
printf("Client recvBuf = %s\n", recvBuf);

// 发送数据
iLen = send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
if (iLen < 0)
{
printf("send errorNum = %d\n", GetLastError());
return -1;
}
// 关闭套接字
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}

UDP套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 服务端
#include <WinSock2.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")
int main()
{
// 初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;

wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
{
return err;
}

if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}

// 创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6001);

// 绑定套接字
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

// 等待并接收数据
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR_IN);

char recvBuf[100];
char sendBuf[100];
while (true)
{
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
std::cout << recvBuf << std::endl;

sprintf_s(sendBuf, 100, "Ack %s", recvBuf);
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
}

closesocket(sockSrv);
WSACleanup();

system("pause");
return 0;
}


// 客户端
#include <WinSock2.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")
int main()
{
// 加载套接字库

WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}

// 创建套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.25.128");
addrSrv.sin_port = htons(6001);
addrSrv.sin_family = AF_INET;

int len = sizeof(SOCKADDR);
char sendBuf[] = "hello";
char recvBuf[100];

//发送数据
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)& addrSrv, len);

recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)& addrSrv, &len);

std::cout << recvBuf << std::endl;

closesocket(sockCli);

system("pause");
return 0;
}

TCP和UDP的总结

UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制
连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信
传输方式 面向报文 面向字节流
适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输