网络基础(1)

文章目录

  • 1. 网络基础
    • 1.1 网络协议
      • 1.1.1 OSI七层模型
    • 1.3 网络中的地址管理
  • 2. 套接字编程
    • 2.1 源IP地址和目的IP地址
    • 2.3 socket编程接口
      • 2.3.2 sockaddr结构
      • 2.2.3 UDPecho服务器
      • 2.24 netstat
      • 2.25 远程执行命令

1. 网络基础

1.1 网络协议

1.1.1 OSI七层模型

  • OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范;
  • 把网络从逻辑上分为了7层. 每一层都有相关、相对应的物理设备,比如路由器,交换机;
  • OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;
  • 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整. 通过七 个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯;
  • 但是, 它既复杂又不实用; 所以我们按照TCP/IP四层模型来讲解.
层级名称功能
第七层应用层提供用户接口,处理用户请求和数据传输。
第六层表示层处理数据的表示和转换,确保传输的数据格式统一。
第五层会话层管理通信会话,确保数据的传输顺序和完整性。
第四层传输层负责端到端的数据传输,提供可靠的数据传输服务。
第三层网络层处理数据的路由和转发,实现不同网络之间的通信。
第二层数据链路层控制数据在物理介质上的传输,管理节点之间的数据流动。
第一层物理层管理物理介质,负责将比特流转换为适合传输的信号。

###1.1.2 TCP/IP五层(或四层)模型

TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇.

TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.

  • 物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆(现在主要用于有线电视)、光纤, 现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等. 集线器(Hub)工作在物理层.
  • 数据链路层: 负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作. 有以太网、令牌环网, 无线LAN等标准. 交换机(Switch)工作在数据链路层.
  • 网络层: 负责地址管理和路由选择. 例如在IP协议中, 通过IP地址来标识一台主机, 并通过路由表的方式规划出两台主机之间的数据传输的线路(路由). 路由器(Router)工作在网路层.
  • 传输层: 负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机.
  • 应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等. 我们的网络编程主要就是针对应用层.

1

物理层我们考虑的比较少. 因此很多时候也可以称为 TCP/IP四层模型

一般而言

  • 对于一台主机, 它的操作系统内核实现了从传输层到物理层的内容;
  • 对于一台路由器, 它实现了从网络层到物理层;
  • 对于一台交换机, 它实现了从数据链路层到物理层;
  • 对于集线器, 它只实现了物理层;

但是并不绝对. 很多交换机也实现了网络层的转发; 很多路由器也实现了部分传输层的内容(比如端口转发);

##1.2 网络传输基本流程

网络传输流程图

同一个网段内的两台主机进行文件传输.

应用层:Telnet、FTP和e-mail等

传输层:TCP和UDP

网络层:IP、ICMP和IGMP

链路层:设备驱动程序及接口卡

2

数据包封装和分用

  • 不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报 (datagram),在链路层叫做帧(frame).
  • 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装 (Encapsulation).
  • 首部信息中包含了一些类似于首部有多长, 载荷(payload)有多长, 上层协议是什么等信息.
  • 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部, 根据首部中的 “上层协议字段” 将数据交给对应的上层协议处理.

下图为数据封装的过程

3

下图为数据分用的过程

4

1.3 网络中的地址管理

  1. IP地址(Internet Protocol Address):IP地址是用于在Internet上唯一标识设备(如计算机、路由器等)的地址。它是一组数字,通常以四个十进制数(例如,192.168.1.1)表示,每个数的取值范围为0到255。IP地址分为IPv4和IPv6两种版本,IPv4是目前广泛使用的版本,而IPv6则是为了解决IPv4地址短缺问题而设计的新版本。

    IP地址用于在网络中路由数据包,确保它们能够从源设备传输到目标设备。它们也可以用于识别设备所在的网络和子网络。

    IPv6地址并不是六个十进制数,而是由8组16位的十六进制数构成,每组之间用冒号分隔,例如:

    2001:0db8:85a3:0000:0000:8a2e:0370:7334
    

    每组的十六进制数表示一个16位的段,共128位,这样IPv6地址的地址空间远远大于IPv4地址空间,因此可以为更多的设备提供唯一的标识。

  2. MAC地址(Media Access Control Address):MAC地址是网络设备(如网卡、无线网卡等)在出厂时分配的唯一标识符。它是一个48位的十六进制数,通常以6个字节的形式表示,如00:1A:2B:3C:4D:5E。MAC地址是设备的固定地址,与设备的网络连接硬件紧密相关。

    MAC地址用于在局域网(LAN)中唯一标识设备。当数据包在局域网中传输时,路由器或交换机会使用MAC地址来决定将数据包发送到哪个设备。

    在网卡出厂时就确定了, 不能修改. mac地址通常是唯一的(虚拟机中的mac地址不是真实的mac地址, 可能会冲突; 也有些网卡支持用户配置mac地址). 比特

2. 套接字编程

2.1 源IP地址和目的IP地址

IP数据包头部包含了源IP地址和目的IP地址,它们分别指示了数据包的发送者和接收者

##2.2 端口号

端口号是网络通信中的一个重要概念,用于标识在网络连接中的特定应用程序或服务。

在TCP/IP协议中,端口号是一个16位的数字,取值范围是0到65535。端口号分为两种类型:系统端口动态端口

  1. 系统端口:系统端口是预留给一些常用的网络服务的端口号,例如HTTP服务的端口号是80,HTTPS服务的端口号是443,FTP服务的端口号是21等。这些端口号的范围是0到1023,也被称为“众所周知的端口”。
  2. 动态端口:动态端口是由操作系统随机分配给客户端应用程序的端口号,用于临时通信。它们的范围通常是从1024到65535。
  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用.

“端口号” 和 “进程ID”

一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定;

源端口号和目的端口号

传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要 发给谁”;

目标端口号通常是由服务或应用程序提前确定好的,而不是在通信过程中动态选择的。

认识TCP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识;

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

认识UDP协议

此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识;

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

大端模式: 数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处。

小端模式: 数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处。

大端字节序与人类读写数值的方式更加一致,因此在网络通信中被广泛采用。

(记忆方法:小小小)

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

  1. htonl() - 将32位主机字节序转换为网络字节序(大端字节序)。
  2. htons() - 将16位主机字节序转换为网络字节序(大端字节序)。
  3. ntohl() - 将32位网络字节序(大端字节序)转换为主机字节序。
  4. ntohs() - 将16位网络字节序(大端字节序)转换为主机字节序。

这些函数通常在 <arpa/inet.h> 头文件中声明。使用这些函数可以确保在不同字节序的计算机上,通过网络传输的数据始终以大端字节序进行传输和解析。

2.3 socket编程接口

###2.3.1 socket 常见API

C 语言中的 socket 编程接口通常使用 POSIX 标准库中的 socket()bind()listen()accept()connect()recv()send() 等函数来实现。这些函数通常用于创建网络套接字、绑定地址、监听连接、接受连接、建立连接以及发送和接收数据等操作。以下是一些常见的 socket 编程接口函数及其作用:

  1. socket():创建一个新的套接字。

    int socket(int domain, int type, int protocol);
    
    • domain 参数指定了通信的协议族,常见的包括 AF_INET(IPv4)和 AF_INET6(IPv6)等。
    • type 参数指定了套接字的类型,常见的包括 SOCK_STREAM(流套接字,如 TCP)和 SOCK_DGRAM(数据报套接字,如 UDP)等。
    • protocol 参数指定了使用的协议,通常设为 0,让系统根据 domaintype 自动选择合适的协议。
  2. bind():将套接字绑定到一个地址上。

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  3. listen():监听传入的连接。

    int listen(int sockfd, int backlog);
    
  4. accept():接受传入的连接。

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  5. connect():建立与远程主机的连接。

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  6. recv()send():接收和发送数据。

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    

这些函数都在 <sys/socket.h><netinet/in.h>(对于网络编程)中声明。在使用这些函数之前,通常需要创建套接字,并根据需要设置地址、端口等信息。此外,在网络编程中,还需要处理地址结构体 struct sockaddrstruct sockaddr_in(对于 IPv4 地址)等。

2.3.2 sockaddr结构

sockaddr 结构体是用于表示通用套接字地址的结构体,它在网络编程中被广泛使用。在 C 语言中,sockaddr 结构体用于表示各种协议族(如 IPv4、IPv6)的套接字地址信息,使得网络编程可以在不同的协议族之间通用。

以下是 sockaddr 结构体的定义:

struct sockaddr {sa_family_t sa_family;   // 协议族(地址族)char        sa_data[14]; // 地址数据,具体内容取决于协议族
};

这里的 sa_family 是一个无符号短整型,用于指定地址的协议族,它的值可以是 AF_INET(IPv4)、AF_INET6(IPv6)等,具体取值取决于所使用的协议族。而 sa_data 数组则用于存储地址的具体数据,其长度是 14 字节,但具体的内容和长度取决于使用的协议族。

在实际的网络编程中,通常使用的是 sockaddr_in 结构体来表示 IPv4 地址,它是 sockaddr 的一个特定形式。sockaddr_in 结构体的定义如下:

struct sockaddr_in {sa_family_t    sin_family; // 协议族(地址族)in_port_t      sin_port;   // 端口号struct in_addr sin_addr;   // IPv4 地址char           sin_zero[8]; // 为了使 sockaddr_in 和 sockaddr 兼容而保留的空字节
};

sockaddr_in 结构体中,除了协议族之外,还包含了端口号和 IPv4 地址。其中 sin_port 是一个无符号短整型,用于存储端口号,sin_addr 则是一个 struct in_addr 结构体,用于存储 IPv4 地址。sin_zero 数组用于填充,使 sockaddr_in 结构体的大小和 sockaddr 结构体的大小一致,保证了在函数调用时可以进行类型转换。

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好 处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为 参数;

2.2.3 UDPecho服务器

//main.cc
#include "UdpServer.hpp"
#include <memory>
#include <cstdio>// "120.78.126.148" 点分十进制字符串风格的IP地址void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}std::string Handler(const std::string &str)
{std::string res = "Server get a message: ";res += str;std::cout << res << std::endl;// pid_t id = fork();// if(id == 0)// {//     // ls -a -l -> "ls" "-a" "-l"//     // exec*();// }return res;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->Init(/**/);std::cout<<"初始化完成"<<std::endl;svr->Run(Handler);return 0;
}
//UdpClient.cc
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>using namespace std;void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n"<< std::endl;
}// ./udpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport); //?server.sin_addr.s_addr = inet_addr(serverip.c_str());socklen_t len = sizeof(server);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cout << "socker error" << endl;return 1;}// client 要bind吗?要!只不过不需要用户显示的bind!一般有OS自由随机选择!// 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!// 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!// 系统什么时候给我bind呢?首次发送数据的时候string message;char buffer[1024];while (true){cout << "Please Enter@ ";getline(cin, message);std::cout << message << std::endl;// 1. 数据 2. 给谁发sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);std::cout << "send success" << std::endl;struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);std::cout << "receive success" << std::endl;if(s > 0){buffer[s] = 0;cout << buffer << endl;}}close(sockfd);return 0;
}
//UdpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string&)> func_t;Log lg;enum{SOCKET_ERR=1,BIND_ERR
};uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;class UdpServer{
public:UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip),isrunning_(false){}void Init(){// 1. 创建udp socketsockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INETif(sockfd_ < 0){lg(Fatal, "socket create error, sockfd: %d", sockfd_);exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2. bind socketstruct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_); //需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的local.sin_addr.s_addr = inet_addr(ip_.c_str()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??// local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));}void Run(func_t func) // 对代码进行分层{isrunning_ = true;char inbuffer[size];while(isrunning_){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);std::cout<<"收到成功"<<std::endl;if(n < 0){lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}inbuffer[n] = 0;std::string info = inbuffer;std::string echo_string = func(info);sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);}}~UdpServer(){if(sockfd_>0) close(sockfd_);}
private:int sockfd_;     // 网路文件描述符std::string ip_; // 任意地址bind 0uint16_t port_;  // 表明服务器进程的端口号bool isrunning_;
};

INADDR_ANY

在套接字编程中,调用 bind() 函数用于将一个特定的 IP 地址和端口号绑定到一个套接字上。当你使用 bind() 函数时,可以选择绑定到特定的 IP 地址和端口号,也可以选择绑定到特定的端口号而将 IP 地址留空,还可以选择将 IP 地址和端口号都留空。

当你调用 bind() 函数并将 IP 地址参数设为 0(通常表示 INADDR_ANY),这意味着你希望套接字可以接受来自任意 IP 地址的连接。换句话说,它会监听所有网络接口上的连接请求。这样做的典型场景是在服务器程序中,当你想要在主机的所有可用 IP 地址上监听某个端口时。

2.24 netstat

  1. netstat -naup

    • netstat 是网络统计(network statistics)的缩写,用于显示网络连接、路由表和网络接口信息。

    • -naup
      

      是参数,每个字母都代表不同的选项:

      • -n 表示不要解析服务名称,而是显示数字形式的地址和端口号。
      • -a 表示显示所有的连接和侦听端口,而不仅仅是活动的连接。
      • -u 表示只显示 UDP 协议的连接。
      • -p 表示显示建立相关的进程/程序。
    • 所以,netstat -naup 命令将显示所有的 UDP 连接以及它们对应的端口号和相关进程信息。

  2. netstat -natp 命令来只显示 TCP 连接并包含相关进程信息。

  3. netstat -nalp显示所有协议(TCP、UDP、ICMP 等)的连接。

2.25 远程执行命令

bool SafeCheck(const std::string &cmd)
{int safe = false;std::vector<std::string> key_word = {"rm","mv","cp","kill","sudo","unlink","uninstall","yum","top","while"};for(auto &word : key_word){auto pos = cmd.find(word);if(pos != std::string::npos) return false;}return true;
}std::string ExcuteCommand(const std::string &cmd)
{std::cout << "get a request cmd: " << cmd << std::endl;if(!SafeCheck(cmd)) return "Bad man";FILE *fp = popen(cmd.c_str(), "r");if(nullptr == fp){perror("popen");return "error";}std::string result;char buffer[4096];while(true){char *ok = fgets(buffer, sizeof(buffer), fp);if(ok == nullptr) break;result += buffer;}pclose(fp);return result;
}

###2.26 远程执行命令

###2.27 windows客户端

###2.28 聊天室,转发,多线程

多终端,命令行重定向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

扭蛋机小程序对市场的发展有哪些推动作用?

近几年&#xff0c;扭蛋机发展的非常迅猛。随着二次元文化的火热&#xff0c;给扭蛋机带来了发展机遇&#xff0c;扭蛋机行业也受到了大众的喜爱。扭蛋机的商品种类多样化&#xff0c;包含了各类热门IP周边衍生品、玩具、小商品等&#xff0c;适合所有消费人群&#xff0c;市场…

2024年G1工业锅炉司炉证考试题库及G1工业锅炉司炉试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年G1工业锅炉司炉证考试题库及G1工业锅炉司炉试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲…

8.0MGR单主模式搭建_克隆(clone)插件方式

为了应对事务一致性要求很高的系统对高可用数据库系统的要求&#xff0c;并且增强高可用集群的自管理能力&#xff0c;避免节点故障后的failover需要人工干预或其它辅助工具干预&#xff0c;MySQL5.7新引入了Group Replication&#xff0c;用于搭建更高事务一致性的高可用数据库…

【前端缓存】localStorage是同步还是异步的?为什么?

写在开头 点赞 收藏 学会 首先明确一点&#xff0c;localStorage是同步的 一、首先为什么会有这样的问题 localStorage 是 Web Storage API 的一部分&#xff0c;它提供了一种存储键值对的机制。localStorage 的数据是持久存储在用户的硬盘上的&#xff0c;而不是内存。这意…

.Net添加了引用,仍然提示找不到命名空间

如图&#xff0c;MyStudy控制台程序引用了一个C#类库MyClassLibrary 代码里也能敲出来using MyClassLibrary&#xff0c;但是build时始终提示找不到命名空间MyClassLibrary 我检查了MyClassLibrary的Assembly&#xff0c;命名空间名称无误 又检查了MyStudy里的引用信息&#x…

Linux SDIO-WiFi 协议栈

Linux SDIO-WiFi 协议栈 1. 简介2. BCMDHD2.1 WiFi模组2.2 驱动初始化&#xff08;dhd_module_init&#xff09; 3. Broadcom fullmac WLAN 1. 简介 2. BCMDHD BCMDHD&#xff1a;Broadcom Dongle Host DriverSIP&#xff1a;System In Package 2.1 WiFi模组 2.2 驱动初始化…

spring DisposableBean作用,在spring Bean销毁时的钩子 以及@PreDestroy

DisposableBean 作用 在Spring框架中&#xff0c;DisposableBean是一个接口&#xff0c;它定义了一个单一的方法&#xff0c;用于在Spring容器关闭时或一个由Spring管理的Bean不再需要时执行特定的清理操作。当一个Bean实现了DisposableBean接口&#xff0c;Spring容器会在销毁…

IntelliJ IDEA - 10 款 IDEA 宝贝插件,YYDS!

好久没发这种实用贴了&#xff0c;最近用到了一些能提升工作效率的IDEA插件&#xff0c;给小伙伴们分享一下。相信我&#xff0c;我分享的这些插件&#xff0c;都是实实在在能解决实际开发场景中痛处的。 1、POJO to JSON 开发工作中&#xff0c;常常在设计完API后&#xff0c…

FPGA实现AXI4总线的读写_如何写axi4逻辑

FPGA实现AXI4总线的读写_如何写axi4逻辑 一、AXI4 接口描述 通道信号源信号描述全局信号aclk主机全局时钟aresetn主机全局复位&#xff0c;低有效写通道地址与控制信号通道M_AXI_WR_awid[3:0]主机写地址ID&#xff0c;用来标志一组写信号M_AXI_WR_awaddr[31:0]主机写地址&…

最短路问题之Bellman-Ford,SPFA算法,例题 负环

Bellman-Ford算法&#xff1a; Bellman-Ford算法用于解决带有负权边的单源最短路径问题。其基本思想是通过不断地松弛边来逐步求解最短路径。算法的主要步骤如下&#xff1a; 初始化&#xff1a;将源点到各个顶点的距离初始化为无穷大&#xff0c;源点的距离初始化为0。重复更…

IDEA2023版本创建Sping项目无法使用Java8

1. 问题复现 1.1 当前版本2023.3.2 1.2 创建项目时&#xff1a;不存在jdk8选项 提示报错 1.3 原因分析 Spring官方发布Spring Boot 3.0.0 的时候告知了一些情况&#xff0c;Java 17将成为未来的主流版本 2. 如何解决 2.1 替换创建项目的源 我们只知道IDEA页面创建Spring项目…

代码托管基础操作

在待上传代码文件夹中右键&#xff0c;打开Git Bash Here依次输入以下命令&#xff1a; git init(在本地初始化一个代码仓库&#xff0c;具体表现为会在你的文件夹里出现一个隐藏的.git文件夹) git add .&#xff08;先把代码放到本地的一个缓冲区&#xff09;添加当前目录下的…

【C++】从零开始认识泛型编程 — 模版

送给大家一句话&#xff1a; 尽管眼下十分艰难&#xff0c;可日后这段经历说不定就会开花结果。总有一天我们都会成为别人的回忆&#xff0c;所以尽力让它美好吧。 – 岩井俊二 &#xff3c;&#xff3c;\ ⱶ˝୧(๑ ⁼̴̀ᐜ⁼̴́๑)૭兯 //&#xff0f;&#xff0f; &#…

AI大模型探索之路-训练篇3:大语言模型全景解读

文章目录 前言一、语言模型发展历程1. 第一阶段&#xff1a;统计语言模型&#xff08;Statistical Language Model, SLM&#xff09;2. 第二阶段&#xff1a;神经语言模型&#xff08;Neural Language Model, NLM&#xff09;3. 第三阶段&#xff1a;预训练语言模型&#xff08…

Python基础知识(二)

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》 《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 1.输入和输出函数1.1输出函数1.2输入函数 2.常见运算符2.1赋值运算符2.2比较运算符2.3逻辑运算符2.4and逻辑与2.5or逻辑或2.6not逻…

c++二叉树的进阶--二叉搜索树

1. 二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 它的左…

Swift-27-类的初始化与销毁

Swift的初始化是一个有大量规则的固定过程。初始化是设置类型实例的操作&#xff0c;包括给每个存储属性初始值&#xff0c;以及一些其他准备工作。完成这个过程后&#xff0c;实例就可以使用了。 简单来讲就是类的构造函数&#xff0c;基本语法如下&#xff1a; 注意&#xff…

C语言扫雷游戏完整实现(上)

文章目录 前言一、新建好头文件和源文件二、实现游戏菜单选择功能三、定义游戏函数四、初始化棋盘五、 打印棋盘函数六、布置雷函数七、玩家排雷菜单八、标记功能的菜单九、标记功能菜单的实现总结 前言 C语言从新建文件到游戏菜单&#xff0c;游戏函数&#xff0c;初始化棋盘…

JavaScript-4.正则表达式、BOM

正则表达式 正则表达式包含在"/"&#xff0c;"/"中 开始与结束 ^ 字符串的开始 $ 字符串的结束 例&#xff1a; "^The"&#xff1a;表示所有以"The"开始的字符串&#xff08;"There"、"The cat"等&#x…

数据结构(八)——排序

八、排序 8.1 排序的基本概念 排序(Sort)&#xff0c;就是重新排列表中的元素&#xff0c;使表少的元素满足按关键字有序的过程。 输入∶n个记录R1,R2...., Rn&#xff0c;对应的关键字为k1, k2,... , kn 输出:输入序列的一个重排R1,R2....,Rn&#xff0c;使得有k1≤k2≤...≤…