【网络】网络基础知识(协议、mac、ip、套接字)
文章目录
- 1. 计算机网络的背景
- 2. 认识网络协议
- 2.1 协议分层
- 2.2 OS与网络的关系
- 3. 网络传输基本流程
- 3.1 局域网通信流程
- 3.2 跨网络通信流程
- 4. Socket 编程预备
- 4.1 理解源IP地址和目的IP地址
- 4.2 端口号与Socket
- 4.3传输层的典型代表
- 4.4 网络字节序
- 5. socket 编程接口
- 5.1 介绍
- 5.2 案例演示

1. 计算机网络的背景
所谓的 “局域网” 和 “广域网” 只是一个相对的概念。
2. 认识网络协议
我们都知道,协议就是“双方”都必须遵守的约定。
那什么叫网络协议呢?
计算机生产厂商有很多,计算机操作系统也有很多,计算机网络硬件设备还是有很多。
如何让这些不同厂商之间生产的计算机能够相互顺畅的通信呢?就需要有人站出来,约定一个共同的标准,大家都来遵守,这就是网络协议。
无论是电脑、手机还是其他设备,只要它们遵循相同的网络协议,就能在网络上互相通信、交换数据。
2.1 协议分层
协议本质也是软件,在设计上为了更好的进行模块化、解耦合,也是被设计成为层状结构的。
在现实生活中,我们可以通过电话直接和朋友交流,因此我们认为“同层协议可以直接通信”;但本质上,同层协议之间没有直接通信,而是各自使用下层结构的能力,完成通信的,我们本质上是在对电话讲话。
通过上图我们发现,任何一层的变化,只要遵守协议,都不会影响其它层,所以分层可以实现解耦合,让软件维护的成本更低。
- OSI的七层模型
很多书中都讲了7层不好,其实在网络角度, OSI 定的协议7层模型其实非常完善,但是在实际操作的过程中,会话层、表示层是不可能接入到操作系统中的所以在工程实践中,最终落地的是 5 层协议。
- TCP/IP 五层(或四层)模型
TCP/IP 是一组协议的代名词,它还包括许多协议,组成了 TCP/IP 协议族。
TCP/IP 通讯协议采用了 5 层的层级结构, 每一层都呼叫它的下一层所提供的网络来完成自己的需求。
- 物理层:负责光/电信号的传递方式。比如现在以太网通用的网线(双绞 线)、 早 期以太网采用的的同轴电缆(现在主要用于有线电视)、 光纤,现在的 wifi 无线网使用 电磁波等都属于物理层的概念。 物理层的能力决定了最大传输速率、 传输距离、 抗 干扰性等。
集线器(Hub)工作在物理层。
- 数据链路层: 负责设备之间的数据帧的传送和识别。例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、 冲突检测(如果检测到冲突就 自动重发)、数据差错校验等工作。有以太网、令牌环网、无线 LAN 等标准。
交换机 (Switch)工作在数据链路层
.- 网络层: 负责地址管理和路由选择。 例如在 IP 协议中, 通过IP 地址来标识一台 主机,,并通过路由表的方式规划出两台主机之间的数据传输的线路(路由)。
路由器 (Router)工作在网路层.
- 传输层: 负责两台主机之间的数据传输。如传输控制协议 (TCP), 能够确保数据 可靠的从源主机发送到目标主机.
- 应用层: 负责应用程序间沟通, 如简单电子邮件传输(SMTP)、文件传输协 议(FTP)、 网络远程访问协议(Telnet) 等.
网络编程主要就是针对应用层
物理层我们考虑的比较少, 我们只考虑软件相关的内容,因此很多时候我们直接称为TCP/IP 四层模型。
- 对于一台主机,它的操作系统内核实现了从传输层到物理层的内容
- 对于一台路由器,它实现了从网络层到物理层
- 对于一台交换机,它实现了从数据链路层到物理层
- 对于集线器,它只实现了物理层
但是并不绝对,很多交换机也实现了网络层的转发;很多路由器也实现了部分传输层的内容(比如端口转发)。
- 重新理解协议
上面的内容,我们只是懂了一些基本概念,还是达不到我们的目标,下面我们再次重新理解协议和协议分层:
那么为什么要有 TCP/IP 协议?
- 首先,即便是单机,你的计算机内部,其实都是存在协议的,比如:其他设备和内存通信,会有内存协议。 其他设备和磁盘通信,会有磁盘相关的协议,比如:SATA, IDE, SCSI 等。 只不过我们感知不到罢了。 而且这些协议都在本地主机各自的硬件中,通信的成本、问题比较少。
- 其次,网络通信最大的特点就是主机之间变远了。 任何通信特征的变化,一定会带来新的问题,有问题就得解决问题,所以需要新的协议。
所以,协议产生的本质就是:解决因距离变远而产生的通信问题。
由于距离变远产生的问题多种多样,所以要将它们分类,即协议要分层。
2.2 OS与网络的关系
我们都清楚的知道,操作系统各式各样,没有标准,但是为什么不同的操作系统间能够通信呢?
那是因为不同的操作系统中,网络协议栈必须按照TCP/IP协议标准来实现,即不同操作系统使用的网络协议栈一样,那就可以通信了。
所以,操作系统与网络的关系是:操作系统内要实现网络相关的功能,即网络是操作系统的一个模块。
那么到底什么是协议呢?
既然操作系统要实现网络的功能,那OS内就会存在大量的协议,OS就要管理协议,所以协议本质就是数据结构,即结构体
。
问题: 主机 B 能识别 data, 并且准确提取 a=10, b=20, c=30 吗? 回答: 答案是肯定的!
因为双方都有同样的结构体类型 struct protocol。 也就是说,用同样的代码实现协议,用同样的自定义数据类型,天然就具有”共识“, 能够识别对方发来的数据,这不就是约定吗?
关于协议的朴素理解: 所谓协议, 就是通信双方都认识的结构化的数据类型(结构体)
3. 网络传输基本流程
3.1 局域网通信流程
首先,两台主机在同一个局域网,是否能够直接通信? - - 是的,一台主机发送的信息,在同一个局域网下的所有主机,都可以收到。
只不过在局域网上的每台主机,要有唯一的标识来保证主机的唯一性: mac 地址。
mac地址长度为 48 位,即 6 个字节。一般用 16 进制数字加上冒号的形式来表示(例如:08:00:27:03:fb:19), 在网卡出厂时就确定了,不能修改,mac 地址通常是唯一的。
以太网中,任何时刻,只允许一台机器向网络中发送数据
- 如果有多台同时发送, 会发生数据干扰, 我们称之为数据碰撞
所以,以太网就是临界资源,是互斥访问的,但是不加锁,先用,碰撞出错了再说。
- 所有发送数据的主机要进行
碰撞检测
和碰撞避免
(以保证数据读写的原子性)- 没有交换机的情况下,一个以太网就是一个碰撞域
- 局域网通信的过程中,
主机对收到的报文确认是否是发给自己的,是通过目标mac地址在数据链路层判定的,若不是,直接丢弃,上层用户感知不到。
在数据传输时,要经过每一层;其中每层都有协议,所以当我进行数据传输流程的时候,要进行封装和解包
报头部分(对应协议层的结构体字段)我们一般叫做报头;除了报头 剩下的叫做有效载荷,故报文 = 报头 + 有效载荷。
我们在明确一下不同层的完整报文的叫法,不同的协议层对数据包有不同的称谓
- 在传输层叫做段(segment),
- 在网络层叫做数据报 (datagram),
- 在链路层叫做帧(frame).
- 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部,称为封装。
- 首部信息中包含了一些类似于首部有多长,载荷有多长,上层协议是什么等信息。
- 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,根据首部中的 “上层协议字段” 将数据交给对应的上层协议处理。
从今天开始, 我们学习任何协议, 都要先宏观上建立这样的认识:
- 要学习的协议,是如何做到解包的? 只有明确了解包, 封包也就能理解
- 要学习的协议,是如何做到将自己的有效载荷, 交付给上层协议的?
3.2 跨网络通信流程
IP 地址是在 IP 协议中,用来标识网络中不同主机的地址。
- 对于 IPv4 来说, IP 地址是一个 4 字节,32 位的整数;
- 我们通常也使用 “点分十进制” 的字符串表示 IP 地址,例如 192.168.0.1;用点分割的每一个数字表示一个字节,范围是 0 - 255;
可是,你们又没有一个疑问:上面我们所说的mac地址也能唯一标识一个主机,但二者有什么区别呢?
唐僧每经过一个城池,都会询问国王下一个地方它去哪,它给国王说的是:我从长安来,到西天去。
国王根据唐僧的目的地,告诉唐僧距离当前位置最近的下一个城池。
所以:
- IP地址是最终目标,用来标识整个网络中标识唯一性。
- MAC地址是近期目标,用来标识主机在局域网中的唯一性。其受IP地址影响,根据路由器完成mac地址的切换(mac地址仅在局域网中有效)
跨网段的主机的数据传输,数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。
为什么要去目标主机, 先要走路由器?
结合封装与解包,体现路由器解包和重新封装的特点
提炼 IP 网络的意义和网络通信的宏观流程
所以,IP网络层存在的意义:提供网络虚拟层,让世界的所有网络都是IP网络,屏蔽最底层网络的差异。
4. Socket 编程预备
4.1 理解源IP地址和目的IP地址
IP 在网络中,是用来标识主机的唯一性的,可以通过IP传输数据。
但是这里要思考一个问题: 数据传输到主机是目的吗? 不是的,因为数据是给人用的。
- 比如: 聊天是人在聊天,下载是人在下载,浏览网页是人在浏览?
- 但是人是怎么看到聊天信息的呢? 怎么执行下载任务呢? 怎么浏览网页信息呢?通过启动的 qq,迅雷,浏览器。
- 而启动的 qq,迅雷,浏览器都是进程。 换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于就拿到了数据。
所以: 数据传输到主机不是目的,而是手段。 到达主机内部,再交给主机内的进程,才是目的。
那么,源IP地址和目的IP地址是干什么的呢?- - 解决两端主机的唯一性
。
但是系统(主机)中,同时会存在非常多的进程, 当数据到达目标主机之后,怎么转发给目标进程呢?
4.2 端口号与Socket
端口号(port)是传输层协议的内容。
- 端口号是一个2字节16 位的整数
端口号用来标识一个进程
,告诉操作系统,当前的这个数据要交给哪一个进程来处理- 一个端口号只能被一个进程占用
端口号范围划分
0 - 1023
:知名端口号, HTTP、FTP、SSH 等这些广为使用的应用层协议,他们的端口号都是固定的。1024 - 65535
:操作系统动态分配的端口号,客户端程序的端口号就是由操作系统从这个范围分配的。
我们之前在学习系统编程的时候,学习了 pid 表示唯一一个进程;此处我们的端口号也是唯一表示一个进程,那么这两者之间是怎样的关系?
- 进程 PID 属于系统概念,技术上也具有唯一性,确实可以用来标识唯一的一个进程, 但是这样做, 会让系统进程管理和网络强耦合,实际设计的时候, 并没有选择这样做。
- 另外,一个进程可以绑定多个端口号, 但是一个端口号不能被多个进程绑定
传输层协议(TCP 和 UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述 "数据是谁发的,要发给谁"
;
那么, IP 地址 + 端口号就能够标识网络上的某一台主机的某一个进程,所以网络通信的本质是:进程间通信,网络就是它们看到的同一份资源,我们把IP 地址 + 端口号叫做Socket(套接字)
4.3传输层的典型代表
如果我们了解了系统,也了解了网络协议栈,我们就会清楚,传输层是属于内核的,那么我们要通过网络协议栈进行通信,必定调用的是传输层提供的系统调用,来进行的网络通信。
认识 TCP 协议,此处我们先对 TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识
- 传输层协议
- 有连接
可靠传输
- 面向字节流
认识 UDP 协议,此处我们也是对 UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识;
- 传输层协议
- 无连接
不可靠传输
- 面向数据报
4.4 网络字节序
我们知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,大端与小端机器通信,要怎么办呢?
所以,必须在网络中将大小端规定好!
- 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
- 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
- 因此,
网络数据流的地址应这样规定:先发出的数据是低地址(低权值位),后发出的数据是高地址(高权值位)
。- TCP/IP协议规定:网络数据流应采用大端字节序,即低地址处是高权值位。
- 不管这台主机是大端机还是小端机,都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;
- 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可
因此,网络规定:所有发送到网络上的数据,都必须是大端的
为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
这些函数名很好记,h表示host,n表示network,l 表示32位长整数(常用来
转ip
),s表示16位短整数(常用来转port
)。
- 例如 htonl表示将 32 位的长整数从主机字节序转换为网络字节序,例如将 IP 地 址转换后准备发送。
- 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
- 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
5. socket 编程接口
5.1 介绍
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
在上述接口中,很多都有一个 sockaddr 的结构体,这是因为套接字的种类有很多:网络socket、本地socket(域间socket)、原始socket。
OS为了提供一个统一的系统调用接口,设计了sockaddr 的结构体
用户具体使用什么类型的socket不管,只要你传进来的指针是sockaddr类型的;OS内部为了区分是网络还是本地,在这三个结构中,大家的前两个字节用来标识地址的类型。 (这在C++中不就是继承与多态嘛)
5.2 案例演示
下面使用一个简单的回显服务器和客户端代码,演示一些UDP中常用的接口
服务器端:
- 创建套接字
该系统调用的返回值是一个文件描述符,所以创建socket本质是创建了一个文件。
// 1. 创建UDP网络socket
int _socket = ::socket(AF_INET, SOCK_DGRAM, 0);
- 绑定端口号
在绑定socket之前,应初始化一下socket;但在上面我们说了,socket的种类很多,我们这里使用网络socket为例,所以应该先创建一个sockaddr_in类型的socket。
在初始化sockaddr_in之前,需要注意port与IP的网络字节序问题
struct sockaddr_in inetSocket;
bzero(&inetSocket,sizeof(inetSocket)); //sockaddr_in全部清零,也可用memset
inetSocket.sin_family = AF_INET;
inetSocket.sin_port = ::htons(_port); //端口-->网络字节序
inetSocket.sin_addr.s_addr = ::inet_addr(_ip.c_str()); //string -->4字节-->网络字节序
绑定
// 3. 设置套接字进入内核
int n = ::bind(_socket,(sockaddr*)&inetSocket,sizeof(inetSocket));
- 接收信息
struct sockaddr_in fromUser;
socklen_t len = sizeof(fromUser);
char buffer[1024];
int n = ::recvfrom(_socket,&buffer,sizeof(buffer),0,(sockaddr*)(&fromUser),&len);
- 发送信息给指定socket
::sendto(_socket,echoString.c_str(),echoString.size(),0,(sockaddr*)(&fromUser),len);
Server完整代码:
//Common.hpp
#pragma once
#include <stdlib.h> //exit#define Die(code) \do \{ \exit(code); \} while (0)#define CONVERSE(src) (struct sockaddr*)(src)enum exitCode
{SOCKET_ERR = 1,BIND_ERR
};
#pragma once#include <memory>
#include <string>
#include <cstring>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"
#include "Common.hpp"const static int g_socket = -1;
const static int g_port = 8080;
const static std::string g_default_ip = "127.0.0.1";using namespace MyLogModule;
class UdpServer
{
public:UdpServer(uint16_t port = g_port, std::string ip = g_default_ip): _socket(g_socket), _port(port), _ip(ip), _isRunning(false){}~UdpServer(){}void Init(){// 1. 创建网络socket_socket = ::socket(AF_INET, SOCK_DGRAM, 0);if (_socket == -1){LOG(FATAL) << "socket create fail";Die(SOCKET_ERR);}LOG(NORMAL) << "file fd:" << _socket;// 2. 向套接字中填充网络信息struct sockaddr_in inetSocket;bzero(&inetSocket, sizeof(inetSocket)); // sockaddr_in全部清零,也可用memsetinetSocket.sin_family = AF_INET;inetSocket.sin_port = ::htons(_port); // 端口-->网络字节序inetSocket.sin_addr.s_addr = ::inet_addr(_ip.c_str()); // string -->4字节-->网络字节序// 3. 设置套接字进入内核int n = ::bind(_socket, CONVERSE(&inetSocket), sizeof(inetSocket));if (n == -1){LOG(FATAL) << "bind fail";Die(BIND_ERR);}LOG(NORMAL) << "bind success";}void Start(){if (!_isRunning){_isRunning = true;while (true){struct sockaddr_in fromUser;socklen_t len = sizeof(fromUser);char buffer[1024];int n = ::recvfrom(_socket, &buffer, sizeof(buffer), 0, CONVERSE(&fromUser), &len);if (n > 0){buffer[n] = 0;// 接收成功,显示消息内容,谁发来的uint16_t clientPort = ::ntohs(fromUser.sin_port); // 网络字节序-->端口std::string clientIp = ::inet_ntoa(fromUser.sin_addr); // 网络字节序-->4字节-->字符串std::string message = std::to_string(clientPort) + ":" + clientIp + "#:";message += buffer;LOG(DEBUG) << message;std::string echoString = "echo say#";echoString += buffer;// 将消息在回显给发送方::sendto(_socket, echoString.c_str(), echoString.size(), 0, CONVERSE(&fromUser), len);}}}_isRunning = false;}private:int _socket;int16_t _port; // 端口std::string _ip; // ip地址bool _isRunning;
};
客户端
#ifndef __UpdClient__hpp__
#define __UpdClient__hpp__#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"#include <memory>
#include <string>using namespace MyLogModule;class UdpClient
{
public:UdpClient(std::string ip, std::string port){_port = stoi(port.c_str());_ip = ip;}~UdpClient(){}void Start(){_socket = ::socket(AF_INET, SOCK_DGRAM, 0);if (_socket == -1){LOG(FATAL) << "socket create fail";Die(SOCKET_ERR);}// 转换为网络字节序,并设置struct sockaddr_in server;server.sin_family = AF_INET;server.sin_addr.s_addr = ::inet_addr(_ip.c_str());server.sin_port = ::htons(_port);while(true){std::string message;std::cout << "Please enter# ";std::getline(std::cin,message);//客户端也要有自己的ip和port,但不需要绑定,因为OS会自动bind//服务端要显示的bind,是因为服务器的端口号,必须稳定!必须是众所周知且不能改变轻易改变的!::sendto(_socket,message.c_str(),message.size(),0,CONVERSE(&server),sizeof(server));char buffer[1024];//虽然上面已经有了sockaddr_in server,但是可能不只有一个服务器哦struct sockaddr_in tmp;socklen_t len = sizeof(tmp);int n = ::recvfrom(_socket,buffer,sizeof(buffer)-1,0,CONVERSE(&tmp),&len);if(n > 0){buffer[n] = 0;//打印服务器回写的std::cout << buffer << std::endl;}}}private:int _socket;uint16_t _port;std::string _ip;
};#endif
clientMain.cc
#include "Common.hpp"
#include "UdpClient.hpp"int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "usage fail " << "Usage:" << argv[0] << "serverIp serverPort" << std::endl;Die(USE_ERR);}//使用ip与port构建UdpClient对象的指针std::shared_ptr<UdpClient> c_ptr = std::make_shared<UdpClient>(argv[1],argv[2]);c_ptr->Start();return 0;
}
至此,两主机就可完成网络通信了。
查看网络服务是否启动的命令:netstat
- u:udp
- a: all
- p:pid/program
- n:num(能显示成数字的则显示)
但是当前的程序还有一个问题,那就是如果服务器上有多张网卡,那就有多个ip地址,但是server默认的ip是127.0.0.1,这样就会导致server无法收到其它ip所搜到的信息。
因此,需要将server的ip设置为INMADDR_ANY
将server中涉及ip的位置都修改就可以了
但是上面的代码还有点不优雅,特别是涉及ip地址与端口号那里,下面我们修改一下,直接将ip与port封装成一个对象。
#pragma once#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <string>
#include "Common.hpp"
class InetAddr
{
private:void portNet2Host(){_port = ::ntohs(_net_addr.sin_port);}void ipNet2Host(){// 此种方式inbuffer是局部的,不会造成问题char inbuff[64];_ip = ::inet_ntop(AF_INET, &_net_addr.sin_addr, inbuff, sizeof(inbuff));}public:InetAddr(){}InetAddr(const struct sockaddr_in &addr): _net_addr(addr){portNet2Host();ipNet2Host();}// 供服务端创建自己的InetAddr(uint16_t port): _port(port), _ip(""){_net_addr.sin_family = AF_INET;_net_addr.sin_port = ::htons(_port);_net_addr.sin_addr.s_addr = INADDR_ANY;}struct sockaddr *Getaddr() { return CONVERSE(&_net_addr); }socklen_t Getlen() { return sizeof(_net_addr); }uint16_t getPort() { return _port; }std::string getIP() { return _ip; }struct sockaddr_in getAddr() { return _net_addr; }~InetAddr(){}private:struct sockaddr_in _net_addr;uint16_t _port;std::string _ip;
};
效果:
TCP接口介绍:
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen
:函数的第二个参数,表示最多允许有 backlog 个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置不会太大。
accept
:
与UDP不同的是:tcp没有recvfrom与sendto方法,它直接使用read 和write;
connect
:
- 客户端需要调用 connect()连接服务器;
- connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址, 而
connect 的参数是对方的地址
。
相关文章:
【网络】网络基础知识(协议、mac、ip、套接字)
文章目录 1. 计算机网络的背景2. 认识网络协议2.1 协议分层2.2 OS与网络的关系 3. 网络传输基本流程3.1 局域网通信流程3.2 跨网络通信流程 4. Socket 编程预备4.1 理解源IP地址和目的IP地址4.2 端口号与Socket4.3传输层的典型代表4.4 网络字节序 5. socket 编程接口5.1 介绍5.…...
Unity【Colliders碰撞器】和【Rigibody刚体】的应用——小球反弹效果
目录 Collider 2D 定义: 类型: Rigidbody 2D 定义: 属性和行为: 运动控制: 碰撞检测: 结合使用 实用检测 延伸拓展 1、在Unity中优化Collider 2D和Rigidbody 2D的性能 2、Unity中Collider 2D…...
游戏引擎学习第75天
仓库:https://gitee.com/mrxiao_com/2d_game_2 Blackboard: 处理楼梯通行 为了实现楼梯的平滑过渡和角色的移动控制,需要对楼梯区域的碰撞与玩家的运动方式进行优化。具体的处理方式和遇到的问题如下: 楼梯区域的过渡: 在三维空间中&#x…...
ModelScope ms-swift:轻量级模型微调框架
ModelScope ms-swift:轻量级模型微调框架 介绍支持的模型支持的技术使用方法为什么选择ms-swift?结论 介绍 ModelScope ms-swift是ModelScope社区提供的一个官方框架,用于大型语言模型(LLMs)和多模态大型模型…...
管理加密SQLite数据库的软件工具研究
使用软件工具管理加密的 SQLite 数据库是一个常见需求,尤其是当需要保护敏感数据时。以下是实现此目标的步骤和相关工具推荐: 1. 选择支持加密的 SQLite 版本 SQLite 默认并不支持加密功能。你需要使用以下方法之一来启用加密: SQLite Encry…...
react 封装一个类函数使用方法
1.编写ProductCount函数 class ProductCount {public static getProductCount(count: number): string {if (count < 10) {return 当前数量: 0${count};}return 当前数量: ${count};} }export default ProductCount;2.在代码文件中导入 ProductCount 类。 import ProductC…...
Windows 11 上通过 WSL (Windows Subsystem for Linux) 安装 MySQL 8
在 Windows 11 上通过 WSL (Windows Subsystem for Linux) 安装 MySQL 8 的步骤如下: ✅ 1. 检查 WSL 的安装 首先确保已经安装并启用了 WSL 2。 🔧 检查 WSL 版本 打开 PowerShell,执行以下命令: wsl --list --verbose确保 W…...
解决 IntelliJ IDEA 中 Tomcat 日志乱码问题的详细指南
目录 前言1. 分析问题原因2. 解决方案 2.1 修改 IntelliJ IDEA 的 JVM 选项2.2 配置 Tomcat 实例的 VM 选项 2.2.1 设置 Tomcat 的 VM 选项2.2.2 添加环境变量 3. 进一步优化 3.1 修改 Tomcat 的 logging.properties3.2 修改操作系统默认编码 3.2.1 Windows 系统3.2.2 Linux …...
jenkins入门4 --window执行execute shell
1、启动关闭jenkins 在Windows环境下,如果你需要关闭Jenkins服务,可以通过以下几种方式: 1、使用Windows服务管理器: 打开“运行”对话框(Win R),输入services.msc,然后回车。 在服…...
51c嵌入式~单片机~合集4
我自己的原文哦~ https://blog.51cto.com/whaosoft/12868932 一、时钟失效之后,STM32还能运行? 问题: 该问题由某客户提出,发生在 STM32F103VDT6 器件上。据其工程师讲述:在其产品的设计中,STM32 的 H…...
OKHttp调用第三方接口,响应转string报错okhttp3.internal.http.RealResponseBody@4a3d0218
原因分析 通过OkHttp请求网络,结果请求下来的数据一直无法解析并且报错,因解析时String res response.body().toString() 将toString改为string即可!...
杰发科技——使用ATCLinkTool解除读保护
0. 原因 在jlink供电电压不稳定的情况下,概率性出现读保护问题,量产时候可以通过离线烧录工具避免。代码中开了读保护,但是没有通过can/uart/lin/gpio控制等方式进行关闭,导致无法关闭读保护。杰发所有芯片都可以用本方式解除读保…...
SQL 幂运算 — POW() and POWER()函数用法详解
POW() and POWER()函数用法详解 POW() 和 POWER() —计算幂运算(即一个数的指定次方)的函数。 这两个函数是等价的,功能完全相同,只是名字不同。 POW(base, exponent); POWER(base, exponent); base:底数。exponen…...
【Shell脚本】Docker构建Java项目,并自动停止原镜像容器,发布新版本
本文简述 经常使用docker部署SpringBoot 项目,因为自己的服务器小且项目简单,因此没有使用自动化部署。每次将jar包传到服务器后,需要手动构建,然后停止原有容器,并使用新的镜像启动,介于AI时代越来越懒的…...
【ArcGIS Pro二次开发实例教程】(2):BSM字段赋值
一、简介 一般的数据库要素或表格都有一个BSM字段,用来标识唯一值。 此工具要实现的功能是:按一定的规律(前缀中间的填充数字OBJECT码)来给BSM赋值。 主要技术要点包括: 1、ProWindow的创建,Label,Comb…...
VSCode函数调用关系图插件开发(d3-graphviz)
文章目录 1、如何在VSCode插件webview中用d3-graphviz绘图2、VSCode插件使用离线d3.min.js、d3-graphviz3、使用 `@hpcc-js/wasm` 包在 Node.js 环境直接转换dot为svg1、如何在VSCode插件webview中用d3-graphviz绘图 我来帮你创建一个 VS Code 插件示例,实现右键菜单触发 Web…...
OCR图片中文字识别(Tess4j)
文章目录 Tess4J下载 tessdataJava 使用Tess4j 的 demo Tess4J Tess4J 是 Tesseract OCR 引擎的 Java 封装库,它让 Java 项目更轻松地实现 OCR(光学字符识别)功能。 下载 tessdata 下载地址:https://github.com/tesseract-ocr/…...
leetcode 面试经典 150 题:同构字符串
链接同构字符串题序号205题型字符串解法哈希表难度简单熟练度✅✅✅✅ 题目 给定两个字符串 s 和 t ,判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符&#…...
算法-泰波那契
力扣题目链接:1137. 第 N 个泰波那契数 - 力扣(LeetCode) 泰波那契序列 Tn 定义如下: T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n,请返回第 n 个泰波那契数 Tn 的值。 示例 1&…...
Mac修改文件权限
查看文件权限 ll -all 修改读写权限 sudo chmod -R arwx /usr/local/mysql-5.7.30-macos10.14-x86_64/data/a_test 修改用户分组 sudo chown -R _mysql:wheel /usr/local/mysql-5.7.30-macos10.14-x86_64/data/b_test...
如何安装和配置PHP开发环境?
要安装和配置PHP开发环境,可以按照以下步骤进行: 一、下载和安装PHP 1:下载PHP: 访问PHP官方网站(PHP: Downloads),选择适合您操作系统的版本进行下载。 2:解压并安装PHP&#x…...
深入探讨 Android 中的 AlarmManager:定时任务调度及优化实践
引言 在 Android 开发中,AlarmManager 是一个非常重要的系统服务,用于设置定时任务或者周期性任务。无论是设置一个闹钟,还是定时进行数据同步,AlarmManager 都是不可或缺的工具之一。然而,随着 Android 系统的不断演…...
【Vim Masterclass 笔记07】S05L19:Vim 剪切、复制、粘贴操作同步练习
文章目录 S05L19 Vim 剪切、复制、粘贴操作同步练习(Exercise 05 - Cut, Copy and Paste)1 训练目标2 操作指令2.1 打开 dyp.txt 文件2.2 交换文件的头两行2.3 将文件首行 put 到文件其他为止2.4 练习在光标位置的上方粘贴文本行2.5 通过交换字符顺序更正…...
【前端下拉框】获取国家国旗
一、先看效果 二、代码实现(含国旗) <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...
Elasticsearch 操作文档对数据的增删改查操作 索引库文档 操作数据 CRUD
介绍 在 Elasticsearch 中,文档的增、删、改、查操作是核心的基本功能。Elasticsearch 使用 RESTful API 提供这些操作,通常通过 HTTP 请求与 Elasticsearch 集群进行交互。 索引库 {"mappings": {"properties": {"title&qu…...
【动手学电机驱动】STM32-MBD(2)将 Simulink 模型部署到 STM32G431 开发板
STM32-MBD(1)安装 STM32 硬件支持包 STM32-MBD(2)Simulink 模型部署入门 STM32-MBD(3)Simulink 状态机模型部署 【动手学电机驱动】STM32-MBD(2)Simulink 模型部署入门 1. 软硬件条件…...
小试牛刀-SpringBoot集成SOL链
目录 一、什么是solanaj? 二、Pom依赖 三、主要类 3.1 RpcClient 3.2 PublicKey 3.3 Transaction 3.4 TransactionInstruction 四、示例代码 Welcome to Code Blocks blog 本篇文章主要介绍了 [小试牛刀-SpringBoot集成SOL链] ❤博主广交技术好友,喜欢文章的…...
数据结构大作业——家谱管理系统(超详细!完整代码!)
目录 设计思路: 一、项目背景 二、功能分析 查询功能流程图: 管理功能流程图: 三、设计 四、实现 代码实现: 头文件 结构体 函数声明及定义 创建家谱树头结点 绘制家谱树(打印) 建立右兄弟…...
【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现
实验二 交换机基本配置和VLAN 间路由实现 一、实验目的 1.了解交换机的管理方式。 2.掌握通过Console接口对交换机进行配置的方法。 3.掌握交换机命令行各种模式的区别,能够使用各种帮助信息以及命令进行基本的配置。 4&…...
最新MySQL面试题(2025超详细版)
2025最新超详细MySQL面试题 文章目录 2025最新超详细MySQL面试题[toc]一、 SQL 和基本操作1. SQL的执行顺序2. 如何优化MySQL查询3. 常用的聚合函数4. 数据库事务5. 事务的四大特性(ACID)6. 视图7. MySQL中使用LIMIT子句进行分页8. MySQL中使用变量和用户定义的函数9. MySQL中的…...
Unity-Mirror网络框架-从入门到精通之AdditiveScenes 示例
文章目录 前言Additive Levels和Additive ScenesAdditive Levels场景介绍Portal传送门FadeInOut特效 Additive Scenes示例介绍ZoneHandlerSceneMassage 最后 前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框…...
java 转义 反斜杠 Unexpected internal error near index 1
代码: String str"a\\c"; //出现异常,Unexpected internal error near index 1 //System.out.println(str.replaceAll("\\", "c"));//以下三种都正确 System.out.println(str.replace(\\, c)); System.out.println(str.r…...
html内容过长,实现向上循环滑动效果
以下是几种实现 HTML 内容过长时向上循环滑动的常见方法,你可以根据具体需求和项目场景来选择合适的实现方式: 一、使用 CSS3 animation 实现简单的向上循环滑动(适用于简单的文本等内容滑动场景) 原理 通过 CSS3 的 keyframes…...
RAG(Retrieval-Augmented Generation,检索增强生成)流程
目录 一、知识文档的准备二、OCR转换三、分词处理四、创建向量数据库五、初始化语言聊天模型1.prompt2.检索链3.对话 完整代码 知识文档的准备:首先需要准备知识文档,这些文档可以是多种格式,如Word、TXT、PDF等。使用文档加载器或多模态模型…...
数据库系统概论期末复习
期末考试题型: 选择题 20题 20分 判断题 10题 10分 简答题 4题 20分 SQL语句: (select delete update)30分 设计题:ER图 和关系模式 ER转关系模式,注意主码,外码的标注 15分 应用题:…...
B树与B+树:数据库索引的秘密武器
想象一下,你正在构建一个超级大的图书馆,里面摆满了各种各样的书籍。B树和B树就像是两种不同的图书分类和摆放方式,它们都能帮助你快速找到想要的书籍,但各有特点。 B树就像是一个传统的图书馆摆放方式: 1. 书籍摆放&…...
数据结构-栈与队列笔记
普通的双端队列 验证图书取出顺序 class Solution {/*** 验证书籍的借阅顺序是否合法。* * param putIn 表示放入书架的书籍序列。* param takeOut 表示从书架取出的书籍序列。* return 如果书籍的借阅顺序合法,返回 true;否则返回 false。*/public boo…...
Netty中用了哪些设计模式?
大家好,我是锋哥。今天分享关于【Netty中用了哪些设计模式?】面试题。希望对大家有帮助; Netty中用了哪些设计模式? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Netty 是一个高性能的网络通信框架,广泛…...
设计模式与游戏完美开发(3)
更多内容可以浏览本人博客:https://azureblog.cn/ 😊 该文章主体内容来自《设计模式与游戏完美开发》—蔡升达 第二篇 基础系统 第五章 获取游戏服务的唯一对象——单例模式(Singleton) 游戏实现中的唯一对象 在游戏开发过程中…...
人工智能的发展领域之GPU加速计算的应用概述、架构介绍与教学过程
文章目录 一、架构介绍GPU算力平台概述优势与特点 二、注册与登录账号注册流程GPU服务器类型配置选择指南内存和存储容量网络带宽CPU配置 三、创建实例实例创建步骤镜像选择与设置 四、连接实例SSH连接方法远程桌面配置 一、架构介绍 GPU算力平台概述 一个专注于GPU加速计算的…...
【51单片机零基础-chapter5:模块化编程】
模块化编程 将以往main中泛型的代码,放在与main平级的c文件中,在h中引用. 简化main函数 将原来main中的delay抽出 然后将delay放入单独c文件,并单独开一个delay头文件,里面放置函数的声明,相当于收纳delay的c文件里面写的函数的接口. 注意,单个c文件所有用到的变量需要在该文…...
彻底学会Gradle插件版本和Gradle版本及对应关系
看完这篇,保你彻底学会Gradle插件版本和Gradle版本及对应关系,超详细超全的对应关系表 需要知道Gradle插件版本和Gradle版本的对应关系,其实就是需要知道Gradle插件版本对应所需的gradle最低版本,详细对应关系如下表格࿰…...
容器技术思想 Docker K8S
容器技术介绍 以Docker为代表的容器技术解决了程序部署运行方面的问题。在容器技术出现前,程序直接部署在物理服务器上,依赖管理复杂,包括各类运行依赖,且易变,多程序混合部署时还可能产生依赖冲突,给程序…...
在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描
在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描,需要充分利用LevelDB的迭代器(iterator)功能,以便能够高效地扫描和处理大量的键值对。下面是一个详细的实现指南。 环境准备 首先,确保已经安装了Level…...
多模态论文笔记——CogVLM和CogVLM2
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍多模态模型的LoRA版本——CogVLM和CogVLM2。在SD 3中使用其作为captioner基准模型的原因和优势。 文章目录 CogVLM论文背景VLMs 的任务与挑战现有方法及…...
BLDC无感控制的驱动逻辑
如何知道转子已经到达预定位置,因为我们只有知道了转子到达了预定位置之后才能进行换相,这样电机才能顺滑的运转。转子位置检测常用的有三种方式。 方式一:通过过零检测,三相相电压与电机中性点电压进行比较。过零检测的优点在于…...
分布式多机多卡训练全景指南:MPI、DeepSpeed 与 Colossal-AI 深度解析
分布式多机多卡训练技术是深度学习领域提高训练效率和加快模型收敛的重要手段。以下是几个流行的框架和工具: 1. MPI(Message Passing Interface) 概述 MPI 是一种标准化的消息传递协议,用于多机多卡之间的通信与协作,…...
Unity中 Xlua使用整理(一)
1.安装: 从GitHub上下载Xlua源码 Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com) 下载Xlua压缩包,并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unit…...
在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法
在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法 1. GrpcChannel 的概念 GrpcChannel 是 gRPC 客户端与服务器之间通信的核心组件。它是基于 HTTP/2 的连接,支持多路复用,即通过单个通道可以发送多…...
【数据结构与算法:八、排序】
第8章 排序 排序是计算机科学中最基本且最常用的操作之一。本章详细介绍了排序算法的概念、分类、每种算法的定义、图示、代码实现及其应用场景。 8.1 基本概念和排序方法概述 8.1.1 排序的基本概念 排序是指将一组无序的记录按照某种指定的顺序重新排列的过程。 排序的目…...