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

一种棋牌网游的玩法

起因

俺是个记性不好的人,经常记不住牌,所以很少能赢。于是俺就写了个程序来记录出过的牌。

开始

因为是网游,所以就开始监听网络包。因为不需要改网络包,所以俺就选择了cap_ip。cap_ip是一个通过设置网卡混乱模式来监听网络包的源码,很容易在网上找到。

代码 U_GameCap

U_GameCap  用于游戏数据的网络包的监听和处理。

unit U_GameCap;interface
uses windows, classes, conn, cap_ip;
constmaxRecCount = 25 * 4 * 2; //一局牌的最多出牌信息数typeTGameCap = classprivateFPlayHallPort: integer;Fcap_ip: Tcap_ip;FconnList: TconnList;FMsgHandle: THandle;FMsg: UINT;FDebugPath: string;procedure CreatCap;procedure beginCap;procedure endCap;procedure freeCap;procedure OnCap(ip, proto, sourceIP, destIP, SourcePort, DestPort: string;header: pchar; header_size: integer; data: pchar; data_size: integer);procedure OnCapError(Error: string);procedure OnNewConn(sender: TObject; var conn: Tconn);procedure OnConnBuff(sender: TObject; var Buff: array of Byte; var ReceBuff_Len: integer);protectedprocedure ConnBuff(port: integer; var Buff: array of Byte; var ReceBuff_Len: integer); virtual;procedure postmsg(wParam: WPARAM; lParam: LPARAM);procedure debugfile(data: pchar; data_size: integer);publicUserID: integer;OutCardCounts: array[0..255] of Byte;OutCardCount: integer;GroundCards: array[0..52] of Byte;GroundCardCount: integer;GroundCards2: array[0..52] of Byte;GroundCardCount2: integer;maincard: Integer;chairid: Integer;constructor Create();destructor Destroy; override;procedure addCapPort(ip: string; port: Integer);procedure start(Port: Integer);procedure stop;property PlayHallPort: integer read FPlayHallPort;property MsgHandle: THandle read FMsgHandle write FMsgHandle;property Msg: UINT read FMsg write FMsg;property DebugPath: string read FDebugPath write FDebugPath;end;implementationuses SysUtils;{ TGameCap }procedure TGameCap.addCapPort(ip: string; port: Integer);
beginFconnList.addCapPort(ip, port);
end;procedure TGameCap.beginCap;
beginFconnList.clearCapPort;FconnList.clearconn;FconnList.addCapPort('', FPlayHallPort);Fcap_ip.StartCap;
end;procedure TGameCap.ConnBuff(port: integer; var Buff: array of Byte;var ReceBuff_Len: integer);
beginReceBuff_Len := 0;
end;procedure TGameCap.CreatCap;
beginFcap_ip := Tcap_ip.Create(nil);Fcap_ip.OnCap := OnCap;Fcap_ip.OnError := OnCapError;
end;constructor TGameCap.Create;
beginUserID := -1;maincard := -1;chairid := -1;OutCardCount := 0;GroundCardCount := 0;GroundCardCount2 := 0;FconnList := TconnList.Create;FconnList.OnNewConn := OnNewConn;CreatCap;end;procedure TGameCap.debugfile(data: pchar; data_size: integer);
varfn: string;ms: TMemoryStream;
beginms := TMemoryStream.Create;ms.Write(data[0], data_size);fn := DebugPath + FormatDateTime('HHNNSSzzz', now);while FileExists(fn) dofn := fn + '_';ForceDirectories(ExtractFilePath(fn));ms.SaveToFile(fn);FreeAndNil(ms);
end;destructor TGameCap.Destroy;
beginstop;freeCap;FreeAndNil(FconnList);inherited;
end;procedure TGameCap.endCap;
beginFcap_ip.StopCap;
end;procedure TGameCap.freeCap;
beginendCap;Fcap_ip.OnCap := nil;Fcap_ip.OnError := nil;FreeAndNil(Fcap_ip);
end;procedure TGameCap.OnCap(ip, proto, sourceIP, destIP, SourcePort,DestPort: string; header: pchar; header_size: integer; data: pchar;data_size: integer);
varPort: integer;
beginif data_size <= 0 thenexit;if proto <> 'TCP' thenexit;if SourcePort = '80' thenexit;if ip <> destIP thenexit;Port := StrToIntDef(SourcePort, 0);if Port = 0 thenexit;FconnList.processbuff(sourceIP, Port, data, data_size);
end;procedure TGameCap.OnCapError(Error: string);
begin
//
end;procedure TGameCap.OnConnBuff(sender: TObject; var Buff: array of Byte;var ReceBuff_Len: integer);
varport: integer;
beginport := Tconn(sender).port;ConnBuff(port, Buff, ReceBuff_Len);
end;procedure TGameCap.OnNewConn(sender: TObject; var conn: Tconn);
beginconn.OnConnBuff := OnConnBuff;
end;procedure TGameCap.postmsg(wParam: WPARAM; lParam: LPARAM);
beginPostMessage(FMsgHandle, FMsg, wParam, lParam);
end;procedure TGameCap.start(Port: Integer);
beginFPlayHallPort := Port;beginCap;
end;procedure TGameCap.stop;
beginendCap;
end;end.

数据解析 U_27w

//MainID: 101, AssID: 3为游戏房间数据包。
//变长,包头之后8个字节,前4为KindID,后4为游戏的NameID;
//之后为结构体,
//依次为4字节长度,4字节类型标识,4字节游戏类型,4字节KindID,4字节NameID,4字节RoomID,4字节在线人数,4字节每桌玩家数,4字节房间桌子数,
//4字节房间端口号,
//25字节房间IP地址(c格式字符串),
//31字节房间名称(含中文)。

type
  PNetMessageHead = ^NetMessageHead;
  NetMessageHead = packed record
    uMessageSize: Cardinal; //数据包大小
    bMainID: Cardinal; //处理主类型
    bAssistantID: Cardinal; //辅助处理类型 ID
    bHandleCode: Cardinal; //数据包处理代码
    bReserve: Cardinal; //保留字段
  end;
  PRec = ^TRec;
  TRec = packed record
    nextPerId: integer; //下家
    mainCardCount_0: integer; //主牌数量
    mainCardCount_1: integer;
    mainCardCount_2: integer;
    mainCardCount_3: integer;
    mainCardPairCount_0: byte; //主牌数量
    mainCardPairCount_1: byte;
    mainCardPairCount_2: byte;
    mainCardPairCount_3: byte;
    CardCount: byte; //牌数
    PerId: byte; //出牌人
    Cards: array[0..maxCardCount - 1] of byte; //牌
  end;

  T27w = class(TGameCap)
  private
    function willprocess(port: integer; pHead: PNetMessageHead): Boolean;
    procedure process(port: integer; bMainID: Cardinal; bAssistantID: Cardinal; data: pchar; size: integer);

    procedure setUserID(data: pchar; data_size: integer);
    procedure setPort(data: pchar; data_size: integer);

    procedure addrec(data: pchar; data_size: integer);
    procedure setground(data: pchar; data_size: integer);
    procedure setmain(data: pchar; data_size: integer);
    procedure setBegin(data: pchar; data_size: integer);
    procedure setground2(data: pchar; data_size: integer);
    procedure setChairid(data: pchar; data_size: integer);

    procedure newRound;
  protected
    procedure ConnBuff(port: integer; var Buff: array of Byte; var ReceBuff_Len: integer); override;
  public
    RecList: array[0..maxRecCount] of TRec;
    RecCount: integer;
    function ismaincard(v: Integer): boolean;
  end;

unit U_27w;interface
uses Windows, U_GameCap;
constmessage_maxLen = 1024 * 4;maxCardCount = 40; //一轮牌的最多出牌信息数wp_addrec = 1;wp_setground = 2;wp_setmain = 3;wp_setBegin = 4;wp_setground2 = 5;wp_setChairid = 6;
typePNetMessageHead = ^NetMessageHead;NetMessageHead = packed recorduMessageSize: Cardinal; //数据包大小bMainID: Cardinal; //处理主类型bAssistantID: Cardinal; //辅助处理类型 IDbHandleCode: Cardinal; //数据包处理代码bReserve: Cardinal; //保留字段end;PRec = ^TRec;TRec = packed recordnextPerId: integer; //下家mainCardCount_0: integer; //主牌数量mainCardCount_1: integer;mainCardCount_2: integer;mainCardCount_3: integer;mainCardPairCount_0: byte; //主牌数量mainCardPairCount_1: byte;mainCardPairCount_2: byte;mainCardPairCount_3: byte;CardCount: byte; //牌数PerId: byte; //出牌人Cards: array[0..maxCardCount - 1] of byte; //牌end;T27w = class(TGameCap)privatefunction willprocess(port: integer; pHead: PNetMessageHead): Boolean;procedure process(port: integer; bMainID: Cardinal; bAssistantID: Cardinal; data: pchar; size: integer);procedure setUserID(data: pchar; data_size: integer);procedure setPort(data: pchar; data_size: integer);procedure addrec(data: pchar; data_size: integer);procedure setground(data: pchar; data_size: integer);procedure setmain(data: pchar; data_size: integer);procedure setBegin(data: pchar; data_size: integer);procedure setground2(data: pchar; data_size: integer);procedure setChairid(data: pchar; data_size: integer);procedure newRound;protectedprocedure ConnBuff(port: integer; var Buff: array of Byte; var ReceBuff_Len: integer); override;publicRecList: array[0..maxRecCount] of TRec;RecCount: integer;function ismaincard(v: Integer): boolean;end;
implementation
{ T27w }
uses math, SysUtils;procedure T27w.addrec(data: pchar; data_size: integer);
varsize: integer;i: Byte;nowPRec: PRec;OldPRec: PRec;
beginsize := sizeof(TRec) - maxCardCount + PRec(data)^.CardCount;if size > data_size thenbegin//包不对;exit;end;i := PRec(data)^.PerId;if i > 3 thenbegin//包不对;exit;end;nowPRec := PRec(data);if RecCount > 0 thenbegin //校验重复牌for i := RecCount - 1 downto max(0, RecCount - 4) dobeginOldPRec := @RecList[i];if (OldPRec^.nextPerId < 0) or (OldPRec^.nextPerId > 3) then //轮 判断beginBreak;end;if (nowPRec^.nextPerId = OldPRec^.nextPerId) and (nowPRec^.PerId = OldPRec^.PerId) thenexit;end;end;Move(PRec(data)^, RecList[RecCount], size);inc(RecCount);nowPRec := @RecList[RecCount - 1];for i := 0 to nowPRec^.CardCount - 1 doinc(OutCardCounts[nowPRec^.Cards[i]]);inc(OutCardCount, nowPRec^.CardCount);postmsg(wp_addrec, RecCount - 1);
end;procedure T27w.ConnBuff(port: integer; var Buff: array of Byte;var ReceBuff_Len: integer);
varheadsize: integer;data: pchar;data_size: integer;size: Integer;
beginheadsize := sizeof(NetMessageHead);data_size := ReceBuff_Len;data := @Buff[0];while (data_size > headsize) dobeginsize := PNetMessageHead(data)^.uMessageSize;if size <= headsize thenbegininc(data, 1);dec(data_size, 1);Continue; //    size 比 包头 的 size 还小    错包end;if size > message_maxLen thenbegininc(data, 1);dec(data_size, 1);Continue; //    size 太大  错包end;with PNetMessageHead(data)^ doif (bMainID = 0) or (bAssistantID = 0) or (bReserve <> 0) thenbegininc(data, 1);dec(data_size, 1);Continue; //     错包end;//以上 是为了 判断 出第一个包//因为 有可能 是程序 可能 后开 接收到了 某个包的 后半端//所以 要找出 第一个 uMessageSize 的位置if size > data_size thenBreak; //    没收全if willprocess(port, PNetMessageHead(data)) thenbeginwith PNetMessageHead(data)^ doprocess(port, bMainID, bAssistantID, @(data[headsize]), size - headsize);inc(data, size);dec(data_size, size);Continue;end;inc(data, size); //   inc(data, 1);dec(data_size, size); //  inc(data, 1);end;if data_size <= 0 thenbeginReceBuff_Len := 0;endelsebeginif ReceBuff_Len <> data_size thenbeginMove(Buff[ReceBuff_Len - data_size], Buff[0], data_size);ReceBuff_Len := data_size;end;end;
end;function T27w.ismaincard(v: Integer): boolean;
varb: Byte;
beginResult := false;if maincard < 0 thenexit;if v div 16 = maincard thenbeginResult := true;exit;end;b := v mod 16;if (b = 1) or (b = 6) or (b = $E) or (b = $F) thenbeginResult := true;exit;end;
end;procedure T27w.newRound;
vararow, acol: integer;
beginRecCount := 0;OutCardCount := 0;GroundCardCount := 0;GroundCardCount2 := 0;maincard := -1;
//  chairid := -1;ZeroMemory(@OutCardCounts[0], length(OutCardCounts));
end;procedure T27w.process(port: integer; bMainID: Cardinal; bAssistantID: Cardinal; data: pchar; size: integer);
beginif port = PlayHallPort thenbeginif (bMainID = 100) or (bAssistantID = 5) thenbeginsetUserID(data, size);exit;end;if (bMainID = 101) or (bAssistantID = 3) thenbeginsetPort(data, size);exit;end;endelsebeginif (bMainID = 180) thencase bAssistantID of121: addrec(data, size);90: setground(data, size);81: setmain(data, size);62: setBegin(data, size);93: setground2(data, size);end;if (bMainID = 102) thencase bAssistantID of2: setChairid(data, size);end;end
end;procedure T27w.setBegin(data: pchar; data_size: integer);
beginnewRound;postmsg(wp_setBegin, 0);
end;procedure T27w.setground(data: pchar; data_size: integer);
varb: Byte;
begin
//   第一字节为庄的椅子号,第5~8字节为底牌数量,第9~16字节共8字节为底牌信息Move(data[0], b, 1);if b > 3 thenexit;Move(data[4], GroundCardCount, 4);if (GroundCardCount > 0) and (GroundCardCount <= 8) thenMove(data[8], GroundCards[0], GroundCardCount)elseGroundCardCount := 0;PostMsg(wp_setground, 0);
end;procedure T27w.setground2(data: pchar; data_size: integer);
varb: Byte;
begin
//  1~4字节,庄家椅子号;5~8字节,埋牌数量;
// 9~16字节,所埋牌信息。Move(data[4], GroundCardCount2, 4);if (GroundCardCount2 > 0) and (GroundCardCount2 <= 8) thenMove(data[8], GroundCards2[0], GroundCardCount2)elseGroundCardCount := 0;PostMsg(wp_setground2, 0);
end;procedure T27w.setmain(data: pchar; data_size: integer);
varb: byte;
begin
{MainID: 180, AssID: 81指定主牌花色数据包
第1字节,为叫花色玩家椅子号即庄的椅子号;
第2字节,为叫的花色,06,16,26,36分别代表方片、草花、红桃和黑桃。
}Move(data[1], b, 1);b := b div 16;if b <= 3 thenmaincard := belsemaincard := -1;PostMsg(wp_setmain, 0);
end;procedure T27w.setChairid(data: pchar; data_size: integer);
varb: Byte;tmp_userid: Integer;
beginif UserID <= 0 thenexit;Move(data[0], tmp_userid, 4);if tmp_userid <> UserID thenexit;Move(data[6], b, 1);if b <= 3 thenchairid := belsebegin// chairid := -1;end;PostMsg(wp_setChairid, 0);
end;procedure T27w.setPort(data: pchar; data_size: integer);
vari, k, count, idx, port: Integer;ip: string;a_size: Integer;
begin//MainID: 101, AssID: 3为游戏房间数据包。
//变长,包头之后8个字节,前4为KindID,后4为游戏的NameID;
//之后为结构体,
//依次为4字节长度,4字节类型标识,4字节游戏类型,4字节KindID,4字节NameID,4字节RoomID,4字节在线人数,4字节每桌玩家数,4字节房间桌子数,
//4字节房间端口号,
//25字节房间IP地址(c格式字符串),
//31字节房间名称(含中文)。if data_size <= 12 thenexit;move(data[8], a_size, 4);if a_size <> $B4 thenexit;count := (data_size - 8) div (a_size);for i := 0 to count - 1 dobeginidx := 8 + a_size * i + 36;Move(data[idx], port, 4);SetLength(ip, 25);Move(data[idx + 4], ip[1], 25);ip := Trim(ip);for k := 1 to length(ip) doif ip[k] = #0 thenbeginSetLength(ip, k - 1);Break;end;addCapPort(ip, port);end;
end;procedure T27w.setUserID(data: pchar; data_size: integer);
beginMove(data[0], userid, 4);
end;function T27w.willprocess(port: integer; pHead: PNetMessageHead): Boolean;
beginResult := false;if port = PlayHallPort thenbeginwith pHead^ doResult :=((bMainID = 100) or (bAssistantID = 5))or ((bMainID = 101) or (bAssistantID = 3));endelsebeginwith pHead^ doResult :=((bMainID = 180) or (bAssistantID = 121))or ((bMainID = 180) or (bAssistantID = 90))or ((bMainID = 180) or (bAssistantID = 62))or ((bMainID = 180) or (bAssistantID = 81))or ((bMainID = 180) or (bAssistantID = 62))or ((bMainID = 180) or (bAssistantID = 93))or ((bMainID = 102) or (bAssistantID = 2))end;
end;end.

如何知道数据包的定义?

答案就是“

这个网游刚好没有加密,而俺也刚好猜出来了。

中间记录了大量的网络包的日志。猜来猜去就猜出来了。

相关文章:

一种棋牌网游的玩法

起因 俺是个记性不好的人&#xff0c;经常记不住牌&#xff0c;所以很少能赢。于是俺就写了个程序来记录出过的牌。 开始 因为是网游&#xff0c;所以就开始监听网络包。因为不需要改网络包&#xff0c;所以俺就选择了cap_ip。cap_ip是一个通过设置网卡混乱模式来监听网络包…...

9.综合调试|输入不能存在空格|desc存在None|输出权值和ID|函数重名|修改文件名|权值和实际关键词出现次数(C++)

输入不能存在空格 目前输入的关键词时每隔一空格内容分别进行搜索&#xff0c;大部分时候我们都是将一串包含空格的内容直接进行搜索&#xff0c;需要将代码改进。 将cin换为fgets #include "searcher.hpp" #include <iostream> #include <cstdio> #in…...

使用SHOW PROCESSLIST和SHOW ENGINE INNODB STATUS排查mysql锁等待问题

现象&#xff1a; mysql 查某表一直不能结束&#xff0c;查别的表没有问题。已知之前刚刚alter此表想把它的一个字段长度增长&#xff0c;但是这个操作一直没有结束。现在应该怎么办? 方案: 使用 SHOW PROCESSLIST; 查看当前所有活动的SQL线程&#xff0c;找出是否有长时间…...

ElasticSearch映射分词

目录 弃用Type why 映射 查询 mapping of index 创建 index with mapping 添加 field with mapping 数据迁移 1.新建 一个 index with correct mapping 2.数据迁移 reindex data into that index 分词 POST _analyze 自定义词库 ik分词器 circuit_breaking_excep…...

JVM——堆的回收:引用计数发和可达性分析法、五种对象引用

目录 引用计数法和可达性分析法 引用计数法&#xff1a; 可达性分析算法&#xff1a; 五种对象引用 软引用&#xff1a; 弱引用&#xff1a; 引用计数法和可达性分析法 引用计数法&#xff1a; 引用计数法会为每个对象维护一个引用计数器&#xff0c;当对象被引用时加1&…...

PosgreSQL比MySQL更优秀吗?

一日&#xff0c;一群开发者对PosgreSQL是不是比MySQL更优秀进行了激烈的辩论&#xff0c;双方吵的都要打起来了 正方有以下理由&#xff1a; PostgreSQL严格遵循SQL标准规范&#xff0c;相较MySQL在语法兼容性和功能完整性方面展现出更强的体系化设计&#xff0c;尤其在事务处…...

冒险岛079 V8 整合版源码搭建教程+IDEA启动

今天教大家来部署下一款超级怀旧游戏冒险岛&#xff0c;冒险岛源码是开源的&#xff0c;但是开源的代码会有各种&#xff0c;本人进行了加工整合&#xff0c;并且用idea进行了启动测试&#xff0c;经过修改后没有任何问题。 启动截图 后端控制台 前端游戏界面 声明 冒险岛源码…...

基于Python的Flask微博话题舆情分析可视化系统

2024数据 ✅️标价源码 远程部署加 20 ✅️爬虫可用 有六月数据 ✅️修复bug不会突然打不开网页 系统稳定 系统的功能如下: 1.数据的爬取 2.用户的登录注册 3.热词统计&#xff0c;舆情统计 4.文章统计分析 5.发布地址统计 6.评论统计 7.情感分类统计 编程语言&#xff1a;py…...

ms-swift3 序列分类训练

目录 引言 一、数据集准备 二、训练/推理代码 2.1 训练 2.2 推理 三、性能验证 引言 swift 3.x支持了序列分类Command Line Parameters — swift 3.2.0.dev0 documentation 想尝试一下用多模态&#xff08;图像&#xff09;的序列分类与普通的图像分类任务有啥区别 一、…...

VSCode 实用快捷键

前文 VSCode 作为文本编辑神器, 熟练使用其快捷键更是效率翻倍, 本文介绍 VSCode 常用的实用的快捷键 实用快捷键 涉及到文本操作, 搜索定位, 多光标, 面板打开等快捷键 功能快捷键复制光标当前行 (不需要鼠标选中) Ctrl C 剪切光标当前行 (不需要鼠标选中) Ctrl X 当前行下…...

MVC模式和MVVM模式

目录 一、MVC模式和MVVM模式 1. MVC模式 2. MVVM 模式 3.在Qt中的应用示例 4.总结 二、MVC与MVVM模式的共同点和区别 1.共同点 2.区别 3.交互流程 4.总结 MVC&#xff08;Model-View-Controller&#xff09;和MVVM&#xff08;Model-View-ViewModel&#xff09;是两种…...

CSS伪类选择器全解析:让你的样式更加灵活和智能

目录 前言 一、什么是伪类选择器&#xff1f; 二、常见的伪类选择器详解 1. :hover —— 悬停状态 2. :active —— 活动状态 3. :focus —— 焦点状态 综合案例 4. :first-child —— 第一个子元素 5. :last-child —— 最后一个子元素 6. :nth-child(n) —— 按顺序选…...

【GESP】2024年12月图形化一级 -- 飞行的小猫

飞行的小猫 1. 准备工作 &#xff08;1&#xff09;删除默认小猫角色。 &#xff08;2&#xff09;添加角色Cat Flying和Clouds。 &#xff08;3&#xff09;删除默认白色背景&#xff0c;添加背景Blue Sky 2。 2. 功能实现 &#xff08;1&#xff09;点击绿旗&#xff0c…...

30填学习自制操作系统第二天

今天要干什么&#xff1f; 初步了解汇编语言使用汇编重新写个昨天的镜像文件继续开发 一: 什么是电信号&#xff1f; 电脑的处理中心是CPU&#xff0c;即“central process unit”的缩写&#xff0c;翻译成中文就是“中央处理单元”&#xff0c;顾名思义&#xff0c;他就是…...

MapReduce的工作原理及其在大数据处理中的应用

MapReduce是一种由Google提出的面向大数据并行处理的计算模型、框架和平台&#xff0c;它通过将复杂的数据处理任务分解为两个简单的阶段——Map&#xff08;映射&#xff09;和Reduce&#xff08;归约&#xff09;&#xff0c;实现了分布式并行计算&#xff0c;极大地提高了数…...

vue3.x 的provide 与 inject详细解读

在 Vue 3.x 中&#xff0c;provide 和 inject 是一对用于实现依赖注入的 API。它们允许父组件向其所有子组件&#xff08;无论嵌套多深&#xff09;传递数据或方法&#xff0c;而不需要通过 props 逐层传递。这在开发复杂组件或高阶组件时非常有用。 1. provide 的基本用法 p…...

c#中“事件-event”的经典示例与理解

在C#编程语言中&#xff0c;事件&#xff08;Event&#xff09;是一个非常重要的概念&#xff0c;它提供了一种松耦合的方式&#xff0c;让对象间能够通知彼此&#xff0c;而无需直接联系。事件的使用可以让我们的代码更加灵活、可扩展且易于维护。 事件可以视作委托的实例&…...

《第三代大语言模型Grok 3:闪亮登场》

《第三代大语言模型Grok 3:闪亮登场》 在科技飞速发展的今天,人工智能领域的每一次重大突破都如同巨石投入平静湖面,激起千层浪。当地时间 2 月 15 日,马斯克在社交平台 X 上投下了这样一颗 “巨石”,他宣布旗下人工智能公司 xAI 开发的第三代大语言模型 Grok 3,将于北京…...

rem、em、vw区别

在前端开发里&#xff0c;rem、em、vw都是用来设置元素大小的单位&#xff0c;下面就用大白话讲讲它们的区别。 参考标准不一样 rem&#xff1a;就像大家都用同一把“大尺子”来量东西&#xff0c;这把“大尺子”就是网页里根元素&#xff08;也就是 <html> 标签&#…...

最新Apache Hudi 1.0.1源码编译详细教程以及常见问题处理

1.最新Apache Hudi 1.0.1源码编译 2.Flink、Spark、Hive集成Hudi 1.0.1 3.flink streaming写入hudi 目录 1. 版本介绍 2. 安装maven 2.1. 下载maven 2.2. 设置环境变量 2.3. 添加Maven镜像 3. 编译hudi 3.1. 下载hudi源码 3.2. 修改hudi源码 3.3. 修改hudi-1.0.1/po…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...