当前位置: 首页 > ops >正文

C++高频知识点(十九)

文章目录

  • 91. TCP断开连接的时候为什么必须4次而不是3次?
  • 92. 为什么要区分用户态和内核态?
  • 93. 说说编写socket套接字的步骤
    • 1. 服务器端编写步骤
      • 1.1 创建套接字
      • 1.2 绑定套接字
      • 1.3 监听连接
      • 1.4 接受连接
      • 1.5 数据传输
      • 1.6 关闭套接字
    • 2. 客户端编写步骤
      • 2.1 创建套接字
      • 2.2 连接服务器
      • 2.3 数据传输
      • 2.4 关闭套接字
  • 94. 什么是大小端模式,编写代码区分大小端
    • 如何检查自己的电脑 是大端还是小端?
      • 第一种方法:
      • 第二种方法:
  • 95. 代码实现:实现简单的智能指针

91. TCP断开连接的时候为什么必须4次而不是3次?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

92. 为什么要区分用户态和内核态?

在这里插入图片描述
在这里插入图片描述

93. 说说编写socket套接字的步骤

在这里插入图片描述
编写一个基于套接字(socket)的网络程序通常包括以下步骤,无论是客户端还是服务器都需要遵循这些步骤。下面分别说明服务器和客户端编写的步骤,这些是简单的代码示例,仅仅帮助大家去理解这个过程。

1. 服务器端编写步骤

1.1 创建套接字

使用 socket() 函数创建一个套接字。这个函数返回一个套接字描述符,用于后续的操作。

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {perror("socket failed");exit(EXIT_FAILURE);
}

1.2 绑定套接字

将创建的套接字绑定到指定的 IP 地址和端口号上,使用 bind() 函数。

struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);
}

网络字节序是大端模式
在这里插入图片描述
address.sin_addr.s_addr = INADDR_ANY;指绑定端口到本地所有网络接口
在这里插入图片描述

1.3 监听连接

使用 listen() 函数使套接字进入监听状态,等待客户端连接请求。

if (listen(server_fd, 3) < 0) {perror("listen");close(server_fd);exit(EXIT_FAILURE);
}

1.4 接受连接

使用 accept() 函数接受客户端的连接请求,返回一个新的套接字描述符,用于与客户端通信。

int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0) {perror("accept");close(server_fd);exit(EXIT_FAILURE);
}

1.5 数据传输

使用 read() 和 write() 函数(或 recv() 和 send() 函数)进行数据的接收和发送。

char buffer[1024] = {0};
read(new_socket, buffer, 1024);
printf("Message from client: %s\n", buffer);
send(new_socket, "Hello from server", strlen("Hello from server"), 0);

1.6 关闭套接字

完成通信后,使用 close() 函数关闭套接字。

close(new_socket);
close(server_fd);

2. 客户端编写步骤

2.1 创建套接字

与服务器端类似,使用 socket() 函数创建一个套接字。

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {perror("socket failed");exit(EXIT_FAILURE);
}

2.2 连接服务器

使用 connect() 函数将套接字连接到服务器端的指定 IP 地址和端口号。

struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {perror("Invalid address/ Address not supported");close(sock);exit(EXIT_FAILURE);
}if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("Connection Failed");close(sock);exit(EXIT_FAILURE);
}

2.3 数据传输

同样使用 read() 和 write() 或 recv() 和 send() 进行数据的发送和接收。

send(sock, "Hello from client", strlen("Hello from client"), 0);
char buffer[1024] = {0};
read(sock, buffer, 1024);
printf("Message from server: %s\n", buffer);

2.4 关闭套接字

完成通信后,使用 close() 函数关闭套接字。

close(sock);

94. 什么是大小端模式,编写代码区分大小端

在这里插入图片描述
在这里插入图片描述

如何检查自己的电脑 是大端还是小端?

第一种方法:

#include <iostream>  bool isLittleEndian() {  int num = 1;  // 数值 1 的 32 位表示是:0x00 00 00 01(从高到低 4 个字节)。// 大端(big-endian)把最高有效字节放在最低地址,所以内存从低地址到高地址是:// 00 00 00 01// 于是 reinterpret_cast<char*>(&num) 指向的第一个字节就是 0x00// 小端(little-endian)相反,把最低有效字节放在最低地址,所以是:// 01 00 00 00// 这时第一个字节是 0x01// 换个更形象的例子:如果 num = 0x12 34 56 78,// 大端内存排布(低→高地址):12 34 56 78// 小端内存排布(低→高地址):78 56 34 12char *c = reinterpret_cast<char*>(&num);  return *c == 1; // 如果最低有效字节在最低地址处,则为小端字节序  
}  int main() {  if (isLittleEndian()) {  std::cout << "This is a little-endian system." << std::endl;  } else {  std::cout << "This is a big-endian system." << std::endl;  }  return 0;  
}

在这里插入图片描述

第二种方法:

利用数据类型的存储方式来判断当前系统的字节序。常见的实现方法是使用 union 联合体来共享内存,并通过访问不同的成员来检查数据的存储顺序。

#include <iostream>
#include <cstdint>// 定义一个联合体,包含一个整数和一个字符数组
// 联合体(union)的所有成员共享同一段内存;写一个成员,换个成员读,能看到相同内存里的原始字节
union {// uint8_t:精确 8 位 无符号整数,范围 0 ~ 255。// uint32_t:精确 32 位 无符号整数,范围 0 ~ 4,294,967,295。uint32_t i;      // 32 位整数uint8_t c[4];    // 4 字节字符数组
} test;int main() {test.i = 0x12345678;  // 将一个已知的 32 位整数存入联合体中// 根据第一个字节的值判断大小端if (test.c[0] == 0x78) {std::cout << "小端模式 (Little-endian)" << std::endl;} else if (test.c[0] == 0x12) {std::cout << "大端模式 (Big-endian)" << std::endl;} else {std::cout << "无法确定字节序" << std::endl;}return 0;
}

在这里插入图片描述

在这里插入图片描述

95. 代码实现:实现简单的智能指针

下面是一个简单的智能指针实现的例子,用于管理动态分配的内存,避免内存泄漏。这个示例实现了一个类似于 std::shared_ptr 的简单智能指针,叫做 SimpleSmartPointer,它使用引用计数来管理对象的生命周期。

是在定义一个新对象 sp2,带着一个“初始值”sp1。
在 C++ 里,带初始值的定义叫“拷贝初始化(copy-initialization)”,它会调用拷贝构造函数(或能匹配的移动构造),不会调用赋值运算符。赋值运算符只在对象已经存在之后再用 = 给它“换内容”时才会被调用。SimpleSmartPointer<int> a(new int(10));// 1) 拷贝初始化:调用拷贝构造函数
SimpleSmartPointer<int> b = a;     // == SimpleSmartPointer<int> b(a);// 2) 直接初始化:也调用拷贝构造函数
SimpleSmartPointer<int> c(a);// 3) 先默认构造一个对象,再赋值:调用赋值运算符 operator=
SimpleSmartPointer<int> d;         // 等价于 SimpleSmartPointer<int> d(nullptr);
d = a;                             // 这里才会走你的 operator=
#include <iostream>// 简单智能指针类
template<typename T>
class SimpleSmartPointer {
private:T* ptr;          // 原生指针unsigned* count; // 引用计数public:// 构造函数,接受一个原生指针//这个explicit 主要目的是防止隐式类型转换。提高代码可读性和安全性explicit SimpleSmartPointer(T* p = nullptr) : ptr(p) {if (p) {count = new unsigned(1); // 初始化引用计数为1} else {count = nullptr;}}// 拷贝构造函数SimpleSmartPointer(const SimpleSmartPointer<T>& sp) : ptr(sp.ptr), count(sp.count) {if (count) {(*count)++; // 增加引用计数}}// 赋值运算符重载SimpleSmartPointer<T>& operator=(const SimpleSmartPointer<T>& sp) {if (this == &sp) {return *this; // 防止自我赋值}// 释放当前资源//--(*count):对 *count(引用计数值)进行自减操作,表示当前对象不再使用该资源//如果 *count 为 0,说明已经没有其他智能指针对象在使用这个资源了,此时需要释放资源if (count && --(*count) == 0) {delete ptr;delete count;}// delete ptr; 释放的是 ptr 指向的那块堆内存(并调用析构),并不会“删掉变量 ptr 本身”// 赋值新资源ptr = sp.ptr;count = sp.count;if (count) {(*count)++;}return *this;}// 解引用运算符重载// 返回类型:对 T 的引用。有了引用返回,*sp 就是一个可当左值用的对象(能读也能改)。// 函数名是个特殊运算符函数:重载“一元解引用运算符 *”。// 调用方式:*sp 等价于 sp.operator*()// operator* 是运算符重载函数,重载的是“一元解引用运算符 *”(注意不是乘法;乘法是二元 *)。// 这是一个成员函数,当你写 *sp 时,编译器会把它当作:// sp.operator*()   // 调用你这个函数// ptr 是你类里存的裸指针(T*)。// *ptr 是对这个裸指针的解引用,得到“那个 T 对象本身”。结合返回类型 T&,就把“托管对象”的引用交给了调用者。T& operator*() const {return *ptr;}// 指针访问运算符重载// T* operator->() const { return ptr; }// 把内部的裸指针 ptr(类型 Point*)拿出来,然后再用普通指针的 -> 去调用 print() / move() / 访问 x、y。所以 p->成员 就能像真指针那样用起来了// 前提是裸指针对应的数据结构里面有定义成员变量或者成员函数// operator->() 只有在 T 有成员时才有用;你现在用的是 SimpleSmartPointer<int>,int 没成员,所以用不上。给它换个有成员的类型,比如 Point,就能直接写 sp->成员/方法 了。T* operator->() const {return ptr;}// 返回当前共享计数;空指针时按惯例返回 0unsigned use_count() const {return count ? *count : 0;}// 获取原生指针T* get() const {return ptr;}// 析构函数 RAII~SimpleSmartPointer() {if (count && --(*count) == 0) {delete ptr;delete count;}}
};// 测试函数
void testSimpleSmartPointer() {// sp1.ptr 指向“int(10)”;// sp1.count 指向一块 unsigned,其值 *count == 1SimpleSmartPointer<int> sp1(new int(10));  // 创建一个智能指针,管理整数10std::cout << "sp1: " << *sp1 << std::endl; // 输出sp1所指向的值std::cout << "sp1.use_count() = " << sp1.use_count() << "\n";{// 拷贝构造函数  ← 就是被 “SimpleSmartPointer<int> sp2 = sp1;” 调用的这个SimpleSmartPointer<int> sp2 = sp1;    // sp2与sp1共享同一块内存*sp2 = 33;std::cout << "sp2: " << *sp2 << std::endl; // 输出sp2所指向的值std::cout << "sp2.use_count() = " << sp2.use_count() << "\n";std::cout << "sp1.use_count() = " << sp1.use_count() << "\n";} // sp2超出作用域,引用计数减1// 再次输出sp1所指向的值std::cout << "sp1: " << *sp1 << std::endl;std::cout << "sp1.use_count() = " << sp1.use_count() << "\n";
}int main() {testSimpleSmartPointer();return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

http://www.xdnf.cn/news/17442.html

相关文章:

  • 【AI论文】LongVie:多模态引导的可控超长视频生成
  • 嵌套-列表存储字典,字典存储列表,字典存储字典
  • InfluxDB 在物联网设备数据采集与分析中的应用(一)
  • Python爬虫-爬取政务网站的文档正文内容和附件数据
  • 如何解决线上gc频繁的问题?
  • 在Ansys Simplorer中设计三相逆变器,并与Maxwell FEA耦合,实现160kW PMSM
  • Day 10: Transformer完整架构详解 - 从位置编码到编解码器的全面剖析
  • Excel常用功能函数
  • 重学React(四):状态管理二
  • 攻击者瞄准加密技术的基础:智能合约
  • Dify集成 Echarts 实现智能数据报表集成与展示实战详解
  • 第三章-提示词:从0到1,提示词实训全攻略,解锁大语言模型无限潜能(14/36)
  • 深度解析 Spring Boot 循环依赖:原理、源码与解决方案
  • Python vs MATLAB:智能体开发实战对比
  • JavaScript 变量:数据存储的核心机制
  • 生产环境中Spring Cloud Sleuth与Zipkin分布式链路追踪实战经验分享
  • 消息生态系统全景解析:技术架构、核心组件与应用场景
  • Tomcat报错-chcon无法关联自启脚本
  • MySQL(189)如何分析MySQL的锁等待问题?
  • 采用GPT5自动规划实现番茄计时器,极简提示词,效果达到产品级
  • 祝融号无线电工作频段
  • 繁花深处:花店建设的时代意义与多元应用—仙盟创梦IDE
  • keil之stm32f10x模板工程创建
  • 简要介绍交叉编译工具arm-none-eabi、arm-linux-gnueabi与arm-linux-gnueabihf
  • 【重建技巧】Urban Scene Reconstruction-LoD细节提升
  • 【unitrix数间混合计算】2.9 小数部分特征(bin_frac.rs)
  • 第十四届蓝桥杯青少年组省赛 编程题真题题解
  • [SC]高效地调试SystemC模型中的语法错误
  • AI大模型模态特征详解
  • 【ref、toRef、toRefs、reactive】