windows系统编程2——内存管理和网络

编程入门 行业动态 更新时间:2024-10-07 12:29:36

windows系统编程2——<a href=https://www.elefans.com/category/jswz/34/1765129.html style=内存管理和网络"/>

windows系统编程2——内存管理和网络

学习视频链接

06网络的相关概念_哔哩哔哩_bilibili=6&vd_source=0471cde1c644648fafd07b54e303c905

目录

一、内存管理

1.1 概念

1.2 内存分区

二、网络基础

2.1 IP地址 

2.2 网络分层

2.3 网络程序寻址方式

2.4 TCP协议

2.5 TCP服务器/客户端创建过程

2.6 UDP服务器/客户端创建过程

三、测试 TCP

3.1 测试连接

3.2 测试收发数据

3.3 多线程服务器连接多客户端

3.4 select服务器


一、内存管理

1.1 概念

1、进程的虚拟地址空间

对于32位进程来说,这个地址空间是 4GB,因为 32 位指针可以拥有从 0x00000000 至
0xFFFFFFF 之间的任何一个值。这使得一个指针能够拥有 4,294,967,296 个值中的一个值,它覆盖了一个进程的 4GB 虛拟空间的范围。对于 64 位进程来说,这个地址空间是 16EB (1TB = 1024GB, 1PB = 1024TB, 1EB = 1024PB) ,因为 64 位指针可以拥有从 0x0,000,000,000,000,000 至 0xF,FFF,FFF,FFF,FFF之间的任何值。这使得一个指针可以拥有18446744073709551616 个值中的一个值,它覆盖了一个进程的 16EB 虚拟空间的范围。这是相当大的一个范围

32 位的 CPU 的寻址空间是 4G,所以虚拟内存的最大值为 4G,而 windows 操作系统把这 4G 分成 2 部分,即 3G 的用户空间和 1G 的系统空间,系统空间是各个进程所共享的,他存放的是操作系统及一些内核对象等,而用户空间是分配给各个进程使用的,用户空间包括用:程序代码和数据,堆,共享库,栈。

2、分页

分页的基本方法是,将地址空间分成许多的页。每页的大小由 CPU 决定,然后由操作系统选择页的大小。目前 Inter 系列的 CPU 支持 4KB 或 4MB 的页大小,而 PC 上目前都选择使用 4KB

1.2 内存分区

内核空间:

内核空间表示运行在处理器最高级别的超级用户模式 (supervisor mode) 下的代码或数据,内核空间占用从 0xC0000000 到 0xFFFFFFFF 的 1GB 线性地址空间,内核线性地址空间由所有进程共享,但只有运行在内核态的进程才能访问,用户进程可以通过系统调用切换到内核态访问内核空间,进程运行在内核态时所产生的地址都属于内核空间。

用户空间:

用户空间占用从 0x00000000 到 0xFFFF 共 3GB 的线性地址空间,每个进程都有一个独立的 3GB 用户空间,所以用户空间由每个进程独有,但是内核线程没有用户空间,因为它不产生用户空间地址。另外子进程共享 (继承) 父进程的用户空间只是使用与父进程相同的用户线性地址到物理内存地址的映射关系,而不是共享父进程用户空间。运行在用户态和内核态的进程都可以访问用户空间。

在用户空间内内存被分为:0x08048000 开始

 

二、网络基础

详细了解看视频和计算机网络相关内容,这里只列举一些简单的东西

2.1 IP地址 

2.2 网络分层

各层使用的协议 

2.3 网络程序寻址方式

1、MAC 地址

网络通信的最边缘便是 LAN(Local Area Network) 了,我们先来看在 LAN 中如何寻址的

LAN 主要使用广播通信。在其内部,许多主机连在相同的通信通道上,通信时的关键问题是当争存在时如何决定谁使用通道。解决问题的协议属于链路层的子层,称为 MAC 子层。MAC 子层在 LAN 中特别重要,因为广播通信是由它控制的

网络中的节点都有链路层地址。事实上,并不是节点有链路层地址,而是节点上的适配器有。链路层地址通常叫做 LAN 地址、物理地址或者 MAC 地址。MAC 地址的长度为 6 字节,共有 2 的 48 次方种可能取值。这个 6 字节地址通常以 16 进制表示,每个字节都用一对 16 进制数表示,如: E6-E9-00-17-BB-4B

适配器在生产时就被永久性地安排了一个 MAC 地址,它记录在适配器的 ROM 中,是不可改变的。另外,MAC 地址空间是赋 IEEE 管理的,它保证所有适配器的 MAC 地址都不相同

2、IP地址

IP 地址就是给每个连接在 Internet 上的主机分配的一个 32bit 地址。 互联网上的 IP 地址统一由一个叫 "IANA" (Internet Assigned Numbers Authority,互联网网络号分配机构) 的组织来管理。

各个厂家生产的网络系统和设备,如以太网、分组交换网等,它们相互之间不能互通,不能互通的主要原因是因为它们所传送数据的基本单元 (技术上称之为 “帧”) 的格式不同。IP 协议实际上是一套由软件程序组成的协议软件,它把各种不同"帧”统一转换成 "IP数据报" 格式,这种转换是因特网的一个最重要的特点,使所有各种计算机都能在因特网上实现互通,即具有 "开放性” 的特点。

3、端口号

按照 OSI 七层模型的描述,传输层提供进程 (应用程序) 通信的能力。为了标识通信实体中进行通信的进程 (应用程序),TCP/IP 协议提出了协议端口 (protocol port,简称端口) 的概念。

端口是一种抽象的软件结构 (包括一些数据结构和 I/O 缓冲区) 。应用程序通过系统调用与某端口建立连接 (binding) 后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。

端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP 传输层的两个协议 TCP 和 UDP 是完全独立的两个软件模块,因此各自的端口号也相互独立。

端口使用一个 16 位的数字来表示,它的范围是 0~65535,1024 以下的端口号保留给预定义的服务。例如:http 使用 80 端口。

2.4 TCP协议

三次握手

1、第一次握手:建立连接。客户端发送连接请求报文段,将 SYN 位置为 1,Sequence Number为 x;然后,客户端进入 SYN _SEND 状态,等待服务器的确认;

2、第二次握手:服务器收到 SYN 报文段。服务器收到客户端的 SYN 报文段,需要对这个 SYN 报文段进行确认,设置 Acknowledgment Number 为 x+1(Sequence Number+1);同时,自己还要发送 SYN 请求信息,将 SYN 位置为 1, Sequence Number 为 y;服务器端将上述所有信息放到一个报文段 (即 SYN+ ACK 报文段) 中,一并发送给客户端,此时服务器进入 SYN_RECV 状态;

3、第三次握手:客户端收到服务器的 SYN+ACK 报文段。然后将 Acknowledgment Number 设置为 y+1,向服务器发送 ACK 报文段,这个报文段发送完毕以后,客户端和服务器端都进入 ESTABLISHED 状态,完成 TCP 三次握手。

完成了三次握手,客户端和服务器端就可以开始传送数据。

四次分手

当客户端和服务器通过三次握手建立了 TCP 连接以后,当数据传送完毕,断开 TCP 连接

1、第 1 次分手:主机1 (可以是客户端,也可以是服务器端),设置 Sequence Number 和 Acknowledgment Number,向主机 2 发送一个 FIN 报文段此时, 主机 1 进入 FIN_WAIT_1 状态;这表示主机 1 没有数据要发送给主机 2 了;

2、第 2 次分手:主机 2 收到了主机 1 发送的 FIN 报文段,向主机 1 回一个 ACK 报文段,Acknowledgment Number 为 Sequence Number 加 1;主机 1 进入 FIN_WAIT_2 状态;主机 2 告诉主机 1,我也没有数据要发送了,可以进行关闭连接了;

3、第 3 次分手:主机 2 向主机 1 发送 FIN 报文段,请求关闭连接,同时主机 2 进入 CLOSE_WAIT 状态;

4、第 4 次分手:主机 1 收到主机 2 发送的 FIN 报文段,向主机 2 发送 ACK 报文段,然后主机 1 进入 TIME_ WAIT 状态;主机 2 收到主机 1 的 ACK 报文段以后,就关闭连接;此时,主机 1 等待 2MSL 后依然没有收到回复,则证明 Server 端已正常关闭,那好,主机 1 也可以关闭连接了

2.5 TCP服务器/客户端创建过程

2.6 UDP服务器/客户端创建过程

三、测试 TCP

3.1 测试连接

1、头文件

#ifndef _SOCKET_INIT_H_
#define _SOCKET_INIT_H_#pragma once
#pragma comment(lib, "ws2_32.lib")#include <iostream>
#include <winsock2.h>class SocketInit {
public:SocketInit() {WORD sockVersion = MAKEWORD(2, 2);WSADATA wasData;if (WSAStartup(sockVersion, &wasData) != 0) {printf("动态链接库加载失败!\n");}}~SocketInit() {WSACleanup();}
};#endif // !_SOCKET_INIT_H_

2、服务端

#include "SocketInit.hpp"using namespace std;int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sListen == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = INADDR_ANY; // 获取本机的IP地址// 绑定套接字int ret = bind(sListen, (const sockaddr*)&sock_in, sizeof(sockaddr_in));if (ret == SOCKET_ERROR) {printf("绑定套接字失败!\n");closesocket(sListen);return -1;}// 监听if (listen(sListen, 10) == SOCKET_ERROR) {printf("监听失败!\n");closesocket(sListen);return -1;}sockaddr_in clientAddr;int nlen = sizeof(sockaddr_in);// 接受客户端的连接SOCKET sClient = accept(sListen, (sockaddr*)&clientAddr, &nlen);if (sClient == SOCKET_ERROR) {printf("接收客户端失败!\n");closesocket(sListen);return -1;}printf("与客户端建立连接...\n");getchar();return 0;
}

3、客户端

#include "SocketInit.hpp"using namespace std;int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sClient == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = inet_addr("192.168.3.165");if (connect(sClient, (const sockaddr*)&sock_in, sizeof(sock_in)) == SOCKET_ERROR) {printf("连接服务器失败!\n");return -1;}getchar();return 0;
}

过程如果报错,可以使用的解决方法:

(4条消息) C4996: 'inet_addr': Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS_小哈龙的博客-CSDN博客,然后再打开客户端,运行结果:

3.2 测试收发数据

#include "SocketInit.hpp"using namespace std;int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sListen == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = INADDR_ANY; // 获取本机的IP地址// 绑定套接字int ret = bind(sListen, (const sockaddr*)&sock_in, sizeof(sockaddr_in));if (ret == SOCKET_ERROR) {printf("绑定套接字失败!\n");closesocket(sListen);return -1;}// 监听if (listen(sListen, 10) == SOCKET_ERROR) {printf("监听失败!\n");closesocket(sListen);return -1;}sockaddr_in clientAddr;int nlen = sizeof(sockaddr_in);// 接受客户端的连接SOCKET sClient = accept(sListen, (sockaddr*)&clientAddr, &nlen);if (sClient == SOCKET_ERROR) {printf("接收客户端失败!\n");closesocket(sListen);return -1;}printf("与客户端建立连接...\n");while (true) {char buff[1024] = { 0 };int  result = recv(sClient, buff, 1024, 0);if (result > 0) {printf("接收到的数据:%s\n", buff);}else {printf("客户端断开连接!\n");break;}}closesocket(sListen);getchar();return 0;
}
#include "SocketInit.hpp"
#define _WINSOCK_DEPRECATED_NO_WARNINGSusing namespace std;int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sClient == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = inet_addr("192.168.3.165");if (connect(sClient, (const sockaddr*)&sock_in, sizeof(sock_in)) == SOCKET_ERROR) {printf("连接服务器失败!\n");return -1;}while (true) {char buff[1024] = { 0 };gets_s(buff, 1024);send(sClient, buff, strlen(buff), 0);}closesocket(sClient);getchar();return 0;
}

3.3 多线程服务器连接多客户端

#include "SocketInit.hpp"using namespace std;DWORD WINAPI ThreadProc(LPVOID lp) {SOCKET sClient = *(SOCKET*)lp;while (true) {char buff[1024] = { 0 };int  result = recv(sClient, buff, 1024, 0);if (result > 0) {printf("接收到的数据:%s\n", buff);}else {printf("客户端断开连接!\n");break;}}return NULL;
}int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sListen == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = INADDR_ANY; // 获取本机的IP地址// 绑定套接字int ret = bind(sListen, (const sockaddr*)&sock_in, sizeof(sockaddr_in));if (ret == SOCKET_ERROR) {printf("绑定套接字失败!\n");closesocket(sListen);return -1;}// 监听if (listen(sListen, 10) == SOCKET_ERROR) {printf("监听失败!\n");closesocket(sListen);return -1;}sockaddr_in clientAddr;int nlen = sizeof(sockaddr_in);while (true) {// 接受客户端的连接SOCKET sClient = accept(sListen, (sockaddr*)&clientAddr, &nlen);if (sClient == SOCKET_ERROR) {printf("接收客户端失败!\n");closesocket(sListen);return -1;}printf("与客户端建立连接...\n");CreateThread(NULL, NULL, ThreadProc, (LPVOID)&sClient, NULL, NULL);}closesocket(sListen);getchar();return 0;
}

3.4 select服务器

#include "SocketInit.hpp"using namespace std;int main()
{SocketInit socketInit;  // 加载动态库// 创建监听套接字SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sListen == SOCKET_ERROR) {printf("监听套接字创建失败!\n");return -1;}sockaddr_in sock_in;sock_in.sin_family = AF_INET;              // 协议族sock_in.sin_port = htons(12306);           // 端口号sock_in.sin_addr.S_un.S_addr = INADDR_ANY; // 获取本机的IP地址// 绑定套接字int ret = bind(sListen, (const sockaddr*)&sock_in, sizeof(sockaddr_in));if (ret == SOCKET_ERROR) {printf("绑定套接字失败!\n");closesocket(sListen);return -1;}// 监听if (listen(sListen, 10) == SOCKET_ERROR) {printf("监听失败!\n");closesocket(sListen);return -1;}FD_SET fd_read;    // 存储SOCKET对象FD_ZERO(&fd_read); // 初始化FD_SET(sListen, &fd_read); // 将sListen套接字放到fd_read集合中while (true) {FD_SET fd_tmp = fd_read;const timeval tv = { 1, 0 };int ret = select(NULL, &fd_tmp, NULL, NULL, &tv);if (ret == 0) {  // 如果没有网络事件Sleep(1);continue;}for (int i = 0; i < fd_tmp.fd_count; i++) {if (fd_tmp.fd_array[i] == sListen) { // 如果监听套接字有网络事件,证明有客户端正在连接服务器sockaddr_in clientAddr;int nlen = sizeof(sockaddr_in);// 接受客户端的连接SOCKET sClient = accept(sListen, (sockaddr*)&clientAddr, &nlen);if (sClient == SOCKET_ERROR) {printf("接收客户端失败!\n");closesocket(sListen);return -1;}printf("与客户端建立连接: %s ...\n", inet_ntoa(clientAddr.sin_addr));FD_SET(sClient, &fd_read);}else { // 如果监听套接字有网络事件,证明客户端正在发送数据,服务端接收数据char buff[1024] = { 0 };int  result = recv(fd_tmp.fd_array[i], buff, 1024, 0);if (result > 0) {printf("接收到的数据:%s\n", buff);}else {// 从fd_read中移除当前SOCKETFD_CLR(fd_tmp.fd_array[i], &fd_read);printf("客户端断开连接!\n");break;}}}}closesocket(sListen);getchar();return 0;
}

更多推荐

windows系统编程2——内存管理和网络

本文发布于:2024-02-27 20:50:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1766335.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:内存管理   系统   网络   windows

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!