从零开始 verilog 以太网交换机(三)MAC发送控制器的设计与实现

news2024/11/14 21:37:45

从零开始 verilog 以太网交换机(三)MAC发送控制器的设计与实现



🔈声明:
😃博主主页:王_嘻嘻的CSDN主页
🧨 从零开始 verilog 以太网交换机系列专栏:点击这里
🔑未经作者允许,禁止转载,侵权必删
🚩关注本专题的朋友们可以收获一个经典交换机设计的全流程,包括设计与验证(FPGA);以太网MAC的基础知识。新手朋友们还将获得一个具有竞争力的项目经历,后续整个工程和代码下载链接也都会放在csdn和公众号内

  本章将继续进行MAC发送端控制器的设计与实现,交换机完整的架构可以参考:从零开始 verilog 以太网交换机(一)架构分析。


1、MAC发送控制器功能

  MAC发送控制器的功能和接收控制器对称,负责以太网的MAC -> PHY的数据交换,将数据帧转换为MII接口形式的数据流,并根据长度进行补零,计算CRC校验等
  其功能包括以下5点:

  • 根据以太网规范,先发送前导码,再发送帧起始符(关于前导符和帧起始符的基础知识在第二章中有介绍:MAC接收控制器的设计与实现);
  • 之后将Payload(有效荷载)从数据缓冲区读出,并发送;
  • 若数据过短,需要填充数据‘0’,使MAC帧长度符合要求;
  • 将同步计算的CRC-32校验值发送;
  • 当MAC全部发送结束后,需要按照以太网规范插入帧间等待时间,本工程中为24个cycle(时钟周期);

2、MAC发送控制器接口

  与接收控制器正好相反,MAC发送控制器一端连接数据缓冲区和状态缓冲区,另一端连接标准MII接口,具体接口如下。

在这里插入图片描述

  需要注意的是,系统时钟clk和MII发送端时钟tx_clk不同,tx_dv和tx_d需要在tx_clk下控制


3、MAC发送控制器实现细节

  发送控制器需要根据数据帧生成CRC-32校验值,通常CRC的多项式公式可以直接通过生成器来生成。(CRC生成器将放在本号的资源栏中,有需要的可以下载,或者关注博主的公众号,也有下载链接
  AFIFO的格式和结构都与接受控制器的相同,不了解的同学可以回看上一章内容:从零开始 verilog 以太网交换机(一)架构分析。


3.1、功能细节分析

  1. 检查state_fifo是否为空,若非空,则从state_fifo中取出一个数据,得到需要发送的MAC帧长度信息
  2. 发送前导码’0101’(本工程内重复6次)和帧起始符4‘b1011;
  3. 将data_fifo数据取出发送到MII接口,并同步进行CRC-32的计算,当长度过小时进行填充处理
  4. Payload发送完毕后,将CRC-32的计算值一并发送
  5. MAC发送结束后,等待帧间隔时间后,继续发送下一帧;


3.3、MAC发送器核心电路设计

  由于MAC发送控制器需要制造前导码,在帧尾填充数据,将交换机内部的8-bits数据转换成MII接口的4-bits数据,且系统时钟sys_clk和MII时钟tx_clk异步,所以在该模块中设置了一级用AFIFO实现的中间队列,既便于控制数据通路,又能处理异步时钟域。

  所以发送控制器的总体架构如下:


在这里插入图片描述

  前级队列控制状态机负责接收交换机处理完的数据帧,并写入对应的中间队列internal_data_fifo和internal_state_fifo,在设计中前级状态机分为5个状态,R_IDLER_PRER_SENDR_PADR_CRC,后四个状态分别负责向internal_data fifo存储前导码、Payload、填充‘0’和CRC校验值。

  根据每个状态需要的字节数cnt_r控制状态的跳转,每当前级state fifo非空时,发起一次接收数据帧的处理,把封装好的完整帧存储在internal data fifo中,并在最后一拍更新internal state fifo内容。

  而当internal data fifo不足以存放一个最大帧时(此最大帧需要包括前导码和CRC校验的字节数),前级队列状态机会反压输入,停止接收任何数据。

在这里插入图片描述


  后级队列控制器负责将接收到的完整数据帧连续不断的发送到MII接口,同样也是通过状态机的形式控制internal fifo的输出逻辑。

  后级队列控制相对容易,当internal state fifo非空时,取出实际长度数据,并按长度读取data fifo,每拍发送一个4-bits data到MII接口,发送完一个完整帧后,停止一段时间后继续开始检测下一数据帧是否准备完成


3.4、MAC发送器代码

  控制器的设计并不复杂,Verilog代码将放在下面,Testbench就不展示了,有需要的可以等专题结束后在资源中下载,或者去我的公众号获得链接。


module mac_t(
//system interface
input           clk,
input           rst_n,
//MII interface
input           tx_clk,
output  reg     tx_dv,
output  [3:0]   tx_d,
//mac-r - interface mux
output  reg     data_fifo_rd,
input   [7:0]   data_fifo_dout,
output  reg     state_fifo_rd,
input   [15:0]  state_fifo_dout,
input           state_fifo_empty
    );

    
parameter   BCNT_MAX        = 1518;
parameter   BCNT_MIN        = 64;
parameter   PRE_CNT         = 7;    //前导码数量
parameter   CRC_CNT         = 4;    //4 byte crc code
parameter   FRAME_WAIT_CNT  = 24;
//前级接收数据帧的状态机
localparam  R_IDLE    =   5'b00001;
localparam  R_PRE     =   5'b00010;
localparam  R_SEND    =   5'b00100;
localparam  R_PAD     =   5'b01000;
localparam  R_CRC     =   5'b10000;
    
//缓存向MII发送数据帧的状态机
localparam  T_IDLE    =   3'b001;
localparam  T_SEND    =   3'b010;
localparam  T_WAIT    =   3'b100;


reg     [4:0]   cur_sta_r;
reg     [4:0]   next_sta_r;

reg     [10:0]  cnt_r;   //内部计数器  记录还有多少byte需要发送   最大2k

//internal data fifo变量
reg     [7:0]   in_data_fifo_din;
reg             in_data_fifo_wr;
reg             in_data_fifo_rd;
wire    [7:0]   in_data_fifo_dout;
wire    [11:0]  in_data_fifo_wr_cnt;

//internal state fifo变量
reg     [15:0]  in_state_fifo_din;
reg             in_state_fifo_wr;
wire            in_state_fifo_rd;
wire    [15:0]  in_state_fifo_dout;
wire            in_state_fifo_full;
wire            in_state_fifo_empty;

wire    bp;  //反压信号,当internal fifo空间不够一个最大帧时,反压上级,拒绝接收一切数据

//crc 变量
reg             crc_init;
wire            crc_cal;
wire            crc_vld;
wire    [7:0]   crc_dout;
wire    [7:0]   crc_din;

assign bp = (in_data_fifo_wr_cnt[11:0] > 4096-BCNT_MAX-PRE_CNT-1) | in_state_fifo_full;

assign crc_din[7:0] = in_data_fifo_din[7:0];

//后级状态机变量
reg     [2:0]   cur_sta_t;
reg     [2:0]   next_sta_t;

reg     [11:0]  cnt_t;           //以半字节为单位
reg             in_state_fifo_empty_dly;

//============================前级状态机=========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cur_sta_r[4:0] <= R_IDLE;
    else
        cur_sta_r[4:0] <= next_sta_r[4:0];
end


always @(*)begin
    case(cur_sta_r[4:0])
        R_IDLE: next_sta_r[4:0] = (!bp & !state_fifo_empty) ? R_PRE : R_IDLE;  //IDLE态时 只有internal fifo空间足够 且 前级已有处理完的数据帧才能进入下一状态
        
        R_PRE:  next_sta_r[4:0] = (cnt_r[10:0]==11'b1) ? R_SEND : R_PRE;        //发送完所有前导码和起始符后 进入下一状态
        
        R_SEND: next_sta_r[4:0] = (cnt_r[10:0]==11'b1) ? ( state_fifo_dout[10:0]< BCNT_MIN-CRC_CNT ) ? R_PAD : R_CRC : R_SEND;        //发送完所有数据帧  进入下一状态 cnt每一状态都会更新
        
        R_PAD:  next_sta_r[4:0] = (cnt_r[10:0]==11'b1) ? R_CRC : R_PAD;         //完成所有填充 进入下一状态
        
        R_CRC:  next_sta_r[4:0] = (cnt_r[10:0]==11'b1) ? R_IDLE: R_CRC;         //CRC校验需要4 cycle 结束后,将校验值填入 返回IDLE态
        
        default: next_sta_r[4:0] = R_IDLE;       
    endcase
end


//cnt采样
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_r[10:0] <= 11'b0;
    else begin
        case(cur_sta_r[4:0])
            R_IDLE: cnt_r[10:0] <= PRE_CNT ;
            R_PRE:  cnt_r[10:0] <= cnt_r[10:0] > 11'b1 ? cnt_r[10:0] - 11'b1 : state_fifo_dout[10:0];
            R_SEND: cnt_r[10:0] <= cnt_r[10:0] > 11'b1 ? cnt_r[10:0] - 11'b1 : (state_fifo_dout[10:0] < (BCNT_MIN-CRC_CNT)) ? BCNT_MIN-CRC_CNT-state_fifo_dout[10:0] : CRC_CNT;
            R_PAD:  cnt_r[10:0] <= cnt_r[10:0] > 11'b1 ? cnt_r[10:0] - 11'b1 : CRC_CNT;
            R_CRC:  cnt_r[10:0] <= cnt_r[10:0] - 11'b1;
            default: cnt_r[10:0] <= 11'b0;
        endcase
    end
end

//==============crc var 控制================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        crc_init <= 1'b0;
    else if(cur_sta_r[4:0]==R_IDLE && next_sta_r[4:0]==R_PRE)
        crc_init <= 1'b1;
    else
        crc_init <= 1'b0;
end

assign crc_cal = cur_sta_r[4:0]==R_SEND | cur_sta_r[4:0]==R_PAD;
assign crc_vld = cur_sta_r[4:0]==R_SEND | cur_sta_r[4:0]==R_PAD | cur_sta_r[4:0]==R_CRC;

//=========================================
//out data fifo控制
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        data_fifo_rd <= 1'b0;
    else if(cur_sta_r[4:0]==R_PRE && cnt_r[10:0]==11'b1)
        data_fifo_rd <= 1'b1;
    else if(cur_sta_r[4:0]==R_SEND && cnt_r[10:0]==11'b1)
        data_fifo_rd <= 1'b0;
end

//out state fifo控制
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        state_fifo_rd <= 1'b0;
    else if(cur_sta_r[4:0]==R_CRC && cnt_r[10:0]==11'b1)
        state_fifo_rd <= 1'b1;
    else
        state_fifo_rd <= 1'b0;
end

//internal data fifo控制
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_data_fifo_wr <= 1'b0;
    else if(cur_sta_r[4:0]==R_IDLE)
        in_data_fifo_wr <= 1'b0;
    else if(next_sta_r[4:0]==R_PRE)
        in_data_fifo_wr <= 1'b1;
end

always @(posedge clk or negedge rst_n)begin   //TODO
    if(!rst_n)
        in_data_fifo_din[7:0] <= 8'h0;
    else if(cur_sta_r[4:0]==R_IDLE)
        in_data_fifo_din[7:0] <= 8'h55;
    else if(cur_sta_r[4:0]==R_PRE && cnt_r[10:0]==11'd1)
        in_data_fifo_din[7:0] <= 8'hd5;
    else if(cur_sta_r[4:0]==R_SEND)
        in_data_fifo_din[7:0] <= data_fifo_dout[7:0];
    else if(cur_sta_r[4:0]==R_PAD)
        in_data_fifo_din[7:0] <= 8'h0;
    else if(cur_sta_r[4:0]==R_CRC)
        in_data_fifo_din[7:0] <= crc_dout[7:0];
end


//internal state fifo控制
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_state_fifo_wr <= 1'b0;
    else if(cur_sta_r[4:0]==R_CRC && cnt_r[10:0]==11'b1)
        in_state_fifo_wr <= 1'b1;
    else
        in_state_fifo_wr <= 1'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_state_fifo_din[15:0] <= 16'b0;
    else if(cur_sta_r[4:0]==R_CRC && cnt_r[10:0]==11'b1)
        in_state_fifo_din[15:0] <= state_fifo_dout[15:0];
    else
        in_state_fifo_din[15:0] <= 16'b0; 
end

//==========================================================================

//=================================后级状态机===============================
always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        cur_sta_t[2:0] <= T_IDLE;
    else
        cur_sta_t[2:0] <= next_sta_t[2:0];
end

always @(posedge tx_clk or negedge rst_n)begin  //打一拍 为了in state fifo read over, fsm enter next state
    if(!rst_n)
        in_state_fifo_empty_dly <= 1'b1;
    else
        in_state_fifo_empty_dly <= in_state_fifo_empty;
end

always @(*)begin
    case(cur_sta_t[2:0])
        T_IDLE: next_sta_t[2:0] = !in_state_fifo_empty_dly ? T_SEND : T_IDLE;
        T_SEND: next_sta_t[2:0] = cnt_t[11:0]==12'b1 ? T_WAIT : T_SEND;
        T_WAIT: next_sta_t[2:0] = cnt_t[11:0]==12'b1 ? T_IDLE : T_WAIT;
        default:next_sta_t[2:0] = T_IDLE;
    endcase
end

//发送状态时 cnt采样
always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        cnt_t[11:0] <= 12'b0;
    else begin
        case(cur_sta_t[2:0])
            T_IDLE: cnt_t[11:0] <= !in_state_fifo_empty_dly ? (in_state_fifo_dout[10:0]+PRE_CNT+CRC_CNT)<<1'b1 : 12'b0;
            T_SEND: cnt_t[11:0] <= cnt_t[11:0]==12'b1 ? FRAME_WAIT_CNT : cnt_t[11:0] - 12'b1;
            T_WAIT: cnt_t[11:0] <= cnt_t[11:0] - 12'b1;
            default: cnt_t[11:0] <= 12'b0;
        endcase
    end
end



//internal state fifo 控制

assign in_state_fifo_rd = cur_sta_t[2:0]==T_IDLE & !in_state_fifo_empty;


//internal data fifo 控制
always @(posedge tx_clk or negedge rst_n)begin    //fifo dout需要提前读取后,dout上才有数据,并不是数据先在总线上
    if(!rst_n)
        in_data_fifo_rd <= 1'b0;
    else if( (cur_sta_t[2:0]==T_IDLE & !in_state_fifo_empty) | (cur_sta_t[2:0]==T_SEND & cnt_t[0]) )
        in_data_fifo_rd <= 1'b1;
    else
        in_data_fifo_rd <= 1'b0;
end

//MII接口控制
always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        tx_dv <= 1'b0;
    else if(cur_sta_t[2:0]==T_SEND)
        tx_dv <= 1'b1;
    else
        tx_dv <= 1'b0;
end


assign tx_d[3:0] = cnt_t[0] ? in_data_fifo_dout[3:0] : in_data_fifo_dout[7:4];
    
data_fifo 
    x_internal_data_fifo(
    .rst(~rst_n),
    .wr_clk(clk),
    .rd_clk(tx_clk),
    .din(in_data_fifo_din[7:0]),
    .wr_en(in_data_fifo_wr),
    .rd_en(in_data_fifo_rd),
    .dout(in_data_fifo_dout[7:0]),
    .full(),
    .empty(),
    .rd_data_count(),
    .wr_data_count(in_data_fifo_wr_cnt[11:0])
  );
  
state_fifo 
    x_internal_state_fifo(
    .rst(~rst_n),
    .wr_clk(clk),
    .rd_clk(tx_clk),
    .din(in_state_fifo_din[15:0]),
    .wr_en(in_state_fifo_wr),
    .rd_en(in_state_fifo_rd),
    .dout(in_state_fifo_dout[15:0]),
    .full(in_state_fifo_full),
    .empty(in_state_fifo_empty)
  );  

crc32 x_crc32(
    .clk(clk),
    .rst_n(rst_n),
    .data(crc_din[7:0]),
    .init(crc_init),
    .cal(crc_cal),
    .vld(crc_vld),
    .crc_reg(),
    .crc(crc_dout[7:0])
    );    
    
endmodule




搜索关注我的微信公众号【IC墨鱼仔】,获取我的更多IC干货分享!

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

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

相关文章

数据结构概述

逻辑结构 顺序存储 随机访问是可以通过下标取到任意一个元素&#xff0c;即数组的起始位置下标 链式存储 链式存储是不连续的&#xff0c;比如A只保留了当前的指针&#xff0c;那么怎么访问到B和C呢 每个元素不仅存储自己的值还使用额外的空间存储指针指向下一个元素的地址&a…

【python】英雄联盟电竞观赛引擎 掉落提示 CapsuleFarmerEvolved 「Webhook」「钉钉」

介绍 本项目链接 Github本项目链接 Gitee本项目链接 最近在github上发现一个可以用来自动帮你挂英雄联盟(除国服)电竞引擎(可以开出头像和表情)的项目,CapsuleFarmerEvolved,github原项目链接简单来说就是本来是通过看比赛获取奖励的,它帮助你进行观看. 对这个活动有兴趣的话…

3|物联网控制|计算机控制-刘川来胡乃平版|第1章:绪论|青岛科技大学课堂笔记|U1 ppt

目录绪论&#xff08;2学时&#xff09;常用仪表设备&#xff08;3学时&#xff09;计算机总线技术&#xff08;4学时&#xff09;过程通道与人机接口&#xff08;6学时&#xff09;数据处理与控制策略&#xff08;6学时&#xff09;网络与通讯技术&#xff08;3学时&#xff0…

sonarqube 生成pdf报错

sonar 生成report pdf报错&#xff0c;需要先配置username,password, Administration->PDF Report-> Password & Username https://gitee.com/zzulj/sonar-pdf-plugin sonar-pdf-plugin sonar-pdfreport-plugin-4.0.1.jar Installation 下载对应的版本&#xff…

Day894.加锁规则的一些问题 -MySQL实战

加锁规则的一些问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则&#xff0c;这个规则中&#xff0c;包含了两个“原则”、两个“优化”和一个“bug”&#xff1a; 原则 1&#xff1a;加锁的基本单位是 next-key lock。nex…

stable-diffusion-webui 安装使用

文章目录1.github 下载&#xff0c;按教程运行2.安装python 忘记勾选加入环境变量&#xff0c;自行加入&#xff08;重启生效&#xff09;3.环境变量添加后&#xff0c;清理tmp &#xff0c;venv重新运行4.运行报错&#xff0c;无法升级pip&#xff0c;无法下载包&#xff0c;5…

如何将Python打包后的exe还原成.py?

将python打包好的exe解压为py文件&#xff0c;步骤如下&#xff1a;下载pyinstxtractor.py文件下载地址&#xff1a;https://nchc.dl.sourceforge.net/project/pyinstallerextractor/dist/pyinstxtractor.py并将pyinstxtractor.py放到和exe相同的目录文件下打开命令控制台cd 进…

No.182# 技术管理之管理任务管理

引言继前文梳理「团队建设」与「管理规划」后&#xff0c;本文梳理下技术管理的另外一块「任务管理」。走查任务管理的主要内容&#xff1a;主要内容提点任务目标量化任务的优先级拉通形成共识团队梯队建设任务进度跟踪任务完成复盘一、主要内容提点二、任务目标量化任务管理&a…

QT mp3音乐播放器实现框架,Qt鼠标事件,网络编程,QSqlite,Json解析,HTTP请求等

QT mp3音乐播放器实现框架&#xff0c;Qt鼠标事件&#xff0c;网络编程&#xff0c;QSqlite,Json解析&#xff0c;HTTP请求等框架搭建UI设计mp3.hmp3.cpp隐藏窗口标题 最大化 最小化 关闭框架搭建 .pro添加 # 网络 添加多媒体 数据库 QT network multimedia sql添加头…

C进阶:5.动态内存管理

目录 1.为什么存在动态内存分配 2.动态内存函数的介绍 2.1 malloc 和 free 2.2 calloc malloc 与 calloc的区别&#xff1a; 2.3 realloc 3.常见的动态内存错误 3.1对NULL指针的解引用操作 3.2对动态开辟空间的越界访问 3.3对非动态开辟的内存使用free释放 3.4使用f…

帮助指令 man ,help及文档常用管理指令

帮助指令 man&#xff0c;help 1. man 当我们想要了解某个命令如何使用&#xff0c;及选项的含义是什么以及配置文件的帮助信息时&#xff0c;可以使用 man [命令或配置文件]&#xff0c;这样便可以获得到帮助提示信息了。 语法格式&#xff1a;man [命令或者配置文件] 比如…

[SSD科普之2] SATA、mSATA、M.2、M.2(NVMe)、PCIE固态硬盘接口详解

固态硬盘概念固态驱动器&#xff08;Solid State Drive&#xff09;&#xff0c;俗称固态硬盘&#xff0c;固态硬盘是用固态电子存储芯片阵列而制成的硬盘&#xff0c;因为台湾英语里把固体电容称之为Solid而得名。SSD由控制单元和存储单元&#xff08;FLASH芯片、DRAM芯片&…

最全280个上市公司数字化转型指标(2010-2021年)

基于年报测度数字化的论文在中文顶刊已有有趣的研究发表&#xff0c;从深交所、上交所下载2010-2021年上市公司年报&#xff0c;提取MD&A部分&#xff0c;基于《管理世界》、《经济研究》等期刊论文构建企业数字化词典&#xff08;详细参考见后文&#xff09;&#xff0c;将…

JavaScript系列之实现继承的几种方式

文章の目录一、借助父构造函数继承属性1、实现方式2、优点3、缺点二、原型链继承1、实现方式2、优点3、缺点三、组合继承四、ES6继承的实现方式参考写在最后一、借助父构造函数继承属性 1、实现方式 先定义一个父构造函数(this指向为window)&#xff1b;再定义一个子构造函数…

了解Nginx,这一篇就够了

了解Nginx&#xff0c;这一篇就够了1.Nginx应用场景2.Nginx相关概念正向代理和反向代理负载均衡动静分离3.Nginx配置文件解析全局块events块http块1.Nginx应用场景 HTTP服务器&#xff1a;Nginx本身也是一个静态资源的服务器&#xff0c;当只有静态资源的时候&#xff0c;就可…

玩转 Kubernetes 配置管理:ConfigMap 和 Secret 实战演示

目录一、简介二、ConfigMap2.1 基于目录创建 ConfigMap2.2 基于文件创建 ConfigMap2.3 从环境文件创建 ConfigMap2.4 定义从文件创建 ConfigMap 时要使用的键2.5 根据字符串创建 ConfigMap三、Secret3.1 基于文件创建Secret3.2 基于字符串创建Secret3.3 yaml文件方式创建secret…

安卓项目搭建grpc环境

本篇文章使用的IDE是Android Studio。这里先吐槽一句&#xff0c;安卓项目搭建grpc环境&#xff0c;不管是引入插件还是引入第三方库&#xff0c;对于版本的要求都极为苛刻&#xff0c;一旦版本不匹配就会报错&#xff0c;所以对于版本的搭配一定要注意。 下面介绍的这个版本搭…

CMake中cmake_host_system_information的使用

CMake中的cmake_host_system_information命令用于查询各种主机系统信息&#xff0c;其格式如下&#xff1a; cmake_host_system_information(RESULT <variable> QUERY <key> ...) # 1 cmake_host_system_information(RESULT <variable>QUERY WINDOWS_REGIST…

【爬虫理论实战】详解常见头部反爬技巧与验证方式 | 有 Python 代码实现

以下是常见头部反爬技巧与验证方式的大纲&#xff1a; User-Agent 字段的伪装方式&#xff0c;Referer 字段的伪装方式&#xff0c;Cookie 字段的伪装方式。 文章目录1. ⛳️ 头部反爬技巧1.1. User-Agent 字段&User-Agent 的作用1.2. 常见 User-Agent 的特征1.3. User-Age…

Spring IoC容器之常见常用注解以及注解编程模型简介

一、全文概览 本篇文章主要学习记录Spring中的核心注解&#xff0c;罗列常见常用的注解以及Spring中的注解编程模型介绍 二、核心注解 1、Spring模式注解 常用注解场景描述Spring起始支持版本Component通用组件模式注解&#xff0c;是所有组件类型注解的元注解Spring 2.5Repo…