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

wireshark解析FLV插件分享

-- 定义FLV解析器
local flv_proto = Proto("FLV", "Flash Video Format")

-- 定义字段
local fields = {
signature = ProtoField.string("flv.signature", "Signature"),           -- FLV 文件签名
version = ProtoField.uint8("flv.version", "Version", base.DEC),        -- 协议版本
flags = ProtoField.uint8("flv.flags", "Flags", base.HEX),              -- 功能标志位
header_size = ProtoField.uint32("flv.header_size", "Header Size", base.DEC),    -- 头长度
prev_tag_size = ProtoField.uint32("flv.prev_tag_size", "Previous Tag Size", base.DEC),  -- 前 Tag 大小
tag_type = ProtoField.uint8("flv.tag_type", "Tag Type", base.DEC, {         -- Tag 类型
[0x08] = "Audio",          -- 0x08: 音频数据
[0x09] = "Video",          -- 0x09: 视频数据
[0x12] = "Metadata"        -- 0x12: 元数据 (AMF)
}),
data_size = ProtoField.uint24("flv.data_size", "Data Size", base.DEC),           -- 数据区长度
timestamp = ProtoField.uint24("flv.timestamp", "Timestamp", base.DEC),           -- 基本时间戳
timestamp_ext = ProtoField.uint8("flv.timestamp_ext", "Timestamp Extended", base.DEC),   -- 时间戳扩展
stream_id = ProtoField.uint24("flv.stream_id", "Stream ID", base.DEC),          -- 流ID

-- 音频字段
audio_header = ProtoField.uint8("flv.audio.header", "Audio Header", base.HEX),
audio_format = ProtoField.uint8("flv.audio.format", "Audio Format", base.DEC, {
[0] = "Linear PCM, platform endian",
[1] = "ADPCM",
[2] = "MP3",
[3] = "Linear PCM, little endian",
[4] = "Nellymoser 16kHz mono",
[5] = "Nellymoser 8kHz mono",
[6] = "Nellymoser",
[7] = "G.711 A-law",
[8] = "G.711 mu-law",
[10] = "AAC",
[11] = "Speex",
[14] = "MP3 8kHz",
[15] = "Device-specific sound"
}),
audio_rate = ProtoField.uint8("flv.audio.rate", "Sample Rate", base.DEC, {
[0] = "5.5kHz",
[1] = "11kHz",
[2] = "22kHz",
[3] = "44kHz"
}),
audio_size = ProtoField.uint8("flv.audio.size", "Sample Size", base.DEC, {
[0] = "8-bit",
[1] = "16-bit"
}),
audio_type = ProtoField.uint8("flv.audio.type", "Audio Type", base.DEC, {
[0] = "Mono",
[1] = "Stereo"
}),
aac_packet_type = ProtoField.uint8("flv.audio.aac_packet_type", "AAC Packet Type", base.DEC, {
[0] = "AAC sequence header",
[1] = "AAC raw"
}),
audio_object_type = ProtoField.uint8("flv.audio.aac.object_type", "Audio Object Type", base.DEC, {
[1] = "AAC Main",
[2] = "AAC LC",
[3] = "AAC SSR",
[4] = "AAC LTP",
[5] = "SBR",
[6] = "AAC Scalable",
[7] = "TwinVQ",
[8] = "CELP",
[9] = "HVXC"
}),
sampling_index = ProtoField.uint8("flv.audio.aac.sampling_index", "Sampling Frequency Index", base.DEC, {
[0] = "96000 Hz",
[1] = "88200 Hz",
[2] = "64000 Hz",
[3] = "48000 Hz",
[4] = "44100 Hz",
[5] = "32000 Hz",
[6] = "24000 Hz",
[7] = "22050 Hz",
[8] = "16000 Hz",
[9] = "12000 Hz",
[10] = "11025 Hz",
[11] = "8000 Hz",
[12] = "7350 Hz"
}),
channel_config = ProtoField.uint8("flv.audio.aac.channel_config", "Channel Configuration", base.DEC, {
[0] = "Defined in AOT Specific Config",
[1] = "1 channel: front-center",
[2] = "2 channels: front-left, front-right",
[3] = "3 channels: front-center, front-left, front-right",
[4] = "4 channels: front-center, front-left, front-right, back-center",
[5] = "5 channels: front-center, front-left, front-right, back-left, back-right",
[6] = "6 channels: front-center, front-left, front-right, back-left, back-right, LFE",
[7] = "8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE"
}),
frame_length = ProtoField.uint16("flv.audio.mp3.frame_length", "Frame Length", base.DEC),
mpeg_version = ProtoField.uint8("flv.audio.mp3.mpeg_version", "MPEG Version", base.DEC, {
[0] = "MPEG Version 2.5",
[1] = "Reserved",
[2] = "MPEG Version 2",
[3] = "MPEG Version 1"
}),
layer = ProtoField.uint8("flv.audio.mp3.layer", "Layer", base.DEC, {
[1] = "Layer III",
[2] = "Layer II",
[3] = "Layer I"
}),
protection_bit = ProtoField.uint8("flv.audio.mp3.protection", "Protection Bit", base.DEC, {
[0] = "Protected by CRC",
[1] = "Not protected"
}),
bitrate_index = ProtoField.uint8("flv.audio.mp3.bitrate_index", "Bitrate Index", base.DEC),
sampling_rate_index = ProtoField.uint8("flv.audio.mp3.sampling_rate_index", "Sampling Rate Index", base.DEC),
padding_bit = ProtoField.uint8("flv.audio.mp3.padding", "Padding Bit", base.DEC),
private_bit = ProtoField.uint8("flv.audio.mp3.private", "Private Bit", base.DEC),
mode = ProtoField.uint8("flv.audio.mp3.mode", "Mode", base.DEC, {
[0] = "Stereo",
[1] = "Joint stereo",
[2] = "Dual channel",
[3] = "Single channel"
}),
mode_extension = ProtoField.uint8("flv.audio.mp3.mode_extension", "Mode Extension", base.DEC),
copyright = ProtoField.uint8("flv.audio.mp3.copyright", "Copyright", base.DEC),
original = ProtoField.uint8("flv.audio.mp3.original", "Original", base.DEC),
emphasis = ProtoField.uint8("flv.audio.mp3.emphasis", "Emphasis", base.DEC),

-- 视频字段
video_header_flags = ProtoField.uint8("flv.video.header.flags", "Header Flags", base.HEX),
video_frame = ProtoField.uint8("flv.video.frame", "Frame Type", base.DEC, {
[1] = "Keyframe",
[2] = "Inter frame",
[3] = "Disposable inter frame",
[4] = "Generated keyframe",
[5] = "Video info/command frame"
}),
video_codec = ProtoField.uint8("flv.video.codec", "Codec ID", base.DEC, {
[1] = "JPEG",
[2] = "Sorenson H.263",
[3] = "Screen video",
[4] = "On2 VP6",
[5] = "On2 VP6 with alpha",
[6] = "Screen video v2",
[7] = "AVC"
}),
avc_packet_type = ProtoField.uint8("flv.video.avc_packet_type", "AVC Packet Type", base.DEC, {
[0] = "AVC sequence header",
[1] = "AVC NALU",
[2] = "AVC end of sequence"
}),
composition_time = ProtoField.uint24("flv.video.composition_time", "Composition Time", base.DEC),
nalu_type = ProtoField.uint8("flv.video.nalu.type", "NALU Type", base.DEC, {
[1] = "Coded slice of a non-IDR picture",
[5] = "Coded slice of an IDR picture",
[6] = "Supplemental enhancement information (SEI)",
[7] = "Sequence parameter set",
[8] = "Picture parameter set",
[9] = "Access unit delimiter",
[10] = "End of sequence",
[11] = "End of stream",
[12] = "Filler data",
[13] = "Sequence parameter set extension",
[14] = "Prefix NAL unit",
[15] = "Subset sequence parameter set",
[19] = "Coded slice of an auxiliary coded picture without partitioning",
[20] = "Coded slice extension"
}),
nalu_size = ProtoField.uint32("flv.video.nalu.size", "NALU Size", base.DEC),
nalu_header = ProtoField.uint8("flv.video.nalu.header", "NALU Header", base.HEX),
nalu_ref_idc = ProtoField.uint8("flv.video.nalu.ref_idc", "NALU Ref IDC", base.DEC, {
[0] = "Disposable",
[1] = "Lowest",
[2] = "Low",
[3] = "High"
})
}

flv_proto.fields = fields

-- 会话状态枚举
local STATE_HEADER = 1
local STATE_PREV_TAG_SIZE = 2
local STATE_TAG_HEADER = 3
local STATE_TAG_DATA = 4

-- 解析AAC音频特定配置
local function parse_aac_specific_config(subtree, buffer, offset)
if buffer:len() - offset < 2 then return offset end

local config_tree = subtree:add(flv_proto, buffer(offset, 2), "AAC Audio Specific Config")

-- 解析音频对象类型 (5 bits)
local audio_object_type = buffer(offset,1):bitfield(0,5)
config_tree:add(fields.audio_object_type, buffer(offset,1), audio_object_type)

-- 解析采样率索引 (4 bits)
local sampling_frequency_index = buffer(offset,1):bitfield(5,4)
config_tree:add(fields.sampling_index, buffer(offset,1), sampling_frequency_index)

-- 解析声道配置 (4 bits)
local channel_configuration = buffer(offset,2):bitfield(9,4)
config_tree:add(fields.channel_config, buffer(offset,2), channel_configuration)

return offset + 2
end

-- 解析MP3帧头
local function parse_mp3_frame_header(subtree, buffer, offset)
if buffer:len() - offset < 4 then return offset end

local header_tree = subtree:add(flv_proto, buffer(offset, 4), "MP3 Frame Header")

-- 同步字 (11 bits)
local sync_word = buffer(offset,2):bitfield(0,11)
header_tree:add(ProtoField.uint16("flv.audio.mp3.sync", "Sync Word", base.HEX), buffer(offset,2), sync_word)

-- MPEG版本 (2 bits)
local mpeg_version = buffer(offset,1):bitfield(11,2)
header_tree:add(fields.mpeg_version, buffer(offset,1), mpeg_version)

-- 层 (2 bits)
local layer = buffer(offset,1):bitfield(13,2)
header_tree:add(fields.layer, buffer(offset,1), layer)

-- 保护位 (1 bit)
local protection_bit = buffer(offset,1):bitfield(15,1)
header_tree:add(fields.protection_bit, buffer(offset,1), protection_bit)

-- 比特率索引 (4 bits)
local bitrate_index = buffer(offset,2):bitfield(16,4)
header_tree:add(fields.bitrate_index, buffer(offset,2), bitrate_index)

-- 采样率索引 (2 bits)
local sampling_rate_index = buffer(offset,2):bitfield(20,2)
header_tree:add(fields.sampling_rate_index, buffer(offset,2), sampling_rate_index)

-- 填充位 (1 bit)
local padding_bit = buffer(offset,2):bitfield(22,1)
header_tree:add(fields.padding_bit, buffer(offset,2), padding_bit)

-- 私有位 (1 bit)
local private_bit = buffer(offset,2):bitfield(23,1)
header_tree:add(fields.private_bit, buffer(offset,2), private_bit)

-- 声道模式 (2 bits)
local mode = buffer(offset,3):bitfield(24,2)
header_tree:add(fields.mode, buffer(offset,3), mode)

-- 模式扩展 (2 bits)
local mode_extension = buffer(offset,3):bitfield(26,2)
header_tree:add(fields.mode_extension, buffer(offset,3), mode_extension)

-- 版权位 (1 bit)
local copyright = buffer(offset,3):bitfield(28,1)
header_tree:add(fields.copyright, buffer(offset,3), copyright)

-- 原版位 (1 bit)
local original = buffer(offset,3):bitfield(29,1)
header_tree:add(fields.original, buffer(offset,3), original)

-- 强调 (2 bits)
local emphasis = buffer(offset,3):bitfield(30,2)
header_tree:add(fields.emphasis, buffer(offset,3), emphasis)

return offset + 4
end

--local state = STATE_HEADER
--local bytes_expected = 9
--local tag_data_size = 0
--local tag_type = 0

-- FLV解析器主函数
function flv_proto.dissector(buffer, pinfo, tree)
--print(buffer)
--print("state:"..state)
pinfo.cols.protocol = flv_proto.name
local offset = 0
local subtree = tree:add(flv_proto, buffer(), "FLV Data")
-- 使用简单变量存储状态
local state = pinfo.private["flv_state"] or STATE_HEADER
print("状态"..state)
local bytes_expected = pinfo.private["flv_bytes_expected"] or 9
local tag_data_size = pinfo.private["flv_tag_data_size"] or 0
local tag_type = pinfo.private["flv_tag_type"] or 0

-- 初始化会话数据
if state == STATE_HEADER then
-- 检查FLV签名
if buffer:len() < 3 or buffer(0,3):string() ~= "FLV" then
return -- 不是FLV流
end
end


-- 状态机处理
while offset < buffer:len() do
if state == STATE_HEADER then
-- 处理FLV头
if buffer:len() - offset < bytes_expected then
pinfo.private["flv_state"] = state
pinfo.private["flv_bytes_expected"] = bytes_expected
pinfo.desegment_offset = offset
pinfo.desegment_len = bytes_expected - (buffer:len() - offset)
return
end
print("state:STATE_HEADER")
--print(subtree)
local header = subtree:add(flv_proto, buffer(offset, 9), "FLV Header")
--print(header)
--print("FLV Header:"..buffer(offset, 9))
header:add(fields.signature, buffer(offset,3)):append_text(" (FLV)")
offset = offset + 3

local version = buffer(offset,1):uint()
header:add(fields.version, buffer(offset,1))
offset = offset + 1

local flags = buffer(offset,1):uint()
local has_audio = (flags & 0x04) ~= 0
local has_video = (flags & 0x01) ~= 0
local flags_text = string.format("Audio=%s, Video=%s", tostring(has_audio), tostring(has_video))
header:add(fields.flags, buffer(offset,1)):append_text(" ("..flags_text..")")
offset = offset + 1

local header_size = buffer(offset,4):uint()
header:add(fields.header_size, buffer(offset,4))
offset = offset + 4

-- 准备解析第一个Previous Tag Size
state = STATE_PREV_TAG_SIZE
bytes_expected = 4

elseif state == STATE_PREV_TAG_SIZE then
-- 处理Previous Tag Size
if buffer:len() - offset < bytes_expected then
pinfo.private["flv_state"] = state
pinfo.private["flv_bytes_expected"] = bytes_expected
pinfo.desegment_offset = offset
pinfo.desegment_len = bytes_expected - (buffer:len() - offset)
return
end
print("state:STATE_PREV_TAG_SIZE")
local prev_size = buffer(offset,4):uint()
subtree:add(fields.prev_tag_size, buffer(offset,4))
offset = offset + 4

-- 准备解析标签头
state = STATE_TAG_HEADER
bytes_expected = 11

elseif state == STATE_TAG_HEADER then
-- 处理标签头
if buffer:len() - offset < bytes_expected then
pinfo.private["flv_state"] = state
pinfo.private["flv_bytes_expected"] = bytes_expected
pinfo.desegment_offset = offset
pinfo.desegment_len = bytes_expected - (buffer:len() - offset)
return
end
print("state:STATE_TAG_HEADER")
local tag_header = subtree:add(flv_proto, buffer(offset,11), "FLV Tag")
tag_type = buffer(offset,1):uint()
tag_header:add(fields.tag_type, buffer(offset,1))
offset = offset + 1

local data_size = buffer(offset,3):uint()
tag_header:add(fields.data_size, buffer(offset,3))
offset = offset + 3

local timestamp = buffer(offset,3):uint()
tag_header:add(fields.timestamp, buffer(offset,3))
offset = offset + 3

local timestamp_ext = buffer(offset,1):uint()
tag_header:add(fields.timestamp_ext, buffer(offset,1))
offset = offset + 1

local full_timestamp = timestamp + (timestamp_ext * 16777216) -- 256^3
tag_header:append_text(", Full Timestamp="..full_timestamp.."ms")

local stream_id = buffer(offset,3):uint()
tag_header:add(fields.stream_id, buffer(offset,3))
offset = offset + 3

-- 准备解析标签数据
state = STATE_TAG_DATA
bytes_expected = data_size
tag_data_size = data_size

elseif state == STATE_TAG_DATA then
-- 处理标签数据
if buffer:len() - offset < bytes_expected then
pinfo.private["flv_state"] = state
pinfo.private["flv_bytes_expected"] = bytes_expected
pinfo.private["flv_tag_data_size"] = tag_data_size
pinfo.private["flv_tag_type"] = tag_type
pinfo.desegment_offset = offset
pinfo.desegment_len = bytes_expected - (buffer:len() - offset)
--print("STATE_TAG_DATA:"..bytes_expected - (buffer:len() - offset))
return
end
print("state:STATE_TAG_DATA")
local tag_data = subtree:add(flv_proto, buffer(offset, bytes_expected), "Tag Data")

if tag_type == 0x08 then -- 音频标签
local audio_start = offset
local audio_end = offset + bytes_expected - 1

if bytes_expected >= 1 then
local audio_flags = buffer(offset,1):uint()
local audio_header_tree = tag_data:add(flv_proto, buffer(offset,1), "Audio Header")

-- 解析音频标志
local format = (audio_flags & 0xF0) / 16
local rate = (audio_flags & 0x0C) / 4
local size = (audio_flags & 0x02) / 2
local audio_type = audio_flags & 0x01

audio_header_tree:add(fields.audio_header, buffer(offset,1))
audio_header_tree:add(fields.audio_format, buffer(offset,1), format)
audio_header_tree:add(fields.audio_rate, buffer(offset,1), rate)
audio_header_tree:add(fields.audio_size, buffer(offset,1), size)
audio_header_tree:add(fields.audio_type, buffer(offset,1), audio_type)

offset = offset + 1

-- 根据音频格式进行详细解析
if bytes_expected > 1 then
local audio_body = tag_data:add(flv_proto, buffer(offset, bytes_expected-1), "Audio Body")

-- AAC音频详细解析
if format == 10 then -- AAC
if bytes_expected > 2 then
local aac_packet_type = buffer(offset,1):uint()
audio_body:add(fields.aac_packet_type, buffer(offset,1), aac_packet_type)
offset = offset + 1

if aac_packet_type == 0 then -- AAC sequence header
audio_body:append_text(" (Sequence Header)")
offset = parse_aac_specific_config(audio_body, buffer, offset)

-- 剩余AAC配置数据
local remaining = audio_end - offset + 1
if remaining > 0 then
audio_body:add(buffer(offset, remaining), "AAC Config Data")
end
else -- AAC raw data
audio_body:append_text(" (Raw Data)")
audio_body:add(buffer(offset, bytes_expected-2), "AAC Audio Data")
end
end

-- MP3音频详细解析
elseif format == 2 or format == 14 then -- MP3
audio_body:append_text(" (MP3 Audio)")

-- 尝试解析MP3帧头
if bytes_expected > 4 then
offset = parse_mp3_frame_header(audio_body, buffer, offset)
end

-- 剩余MP3数据
local remaining = audio_end - offset + 1
if remaining > 0 then
audio_body:add(buffer(offset, remaining), "MP3 Audio Data")
end

-- ADPCM音频详细解析
elseif format == 1 then -- ADPCM
audio_body:append_text(" (ADPCM Audio)")

-- ADPCM头通常包含块大小等信息
if bytes_expected > 2 then
local adpcm_header = audio_body:add(flv_proto, buffer(offset,2), "ADPCM Header")
adpcm_header:add(ProtoField.uint8("flv.audio.adpcm.block_size", "Block Size"), buffer(offset,1))
adpcm_header:add(ProtoField.uint8("flv.audio.adpcm.samples", "Samples per Block"), buffer(offset+1,1))
offset = offset + 2
end

-- 剩余ADPCM数据
local remaining = audio_end - offset + 1
if remaining > 0 then
audio_body:add(buffer(offset, remaining), "ADPCM Data")
end

-- 其他音频格式
else
audio_body:append_text(" (Audio Data)")
audio_body:add(buffer(offset, bytes_expected-1), "Audio Data")
end
end
end

-- 确保偏移量正确
offset = audio_end + 1

elseif tag_type == 0x09 then -- 视频标签
local video_start = offset
local video_end = offset + bytes_expected - 1

if bytes_expected >= 1 then
local video_flags = buffer(offset,1):uint()
local video_header_tree = tag_data:add(flv_proto, buffer(offset,1), "Video Header")

-- 视频标志处理
local frame_type = (video_flags & 0xF0) / 16
local codec_id = video_flags & 0x0F

video_header_tree:add(fields.video_header_flags, buffer(offset,1))
video_header_tree:add(fields.video_frame, buffer(offset,1), frame_type)
video_header_tree:add(fields.video_codec, buffer(offset,1), codec_id)

offset = offset + 1

-- 视频体解析
if bytes_expected > 1 then
local video_body = tag_data:add(flv_proto, buffer(offset, bytes_expected-1), "Video Body")

-- AVC/H.264视频解析
if codec_id == 7 then -- AVC
if bytes_expected > 4 then
local avc_header_tree = video_body:add(flv_proto, buffer(offset,4), "AVC Header")

-- AVC包类型
local avc_packet_type = buffer(offset,1):uint()
avc_header_tree:add(fields.avc_packet_type, buffer(offset,1), avc_packet_type)
offset = offset + 1

-- Composition time
local composition_time = buffer(offset,3):uint()
avc_header_tree:add(fields.composition_time, buffer(offset,3), composition_time)
offset = offset + 3

-- AVC数据
local avc_data_size = bytes_expected - 5
if avc_data_size > 0 then
local avc_data_tree = video_body:add(flv_proto, buffer(offset, avc_data_size), "AVC Data")

if avc_packet_type == 0 then -- Sequence header
avc_data_tree:append_text(" (Sequence Header)")
elseif avc_packet_type == 1 then -- NALU
avc_data_tree:append_text(" (NALU Packets)")
end

avc_data_tree:add(buffer(offset, avc_data_size), "Video Data")
end
end
else
-- 其他视频编解码器
video_body:add(buffer(offset, bytes_expected-1), "Video Data")
end
end
end

-- 确保偏移量正确
offset = video_end + 1

elseif tag_type == 0x12 then -- 元数据标签
tag_data:add(buffer(offset, bytes_expected), "Metadata")
offset = offset + bytes_expected
else
tag_data:add(buffer(offset, bytes_expected), "Unknown Data")
offset = offset + bytes_expected
end

-- 准备解析下一个Previous Tag Size
state = STATE_PREV_TAG_SIZE
bytes_expected = 4

-- 重置状态变量
pinfo.private["flv_tag_data_size"] = nil
pinfo.private["flv_tag_type"] = nil
end
-- 保存当前状态
pinfo.private["flv_state"] = state
--print("状态2"..state)
pinfo.private["flv_bytes_expected"] = bytes_expected
end
end

-- 注册FLV解析器
local function heuristic_checker(buffer, pinfo, tree)
if buffer:len() >= 3 and buffer(0,3):string() == "FLV" then
flv_proto.dissector(buffer, pinfo, tree)
return true
end
return false
end

-- 注册解析器
local function enable_dissector()
DissectorTable.get("tcp.port"):add(7086, flv_proto) 
DissectorTable.get("tcp.port"):add(18080, flv_proto)
end

-- 禁用解析器
local function disable_dissector()
DissectorTable.get("tcp.port"):remove(7086, flv_proto)
DissectorTable.get("tcp.port"):remove(18080, flv_proto)
end

-- 注册菜单项
register_menu("FLV Protocol/Enable", enable_dissector, MENU_TOOLS_UNSORTED)
register_menu("FLV Protocol/Disable", disable_dissector, MENU_TOOLS_UNSORTED)

-- 默认启用
disable_dissector()

-- 注册启发式检查器
flv_proto:register_heuristic("tcp", heuristic_checker)

http://www.dtcms.com/a/354198.html

相关文章:

  • 嵌入式Linux(Exynos 4412)笔记
  • 3459. 最长 V 形对角线段的长度
  • 设计模式理解
  • Nishang PowerShell工具:原理详解+使用方法+渗透实战
  • Go+Gdal 完成高性能GIS数据空间分析
  • 深度学习:常用的损失函数的使用
  • “java简单吗?”Java的“简单”与PHP的挑战:编程语言哲学-优雅草卓伊凡
  • 白话FNN、RNN、Attention和self-attention等
  • 《从有限元到深度学习:我的金属疲劳研究进阶之路》
  • 反内卷加速全产业链价值重塑 通威股份等行业龙头或率先受益
  • 基于 C# OpenCVSharp 的模板匹配检测技术方案
  • 计算机日常答疑,一起寻找问题的最优解
  • select
  • SM4加密算法
  • Karatsuba
  • 前端工程化与AI融合:构建智能化开发体系
  • 4-4.Python 数据容器 - 字典 dict(字典 dict 概述、字典的定义与调用、字典的遍历、字典的常用方法)
  • CPU 虚拟化之Cpu Models
  • 代码随想录刷题Day43
  • 时间轮定时器HashedWheelTimer
  • WSL设置静态IP
  • window程序打包
  • Libvio网站与客户端访问故障排查指南(专业版)
  • 什么是低空经济?
  • JMeter 5.3 性能测试:文件下载脚本编写与导出文件接收完整指南
  • QT鼠标事件中的QMouseEvent :e
  • 深度学习---卷积神经网络CNN
  • PLC_博图系列☞基本指令”S_ODT:分配接通延时定时器参数并启动“
  • HTML5超详细学习内容
  • 程序(进程)地址空间(1)