当前位置: 首页 > 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…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...