【网络】:网络编程套接字

news2025/1/13 7:24:59

目录

源IP地址和目的IP地址

源MAC地址和目的MAC地址 

源端口号和目的端口号

端口号 VS 进程ID

TCP协议和UDP协议

网络字节序

字符串IP和整数IP相互转换

查看当前网络的状态 

socket编程接口

socket常见API

创建套接字

 绑定端口号

发送数据

接收数据 

sockaddr结构


源IP地址和目的IP地址

一、定义

  • 源 IP 地址(Source IP Address):它是发送数据的设备(如计算机、服务器等)在网络中的唯一标识。就像是发送信件时的寄件人地址,在网络通信中,它用于标识数据的来源。
  • 目的 IP 地址(Destination IP Address):这是接收数据的设备在网络中的唯一标识,类似于收件人地址。它告诉网络基础设施,数据最终要传送到哪里。

二、在网络通信中的作用

  • 数据传输导向:在互联网这样庞大的网络环境中,数据通过多个网络节点(如路由器、交换机等)进行传输。
    • 源 IP 地址和目的 IP 地址就像指南针一样,引导数据包从源设备通过复杂的网络路径最终到达目的设备。
    • 例如,当你在浏览器中访问一个网站时,你的计算机(源设备)的源 IP 地址和网站服务器(目的设备)的目的 IP 地址就确定了数据传输的起始点和终点。
  • 路由决策依据:路由器是网络中的关键设备,用于将数据包从一个网络转发到另一个网络。
    • 路由器根据数据包的目的 IP 地址来决定将其转发到哪个下一个网络节点。
    • 它会查询路由表,路由表中记录了目的 IP 地址的网络范围以及对应的转发接口。
    • 例如,如果一个数据包的目的 IP 地址属于某个特定的外部网络,路由器就会将其从连接外部网络的接口转发出去。

三、举例说明

  1. 假设你在自己的家中通过 Wi - Fi 连接的笔记本电脑访问一家在线购物网站。
  2. 你的笔记本电脑(假设其 IP 地址是 192.168.1.10)就是源设备,192.168.1.10 是源 IP 地址。
  3. 在线购物网站的服务器(假设其 IP 地址是 104.27.189.99)是目的设备,104.27.189.99 是目的 IP 地址。
  4. 当你在浏览器中输入网站的网址并发送请求时,数据从你的笔记本电脑(源)出发。
  5. 这个请求数据包包含了源 IP 地址(192.168.1.10)和目的 IP 地址(104.27.189.99)。
  6. 数据包首先到达你家中的无线路由器,路由器根据目的 IP 地址判断这个数据包需要发送到互联网服务提供商(ISP)的网络,然后经过一系列的网络设备(其他路由器等)的转发,最终到达购物网站的服务器(目的)。
  7. 在整个过程中,源 IP 地址和目的 IP 地址始终保持不变,确保数据能够准确地发送和接收。

源MAC地址和目的MAC地址 

  • 源 MAC 地址(Source MAC Address):它是发送数据的网络设备(如计算机网卡、网络交换机端口等)在局域网(LAN)中的物理地址。MAC 地址也被称为硬件地址,是由网络设备制造商固化在设备中的一个 48 位二进制数,通常表示为 12 位的十六进制数,例如 “00 - 1B - 44 - 11 - 3A - 0C”。这个地址在全球范围内是唯一的,用于在局域网环境中标识数据的发送端。
  • 目的 MAC 地址(Destination MAC Address):它是接收数据的网络设备在局域网中的物理地址,同样是一个 48 位的二进制数,用于在局域网中标识数据的目标接收端。

有些数据的传输都是跨局域网的,数据在传输过程中会经过若干个路由器,最终才能到达对端主机。

源MAC地址和目的MAC地址是包含在链路层的报头当中的,而MAC地址实际只在当前局域网内有效,因此当数据跨网络到达另一个局域网时,其源MAC地址和目的MAC地址就需要发生变化,因此当数据达到路由器时,路由器会将该数据当中链路层的报头去掉,然后再重新封装一个报头,此时该数据的源MAC地址和目的MAC地址就发生了变化。

因此数据在传输的过程中是有两套地址:

  • 一套是源IP地址和目的IP地址,这两个地址在数据传输过程中基本是不会发生变化的(存在一些特殊情况,比如在数据传输过程中使用NET技术,其源IP地址会发生变化,但至少目的IP地址是不会变化的)。
  • 另一套就是源MAC地址和目的MAC地址,这两个地址是一直在发生变化的,因为在数据传输的过程中路由器不断在进行解包和重新封装。

源端口号和目的端口号

两台主机之间通信的目的不仅仅是为了将数据发送给对端主机,而是为了访问对端主机上的某个服务(某个进程)。比如我们在用百度搜索引擎进行搜索时,不仅仅是想将我们的请求发送给对端服务器,而是想访问对端服务器上部署的百度相关的搜索服务。

其实,这种在网络中通信的本质:就是进程间通信,只是在不同的机器上进程间通信 (跨网络的进程间通信),我们把这种通信称做 套接字通信(socket通信)

socket通信本质上就是两个进程之间在进行通信,只不过这里是跨网络的进程间通信。比如逛京东和刷抖音的动作,实际就是手机上的京东进程和抖音进程在和对端服务器主机上的京东服务进程和抖音服务进程之间在进行通信。

因此进程间通信的方式除了管道、消息队列、信号量、共享内存等方式外,还有套接字,只不过前者是不跨网络的,而后者是跨网络的。

那我们是如何去实现我们的 socket通信 的呢? 

  1. 先把数据能够到达对方的机器
  2. 再在机器中能找到指定的进程,同一台机器也可能会同时存在多个正在进行跨网络通信的进程,因此当数据到达对端主机后,必须要通过某种方法找到该主机上对应的服务进程 !

端口号(port)的作用实际就是标识一台主机上的一个进程:

  1. 端口号是传输层协议的内容
  2. 端口号是一个2字节16位的整数
  3. 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理
  4. 一个端口号只能被一个进程占用

当数据在传输层进行封装时,就会添加上对应源端口号和目的端口号的信息。这时通过源IP地址+源端口号就能够在网络上唯一标识发送数据的进程,通过目的IP地址+目的端口号就能够在网络上唯一标识接收数据的进程,此时就实现了跨网络的进程间通信。

注意:

  • 因为端口号是属于某台主机的,所以端口号可以在两台不同的主机当中重复,但是在同一台主机上进行网络通信的进程的端口号不能重复。
  • 所以,一个进程可以绑定多个端口号,但是一个端口号不能被多个进程同时绑定。

端口号 VS 进程ID

端口号(port)和 进程ID(PID)的作用都是标识一台主机上唯一的一个进程,那在进行socket通信时为什么不直接用 进程ID 呢?

  • 进程ID(PID)是用来标识系统内所有进程的唯一性的,它是属于系统级的概念
  • 端口号(PORT)是用来标识需要对外进行网络数据请求的进程的唯一性的,它是属于网络的概念
  1. 一台机器上可能会有大量的进程,但并不是所有的进程都要进行网络通信,可能有很大一部分的进程是不需要进行网络通信的本地进程,此时PID虽然也可以标识这些网络进程的唯一性,但在该场景下就不太合适了
  2. 进程管理 和 网络 解耦,因为不同平台的 进程ID 的分配可能有所不同,可能会出现不兼容的问题

底层如何通过端口号(PORT)找到对应进程的?

 机器底层采用哈希的方式建立了端口号和进程PID或PCB之间的映射关系,当底层拿到端口号时就可以直接执行对应的哈希算法,然后就能够找到该端口号对应的进程。

TCP协议和UDP协议

网络协议栈是贯穿整个体系结构的,在应用层、操作系统层和驱动层各有一部分。当我们使用系统调用接口实现网络数据通信时,面对的协议层就是传输层,而传输层最典型的两种协议就是TCP协议和UDP协议

TCP协议

TCP协议叫做传输控制协议(Transmission Control Protocol),TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP协议是面向连接的,如果两台主机之间想要进行数据传输,那么必须要先建立连接,当连接建立成功后才能进行数据传输。其次,TCP协议是保证可靠的协议,数据在传输过程中如果出现了丢包、乱序等情况,TCP协议都有对应的解决方法。

UDP协议

UDP协议叫做用户数据报协议(User Datagram Protocol),UDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议。

使用UDP协议进行通信时无需建立连接,如果两台主机之间想要进行数据传输,那么直接将数据发送给对端主机就行了,但这也就意味着UDP协议是不可靠的,数据在传输过程中如果出现了丢包、乱序等情况,UDP协议本身是不知道的。

既然UDP协议是不可靠的,那为什么还要有UDP协议的存在?

TCP协议可靠是需要我们做更多的工作的,TCP协议虽然是一种可靠的传输协议,但这一定意味着TCP协议在底层需要做更多的工作,因此TCP协议底层的实现是比较复杂的,我们不能只看到TCP协议面向连接可靠这一个特点,我们也要能看到TCP协议对应的缺点。

UDP协议虽然是一种不可靠的传输协议,但这一定意味着UDP协议在底层不需要做过多的工作,因此UDP协议底层的实现一定比TCP协议要简单,UDP协议虽然不可靠,但是它能够快速的将数据发送给对方,虽然在数据在传输的过程中可能会出错。

编写网络通信代码时具体采用TCP协议还是UDP协议,完全取决于上层的应用场景。如果应用场景严格要求数据在传输过程中的可靠性,此时我们就必须采用TCP协议,如果应用场景允许数据在传输出现少量丢包,那么我们肯定优先选择UDP协议,因为UDP协议足够简单。

注意: 一些优秀的网站在设计网络通信算法时,会同时采用TCP协议和UDP协议,当网络流畅时就使用UDP协议进行数据传输,而当网速不好时就使用TCP协议进行数据传输,此时就可以动态的调整后台数据通信的算法。

网络字节序

计算机在存储数据时是有大小端的概念的:

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

如果编写的程序只在本地机器上运行,那么是不需要考虑大小端问题的,因为同一台机器上的数据采用的存储方式都是一样的

但是如果涉及网络通信,那就必须考虑大小端的问题,否则对端主机识别出来的数据可能与发送端想要发送的数据是相反的。

例如,现在两台主机之间在进行网络通信,其中发送端是小端机,而接收端是大端机。发送端将发送缓冲区中的数据按内存地址从低到高的顺序发出后,接收端从网络中获取数据依次保存在接收缓冲区时,也是按内存地址从低到高的顺序保存的,就会出现想要发送的数据是相反的。

因此TCP/IP协议规定,网络数据流采用大端字节序,即低地址高字节。无论是大端机还是小端机,都必须按照TCP/IP协议规定的网络字节序来发送和接收数据。

  1. 如果发送端是小端,需要先将数据转成大端,然后再发送到网络当中
  2. 如果发送端是大端,则可以直接进行发送
  3. 如果接收端是小端,需要先将接收到数据转成小端后再进行数据识别
  4. 如果接收端是大端,则可以直接进行数据识别

为什么网络字节序采用的是大端?而不是小端?

网络字节序采用的是大端,而主机字节序一般采用的是小端,那为什么网络字节序不采用小端呢?如果网络字节序采用小端的话,发送端和接收端在发生和接收数据时就不用进行大小端的转换了。

说法一: TCP在Unix时代就有了,以前Unix机器都是大端机,因此网络字节序也就采用的是大端,但之后人们发现用小端能简化硬件设计,所以现在主流的都是小端机,但协议已经不好改了。
说法二: 大端序更符合现代人的读写习惯。

网络字节序与主机字节序之间的转换

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,系统提供了四个函数,可以通过调用以下库函数实现网络字节序和主机字节序之间的转换。

#include <arpa/inet.h>

// 将一个 32 位的主机字节序整数转换为网络字节序
uint32_t htonl(uint32_t hostlong);

// 将一个 16 位的主机字节序整数转换为网络字节序
uint16_t htons(uint16_t hostshort);

// 将一个 32 位的网络字节序整数转换为主机字节序
uint32_t ntohl(uint32_t netlong);

// 将一个 16 位的网络字节序整数转换为主机字节序
uint16_t ntohs(uint16_t netshort);
  1. 函数名当中的h表示主机字节序(host),n表示网络字节序(network),l表示32位长整数,s表示16位短整数
  2. 例如htonl表示将32位长整数从主机字节序转换为网络字节序
  3. 如果主机是小端字节序,则这些函数将参数做相应的大小端转换然后返回
  4. 如果主机是大端字节序,则这些函数不做任何转换,将参数原封不动地返回

字符串IP和整数IP相互转换

IP地址的表现形式有两种:

  • 字符串IP:类似于192.168.1.1这种字符串形式的IP地址,叫做基于字符串的点分十进制IP地址
  • 整数IP:IP地址在进行网络传输时所用的形式,用一个32位的整数来表示IP地址
    • 192.168.1.1为例,192的二进制是11000000168的二进制是101010001的二进制是00000001,连接起来得到11000000101010000000000100000001,转换为十进制就是3232235777

字符串IP和整数IP的相互转换函数

// 字符串IP 转换成 整数IP
in_addr_t inet_addr(const char* cp);

// 整数IP转换成字符串IP
char* inet_ntoa(struct in_addr in);

查看当前网络的状态 

netstat 命令

netstat常用选项说明:

  • -n:直接使用IP地址,而不通过域名服务器。
  • -l:显示监控中的服务器的Socket。
  • -t:显示TCP传输协议的连线状况。
  • -u:显示UDP传输协议的连线状况。
  • -p:显示正在使用Socket的程序识别码和程序名称

netstat命令显示的信息中:

  1. Proto 表示协议的类型
  2. Recv-Q 表示网络接收队列
  3. Send-Q 表示网络发送队列
  4. Local Address 表示本地地址
  5. Foreign Address 表示外部地址
  6. State 表示当前的状态
  7. PID 表示该进程的进程ID
  8. Program name 表示该进程的程序名称。
  9. 其中Foreign Address写成0.0.0.0:*表示任意IP地址、任意的端口号的程序都可以访问当前进程。

socket编程接口

socket常见API

头文件

#include <sys/types.h>
#include <sys/socket.h>

创建套接字:(TCP/UDP,客户端+服务器)

int socket(int domain, int type, int protocol);

绑定端口号:(TCP/UDP,服务器)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

监听套接字:(TCP,服务器)

int listen(int sockfd, int backlog);

接收请求:(TCP,服务器)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

建立连接:(TCP,客户端)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

发送数据:

// 用于面向连接的套接字,将数据发送到已连接的对等方
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// 用于无连接的套接字或未连接的面向连接套接字,将数据发送到指定地址
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

 接收数据:

// 用于面向连接的套接字,从已连接的对等方接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// 用于无连接的套接字或未连接的面向连接套接字,接收数据并获取发送方地址
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

关闭套接字:

#include <unistd.h>
// 用于关闭套接字,释放资源
int close(int fd);

#include <sys/socket.h>
// 可以更灵活地关闭套接字的某些功能,如只关闭发送或接收功能
int shutdown(int sockfd, int how);

创建套接字

创建套接字(socket)的函数,如下:

int socket(int domain, int type, int protocol);

参数:

  • domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。该参数就相当于struct sockaddr结构的前16个位
    • 如果是本地通信就设置为AF_UNIX
    • 如果是网络通信就设置为AF_INET(IPv4)或 AF_INET6(IPv6)。
  • type:创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAM和SOCK_DGRAM
    • 如果是基于UDP 的网络通信,我们采用的就是SOCK_DGRAM,叫做用户数据报服务,
    • 如果是基于TCP 的网络通信,我们采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服务。
  • protocol:创建套接字的协议类别。你可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,设置为0表示的就是默认,此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。

返回值:

  • 套接字创建成功返回一个文件描述符,创建失败返回-1,同时错误码会被设置。

socket函数属于什么类型的接口?

网络协议栈是分层的,按照TCP/IP四层模型来说,自顶向下依次是应用层、传输层、网络层和数据链路层。

我们现在所写的代码都叫做用户级代码,也就是说我们是在应用层编写代码,因此我们调用的实际是下三层的接口,而传输层和网络层都是在操作系统内完成的,也就意味着我们在应用层调用的接口都叫做系统调用接口

socket函数是被谁调用的?

socket这个函数是被程序调用的,但并不是被程序在编码上直接调用的,而是程序编码形成的可执行程序运行起来变成进程,当这个进程被CPU调度执行到socket函数时,然后才会执行创建套接字的代码,也就是说socket函数是被进程所调用的

socket函数底层如何实现的

socket函数是被进程所调用的,而每一个进程在系统层面上都有一个进程地址空间PCB(task_struct)、文件描述符表(files_struct)以及对应打开的各种文件。

而文件描述符表里面包含了一个数组fd_array,其中数组中的0、1、2下标依次对应的就是标准输入、标准输出以及标准错误。

当我们调用socket函数创建套接字时,实际相当于我们打开了一个“网络文件”,打开后在内核层面上就形成了一个对应的struct file结构体,同时该结构体被连入到了该进程对应的文件双链表,并将该结构体的首地址填入到了fd_array数组当中下标为3的位置,此时fd_array数组中下标为3的指针就指向了这个打开的“网络文件”,最后3号文件描述符作为socket函数的返回值返回给了用户。

每一个struct file结构体中包含的就是对应打开文件各种信息,比如文件的属性信息、操作方法以及文件缓冲区等

  1. 文件对应的属性在内核当中是由struct inode结构体来维护的
  2. 文件对应的操作方法实际就是一堆的函数指针(比如read*)在内核当中就是由struct file_operations结构体来维护的
  3. 文件缓冲区对于打开的普通文件来说对应的一般是磁盘,但对于现在打开的“网络文件”来说,这里的文件缓冲区对应的就是网卡

 

对于一般的普通文件来说,当用户通过文件描述符将数据写到文件缓冲区,然后再把数据刷到磁盘上就完成了数据的写入操作。

而对于现在socket函数打开的“网络文件”来说,当用户将数据写到文件缓冲区后,操作系统会定期将数据刷到网卡里面,而网卡则是负责数据发送的,因此数据最终就发送到了网络当中。

 绑定端口号

绑定的函数叫做bind,该函数的函数原型如下:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • sockfd:绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符
  • addr:网络相关的属性信息,包括协议家族、IP地址、端口号等
  • addrlen:传入的addr结构体的长度

返回值:

  • 绑定成功返回0,绑定失败返回-1,同时错误码会被设置

在绑定时需要将网络相关的属性信息(包括协议家族、IP地址、端口号等)填充到一个结构体当中,然后将该结构体作为bind函数的第二个参数进行传入,这实际就是struct sockaddr_in结构体

struct sockaddr_in结构体当中的成员如下:

  1. sin_family:表示协议家族
    1. 如果是本地通信就设置为 AF_UNIX
    2. 如果是网络通信就设置为 AF_INET(IPv4)或 AF_INET6(IPv6)
  2. sin_port:表示端口号,是一个16位的整数
  3. sin_addr:表示IP地址,是一个32位的整数
    1. sin_addr结构体中就一个成员(s_addr),该成员就是一个32位的整数,IP地址实际就是存储在这个整数当中的

发送数据

  • send 用于面向连接的套接字,将数据发送到已连接的对等方
  • sendto 用于无连接的套接字或未连接的面向连接套接字,将数据发送到指定地址
// 用于面向连接的套接字,将数据发送到已连接的对等方
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// 用于无连接的套接字或未连接的面向连接套接字,将数据发送到指定地址
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

参数: 

  • sockfd:套接字描述符。
  • buf:指向要发送数据的缓冲区的指针。
  • len:要发送数据的长度。
  • flags:控制发送操作的标志,如 MSG_DONTWAIT (不阻塞)等。
  • dest_addr(仅 sendto):指向接收方地址的指针。
  • addrlen(仅 sendto):接收方地址结构体的长度。

返回值:

  • 成功时:返回实际发送的字节数。这个数字可能小于 len,表示只发送了部分数据。这通常发生在以下情况:
    • 发送缓冲区已满,无法一次性发送全部数据,需要多次调用 send 函数将剩余数据发送完。
  • 失败时:返回 -1

接收数据 

  • recv 用于面向连接的套接字,从已连接的对等方接收数据
  • recvfrom 用于无连接的套接字或未连接的面向连接套接字,接收数据并获取发送方地址 
// 用于面向连接的套接字,从已连接的对等方接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// 用于无连接的套接字或未连接的面向连接套接字,接收数据并获取发送方地址
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

参数: 

  • sockfd:套接字描述符。
  • buf:指向接收数据的缓冲区的指针。
  • len:缓冲区的长度。
  • flags:控制接收操作的标志,如 MSG_DONTWAIT (不阻塞)等。
  • src_addr(仅 recvfrom):指向发送方地址的指针。
  • addrlen(仅 recvfrom):指向发送方地址结构体长度的指针。

返回值:

  • 成功时:返回接收到的字节数,如果连接已经正常关闭,返回值为 0,表示对方已关闭连接。
  • 失败时:返回 -1

sockaddr结构

套接字不仅支持跨网络的进程间通信,还支持本地的进程间通信(域间套接字)。在进行跨网络通信时我们需要传递的端口号和IP地址,而本地通信则不需要,因此套接字提供了sockaddr_in结构体和sockaddr_un结构体:

  • sockaddr_in 结构体是用于跨网络通信的
  • sockaddr_un 结构体是用于本地通信的

为了让套接字的网络通信和本地通信能够使用同一套函数接口,于是就出现了sockeaddr结构体,该结构体与sockaddr_in和sockaddr_un的结构都不相同,但这三个结构体头部的16个比特位都是一样的,这个字段叫做协议家族

注意: 实际我们在进行网络通信时,定义的还是sockaddr_in这样的结构体,只不过在传参时需要将该结构体的地址类型进行强转为sockaddr* 了。

为什么没有用 void* 代替 struct sockaddr* 类型?

我们可以将这些函数的 struct sockaddr* 参数类型改为 void*,此时在函数内部也可以直接指定提取头部的16个比特位进行识别,最终也能够判断是需要进行网络通信还是本地通信,那为什么还要设计出sockaddr这样的结构呢?

实际在设计这一套网络接口的时候C语言还不支持void*,于是就设计出了sockaddr这样的解决方案。并且在C语言支持了void*之后也没有将它改回来,因为这些接口是系统接口,系统接口是所有上层软件接口的基石,系统接口是不能轻易更改的,否则引发的后果是不可想的,这也就是为什么现在依旧保留sockaddr结构的原因。

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

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

相关文章

UnityDemo-TheBrave-制作笔记

这是我跟着b站up主MStudio的视频学习制作的&#xff0c;大体上没有去做一些更新的东西&#xff0c;这里只是一个总的总结。在文章的最后&#xff0c;我会放上可以游玩该游戏的链接和exe可执行文件&#xff0c;不过没有对游戏内容进行什么加工&#xff0c;只有基本的功能实现罢了…

力扣经典二分题:4. 寻找两个正序数组的中位数

题目链接&#xff1a;4. 寻找两个正序数组的中位数 - 力扣&#xff08;LeetCode&#xff09; 一、题目分析 这道题目是让我们在 两个正序的数组中寻找中位数已知两个数组的大小分别是&#xff1a;int m nums1.size(),n nums2.size();中位数性质1&#xff1a;中位数左侧元素 …

C++ 文字识别OCR

一.引言 文字识别&#xff0c;也称为光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;是一种将不同形式的文档&#xff08;如扫描的纸质文档、PDF文件或数字相机拍摄的图片&#xff09;中的文字转换成可编辑和可搜索的数据的技术。随着技…

SpringBoot:SaToken的options预检请求鉴权失败

问题描述 使用如下sa-token配置&#xff0c;前端通过IP端口号的方式访问后端服务&#xff0c;会存在options预检请求鉴权失败的问题。 问题分析 http-options请求 HTTP OPTIONS 方法请求给定的 URL 或服务器的允许通信选项。客户端可以用这个方法指定一个 URL&#xff0c;或者…

UE材质函数

材质函数是可在不同材质中重复使用的材质表达式的一个集合 相当于把常用的功能封装到一个集合里&#xff0c;需要用到的时候调用 输入input可以添加输入节点 如果勾上公开到库&#xff0c;就可以在材质面板直接搜索到材质函数 材质函数可以直接做成一个输出

51c~Pytorch~合集5

我自己的原文哦~ https://blog.51cto.com/whaosoft/13059544 一、PyTorch DDP 正在郁闷呢 jetson nx 的torchvision安装~~ 自带就剩5g 想弄到ssd 项目中的 venv中又 cuda.h没有... 明明已经装好什么都对 算了说今天主题 啊对 还是搬运啊 学习之工具人而已 勿怪 Distrib…

用vscode写latex-1

一般大伙使用 LaTeX 大体有两种方案&#xff0c; 一种是在本地配置环境或使用本地的软件&#xff0c;如 vscode LaTeX&#xff0c;texlive&#xff0c;lyx 等等&#xff1b; 另一种是线上 LaTeX 平台&#xff0c;其中用的最多的是 Overleaf&#xff0c;还有一部分高校也有自…

用户界面软件05

已知应用 几乎所有的流行的用户界面架构都使用这种模式。我在这里举三个例子&#xff1a; 1. Seeheim 用户界面架构的特点是有一个应用核心的领域层和一个用户界面层。后者 被分为两层&#xff0c;叫做表示层和对话控制层。因为这个架构和面向事务系统有渊源&#xff0c;没有…

虚拟文件系统 VFS

目录 虚拟文件系统 VFS 文件系统挂载过程 虚拟文件系统 VFS 统一标准的系统调用接口&#xff1a; VFS定义了一组标准的文件操作API&#xff0c;如open(), read(), write(), close()等&#xff0c;使得用户空间的应用程序无需关心底层文件系统的具体类型。 下层文件系统必须实现…

conda 批量安装requirements.txt文件

通常可以用下面conda命令安装requirements.txt文件 conda install --yes --file requirements.txt 但是&#xff0c;一旦遇到某个包安装失败&#xff0c;就会停止安装后续的包。 下面这条命令能解决上面出现的不执行后续包的问题&#xff0c;需要在CMD窗口执行&#xff1a; 点…

Auto-CoT:自动构建大模型的思维链提示

今天分享的是由上海交通大学发表的一篇文章&#xff1a;Auto-COT 论文题目&#xff1a;AUTOMATIC CHAIN OF THOUGHT PROMPTING IN LARGE LANGUAGE MODELS 论文链接&#xff1a;https://arxiv.org/pdf/2210.03493 代码地址&#xff1a;https://github.com/amazon-science/aut…

算法练习7——拦截导弹的系统数量求解

题目描述 某国为了防御敌国的导弹袭击&#xff0c;发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷&#xff1a;虽然它的第一发炮弹能够到达任意的高度&#xff0c;但是以后每一发炮弹都不能高于前一发的高度。 假设某天雷达捕捉到敌国的导弹来袭。由于该系统还在试用…

命令行中打印二维码

前公司中登录设备时&#xff0c;需要下发密钥&#xff0c;密钥是通过扫描终端中的二维码获得的&#xff0c;终端中的二维码类似这样&#xff1a; 当时没理解原理&#xff0c;现在研究了下如何在命令行中打印二维码。主要介绍其中一些开源工具&#xff1a; 1. qrencode 简介 …

Android车机DIY开发之软件篇(八)单独编译

Android车机DIY开发之软件篇(八)单独编译 1.CarLauncher单独编译 CarLauncher源码位于 packages/apps/Car/Launcher 用Eclipse ADT 谷歌定制版编译而成&#xff0c;.mk .bp编译 Android13目录如下: alientekalientek:~/packages/apps/Car$ ls Calendar …

3 前端: Web开发相关概念 、HTML语法、CSS语法

文章目录 前言:导学1 Web开发相关概念2 Web标准(网页标准)3 软件架构(CS/BS)(1)C/S: Client/Server 客户端 / 服务器端(2)B/S: Browser/Server 浏览器 / 服务器端VSCode配置前段开发环境一、HTML概念1 概念2 HTML快速入门(1)语法快速入门(2)VSCode一个 !(快捷键…

Redis:内存管理

1. 最大内存限制 (maxmemory) 简介 概念:maxmemory 参数用于设置 Redis 实例允许使用的最大内存量。作用:当 Redis 使用的内存达到这个限制时,它会根据配置的淘汰策略来决定如何处理新的写入请求或移除现有数据。示例 # 设置最大内存为2GB maxmemory 2gb注意事项 合理规划…

C语言进阶——1数据的存储

目录 1. 数据类型介绍1.1 类型的基本归类&#xff1a;1.1.1 整形家族1.1.2 浮点型家族1.1.3 构造类型&#xff1a;1.1.4.指针类型1.1.5 void 表示空类型&#xff08;无类型&#xff09; 2. 整形在内存中的存储2.1 原码、反码、补码2.1.1 示例 20在计算机中的存储 2.2 大小端2.2…

OpenAI 故障复盘 - 阿里云容器服务与可观测产品如何保障大规模 K8s 集群稳定性

本文作者&#xff1a; 容器服务团队&#xff1a;刘佳旭、冯诗淳 可观测团队&#xff1a;竺夏栋、麻嘉豪、隋吉智 一、前言 Kubernetes(K8s)架构已经是当今 IT 架构的主流与事实标准&#xff08;CNCF Survey[1]&#xff09;。随着承接的业务规模越来越大&#xff0c;用户也在使…

【微服务】面试 1、概述和服务发现

微服务面试题 课程内容架构 Spring Cloud 部分 服务注册&#xff1a;重点讲解&#xff08;Nacos&#xff09;和&#xff08;Eureka&#xff09;&#xff0c;这是微服务架构中实现服务发现与注册的关键组件&#xff0c;确保服务间能够相互定位与通信。负载均衡&#xff1a;涵盖…

Mybatis冲冲冲00(含如何配置模版文件)

官方网站:mybatis – MyBatis 3 | 入门 mybatis安装方式1.下jar包(网址在上),2.Maven(代码也由上可找) 两个核心配置文件 1.核心主配置文件:configuration.xml(文件名自定义),通常设置为mybatis-config.xml 2.SQL映射文件:mapper.xml(文件名自定义),通常设置为:实体类名.xml…