news 2026/10/21 1:51:54

Delphi 中使用 XLSReadWrite 读写 Excel 的安装与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Delphi 中使用 XLSReadWrite 读写 Excel 的安装与实战

简介:针对 Delphi Tokyo 10.2 推出的 XLSReadWriteII 6.00.16 组件包,面向需在 Delphi 中读写 Excel 文件的开发者,可在不安装 Microsoft Office 的前提下直接处理 xls 与 xlsx 格式,兼顾历史版本与新版工作簿。压缩包共 816 个文件,大小 13.47MB,内含 hpp 头文件、pas 源码、dcu/obj 编译单元,以及大量 html 帮助文档和 dpr/dproj 示例工程,方便开发者直接查看接口并集成至 IDE。包内附带图表创建、数据透视表交互、自动筛选等示例配置,可快速掌握单元格赋值、公式写入、样式调整等核心操作,配套的 html 说明也能辅助排查常见错误。已有 312 人学习下载,适合正在使用 Tokyo 10.2 或需要高效生成报表的中高级 Delphi 程序员。使用该组件能降低项目对 Office 环境的依赖,提升大批量 Excel 数据的读写与处理效率。

1. XLSReadWrite 不是黑匣子:纯 Delphi 读写 Excel 的落地路径

项目交付时最怕现场说 Excel 版本不对。XLSReadWrite 系列组件就是为这种场景准备的:它把 Excel 文件的读写逻辑直接编译进 exe,运行时不依赖 Office COM,也不要求目标机器装 Office。6.00.16 这个版本明确面向 RAD Studio Tokyo 10.2,Delphi 项目里用这个版本能省掉一大半兼容性扯皮。

适合桌面管理软件、检测设备上位机这类要稳定导出报表、又不想被 Office 版本绑架的团队。把组件包从下载、安装到读写、排错完整走一遍,中间会给可直接抄的代码和参数说明,照着做就能跑通。

2. 装进 Tokyo 10.2:组件包编译与 IDE 注册的完整路径

2.1 先看包结构:设计期包、运行期包和源码目录

拿到资源包先别急着双击安装。XLSReadWriteII 这种带设计期注册的第三方组件,包通常分成两部分:运行期包负责纯读写逻辑,设计期包负责把组件注册进 IDE 工具栏。源码目录一般叫 Source,核心单元都在里面;包工程以 .dproj 或 .dpk 结尾。第一次动手我习惯把整个包放在一个无空格路径下,比如 C:\Lib\XLSRW,避免后续编译路径解析出问题。某工控项目的开发机把库放在“D:\三号产线\库文件”下,编译包时 msbuild 解析路径连续报错,改成纯英文路径后一次通过。

包结构上常见的是两到三个工程文件加一批源码。编译顺序必须是先运行期包后设计期包。顺序反了,设计期包找不到运行期类,直接报 unit not found 或 cannot load package。可以用下面的命令行把两个包依次编出来:

cd C:\Lib\XLSRW msbuild /t:Build /p:Config=Release /p:Platform=Win32 运行期包.dproj msbuild /t:Build /p:Config=Release /p:Platform=Win32 设计期包.dproj

第一行编译运行期包,第二行编译设计期包。包名替换成下载目录里实际存在的 .dproj 文件名,以资源包内的工程文件清单为准。参数说明:Config=Release 表示发布配置,避免 Debug 配置混入额外调试符号;Platform=Win32 指 IDE 用的 32 位平台,即使项目最终编成 64 位 exe,组件包这一步仍建议用 Win32,注册进 IDE 后 64 位由项目编译时单独处理。Release 和 Win32 这对组合是我在这个版本上验证过最稳的。

编译产物里有两个文件容易混淆:bpl 是运行时动态库,dcp 是包编译后的接口描述。项目如果启用了 Runtime Packages,链接器从 bpl 拿实现;项目关闭运行时包,编译器从 Source 目录的 dcu 拿实现。理解这层关系,就明白为什么装完包仍然报单元找不到。我一般会把编译好的 bpl 放在独立目录,用绝对路径注册,后续升级组件只要重新编译再覆盖,不用来回复制文件。

2.2 库路径和 bpl 注册的完整配置

编译通过只是第一步,注册属性也要配齐。Delphi 第三方组件安装有两个关键入口:Library Path 和 Install Packages。Library Path 告诉编译器源码在哪里,菜单路径是 Tools > Options > Environment Options > Delphi Options > Library,在 Library Path 追加 C:\Lib\XLSRW\Source。这一步不做,IDE 加载包后打开项目仍会报单元找不到,并且报错位置常常指向项目文件自身,容易误判成项目坏了。

Install Packages 负责把设计期 bpl 挂进 IDE,菜单路径是 Components > Install Packages,点 Add 选择编译好的设计期 bpl。Tokyo 10.2 编译出来的包文件带版本尾号,这个版本体系里对应 260。如果 bpl 显示 invalid package 或 version mismatch,说明包不是用当前 IDE 版本编的,解决方法是重新编译而不是硬加载。运行期包和设计期包的分工整理成表格更好对照:

包类型编译产物安装方式缺失时的表现
运行期包带类实现的 bpl / dcp加进库路径打开项目报 unit not found
设计期包注册 IDE 组件的 bplInstall Packages组件面板不出现组件

装完之后组件面板会多出一整套读写组件,拖一个到窗体上,大多场景只需要关注主控件本身的属性就够了。验证是否装好的标准动作是新建一个空项目,拖组件,放一句断点在加载调用上,跑起来能进断点就算通。

一个容易忽略的点:包安装只影响设计期,真正编译目标项目时,项目 Options 里的 Runtime Packages 如果设置为不使用运行时包,链接时会按源码重新编译一份组件实例,这时 Source 路径的优先级就会体现出来。所以我把 Source 目录放在 Library Path 的最前面,避免 IDE 拉到旧版本残存。升级 IDE 大版本时也不要直接拷贝 bpl,必须回到当前版本里重编,否则下次打开项目还会触发版本不一致的报错。

到这里组件包才真正可用。接下来进入读文件的部分。

3. 读 Excel 文件:真实格式判定、表单遍历与单元格取值

3.1 先判断文件真实格式再加载

写读取代码前,我先做一步很多资料里不会提的检查:看文件头,而不是看扩展名。业务系统里经常遇到这种情况,某系统导出的文件叫 .xlsx,实际内容是从 HTML 表格或 CSV 改的扩展名。组件按 xlsx 解析一个 HTML 文件,不会立刻崩溃,而是读出来一个空表单或者报文件头错误。我写过一个工具函数读前几个字节:

function IsRealXlsx(const AFilePath: string): Boolean; var FS: TFileStream; Bytes: array[0..1] of Byte; begin Result := False; FS := TFileStream.Create(AFilePath, fmOpenRead or fmShareDenyWrite); try FS.Read(Bytes, 2); Result := (Bytes[0] = $50) and (Bytes[1] = $4B); // PK 压缩包头 finally FS.Free; end; end;

逻辑说明:xlsx 本质是 zip 压缩包,前两个字节固定是 PK;xls 老格式前 8 个字节是 D0 CF 11 E0 A1 B1 1A E1 这种 OLE2 复合文档头。判断完再决定用哪个分支去加载,能省掉大量诡异异常。参数说明:fmShareDenyWrite 表示只读共享,文件正被 Excel 占用时也能安全读取;这两个字节常量是文件格式规范定的,写死在代码里没有问题。

确认完格式再走正式加载。主类用 TExcelFile,加载和遍历的基础代码:

var Xls: TExcelFile; i: Integer; begin Xls := TExcelFile.Create(nil); try Xls.LoadFromFile('C:\Data\订单明细.xlsx'); for i := 0 to Xls.SheetCount - 1 do begin Xls.SheetIndex := i; // 业务按 SheetName 分支处理 end; finally Xls.Free; end; end;

逻辑说明:Create(nil) 表示组件不挂接窗体,用完手动 Free,这个模式在无界面服务里更安全;LoadFromFile 根据扩展名和内部解析器识别文件,不调用 Office COM;循环里 SheetIndex 是当前表单索引,切换后用 SheetName 拿名称。参数说明:SheetCount 从 0 开始,文件里只有一个表单时这个值是 1,循环到最后一个索引是 0。组件一般也提供按名索引表单的方法,具体名称不同小版本略有差异,以资源包内的帮助文档为准。

加载过程的异常也要处理。如果文件不是合法 Excel 结构,LoadFromFile 会抛出文件头或结构相关的异常。捕获后用 3.1 里的文件头判断函数分诊,是伪装文件就走别的读取分支,是文件损坏就提示用户重新导出,不要用裸异常打在界面上。

3.2 单元格坐标模型和类型枚举

读完表单,读格子。这套组件的坐标模型先列后行,从 0 开始,和 Excel 的 A1 风格正好相反,刚切换时容易把自己绕晕。下面代码扫描当前表单的所有有效单元格并分类型取值:

var Xls: TExcelFile; r, c: Integer; v: Variant; t: TXlsCellType; begin Xls := TExcelFile.Create(nil); try Xls.LoadFromFile('C:\Data\采样数据.xls'); Xls.SheetIndex := 0; for r := 0 to Xls.SheetHeight - 1 do for c := 0 to Xls.SheetWidth - 1 do begin if Xls.CellExists(c, r) then begin t := Xls.CellType[c, r]; case t of xlsCellFloat: v := Xls.AsFloat[c, r]; xlsCellString: v := Xls.AsString[c, r]; xlsCellDateTime: v := Xls.AsDateTime[c, r]; else v := ''; end; end; end; finally Xls.Free; end; end;

逻辑说明:双层循环遍历有效行列范围;CellExists 先判断格子存在性,避免空单元格走类型判断;CellType 拿到枚举后按类型分支取值。参数说明:SheetHeight 和 SheetWidth 返回当前表单有效数据边界,不是 Excel 里的 1048576 行上限;xlsCellDateTime 是日期时间专用类型,读出来直接是 Delphi 的 TDateTime,可以格式化输出,不用手工做浮点转换。

这里有一个典型误用:有人为了图省事,对日期列直接用 AsFloat,拿到 44300 这种序列值,再自己减 25569、乘 86400 换算,结果总有小误差。正确做法就是走类型枚举分支,让组件完成内部转换。读文件时如果发现表单名重复或为空,遍历时要跳过空名表单,这类文件多半是第三方程序生成的,不做保护容易在按名索引时崩掉。

3.3 稀疏表和大文件的遍历策略

Excel 表不保证从 A1 开始连续写。设备软件导出的表经常前两行是空行,中间还有整列空列。如果直接按 0 到 SheetHeight 循环,会读出一堆空壳逻辑,浪费在无意义的判断上。我一般会先取实际占用区域边界,再在这个范围内遍历。这个过程组件内部有现成的属性可以用,效果等同于 Excel 里的 UsedRange。

6.00.16 在解析大文件时会一次性把表格结构读进内存,文件越大首次加载越慢,但遍历时不再有磁盘 IO。如果目标文件有几十 MB,建议在加载前先显示等待状态,不要在 UI 线程里同步等。加载放后台线程时要注意:组件实例和主线程之间不要共享,每次后台任务创建独立实例,避免界面卡死和数据错乱。

循环里如果对每个单元格都做字符串格式化再拼接日志,时间开销会被放大。常见做法是先收集原始值和类型,最后统一格式化。读文件的性能瓶颈通常不在组件,而在取完值之后的处理代码上。

4. 写报表文件:新建、格式、公式与大数据量导出参数

4.1 新建文件与连续导出的状态重置

读是分析已有数据,写是造新文件。新建文件的流程和读的最大区别是:必须先 NewFile 初始化内存模型,否则组件会沿用上一次文件的状态。我见过连续导出两份报表时,第二份文件里出现第一份残留数据的案例,原因就是没调 NewFile。下面这段是写入几行数据的最小骨架:

var Xls: TExcelFile; begin Xls := TExcelFile.Create(nil); try Xls.NewFile('xlsx'); Xls.SheetName := '采样报告'; Xls.SheetIndex := 0; Xls.Cells[0, 0].AsString := '编号'; Xls.Cells[1, 0].AsString := '时间'; Xls.Cells[2, 0].AsString := '温度'; Xls.WriteToFile('C:\Output\report.xlsx'); finally Xls.Free; end; end;

逻辑说明:NewFile 的字符串参数决定文件格式,传 xlsx 生成 OOXML 格式;SheetName 设置当前表单显示名,中文一般可直接写入;WriteToFile 把内存模型一次性落盘。参数说明:如果目标环境是老版 Excel 2003,要改成 NewFile('xls'),但 xls 格式行数上限只有 65536 行,超出会写不进去。每次导出循环开始前强制调 NewFile,把文件状态清干净,这是我在连续报表里固定执行的第一行。

写文件还容易遇到目标文件被占用。WriteToFile 的目标文件如果正被 Excel 打开,Windows 会拒绝写入并抛共享冲突。我习惯先写到一个临时文件,落盘成功后再 CopyFile 覆盖目标文件,这样既避开占用,也防止写一半程序崩溃把原文件搞坏。事后在代码里把这个临时文件路径清理掉,避免磁盘堆积。

4.2 格式控制:字体、颜色、合并、列宽与冻结

单元格值写完了,报表还差格式。格式模型里,每个单元格的 Format 属性持有字体、对齐、背景、边框等子对象。下面这段给表头加了常用样式并做了一个合并单元格:

Xls.Cells[0, 0].AsString := '序号'; Xls.Cells[0, 0].Format.FontName := '微软雅黑'; Xls.Cells[0, 0].Format.FontSize := 12; Xls.Cells[0, 0].Format.Bold := True; Xls.Cells[0, 0].Format.BackgroundColor := $00DDEBF7; Xls.Cells[0, 0].Format.Alignment := xlsAlignCenter; Xls.MergeCells(0, 1, 0, 2);

逻辑说明:FontName、FontSize、Bold 是字体族属性;BackgroundColor 是 BGR 颜色顺序,这和网页的 RGB 相反,写反了颜色会变;MergeCells(Col1, Row1, Col2, Row2) 把坐标范围内区域合并。参数说明:跨列合并和跨行合并都走同一个方法。合并顺序有一条硬性经验:先给范围内每个单元格设置边框格式,再调 MergeCells。如果反过来,Excel 只保留左上角格式,边框会整块消失,这个坑在第一次做采集报表时翻过车。

列宽和行高在报表里同样常用。列宽用 ColWidth 属性按列设置,行高用 RowHeight 按行设置,单位分别是字符宽度和磅值。如果表单里有长文本摘要,我一般把该列宽度设到 40 到 50,中文在 xlsx 里按字符宽度估算,设置为 40 大约能显示 20 个汉字。显式设置列宽还能绕开组件默认列宽与 Excel 渲染策略不一致的问题,某次交付客户反馈列宽不对,就是显式设置后解决的。

冻结首行是导出表格的另一个高频需求。组件提供支持冻结行列的属性或方法,名称在不同小版本里不完全一致,效果等价于 Excel 的冻结窗格。设置时机在写完数据和格式之后、WriteToFile 之前,顺序错乱可能导致冻结区域引用错误。页面设置如纸张方向、缩放比例等也有对应属性,导出打印报表时统一设置一次,避免客户打开后打印排版错乱。

4.3 公式、数字格式与缓存结果

报表里公式的需求通常来自两类:合计行和百分比列。公式赋值走 AsFormulaString,传入是 Excel A1 风格公式文本:

Xls.Cells[4, 5].AsFormulaString := '=SUM(B2:B5)';

逻辑说明:赋值对象是公式文本,组件在 OOXML 里写的是公式定义,不会替你计算。Excel 打开后会触发重算,如果目标机器关了自动重算,打开会显示缓存结果。参数说明:公式区域引用是 A1 风格坐标,不是内部 Cells 索引;公式里带表单名且名称含空格,必须写成 ='采样报告'!A1 这种带单引号的形式,否则 Excel 会提示公式错误。

数字格式和公式不一样,它控制显示。日期列常见的做法是给整列设置数字格式串 yyyy-mm-dd hh:mm:ss,然后循环只写 AsDateTime 值。格式串放在 WriteToFile 前统一设置,避免每个单元格重复触发格式对象分配。xls 和 xlsx 两种格式在能力上有差异,选型时对照一下:

能力xlsxlsx
最大行列65536 行 x 256 列1048576 行 x 16384 列
中文字符集依赖代码页UTF-8 内建
公式复杂度基本公式完整 OOXML 公式
文件体积相对小相对大

4.4 大数据量导出的节奏与内存释放

大数据量导出的核心问题是格式对象数量。5 万行数据,如果每行都单独设置边框和背景,组件内部要给每个单元格创建独立格式对象,时间和内存都上去了。我一般把写值和写格式拆成两遍:第一遍循环用 AsString、AsFloat、AsDateTime 写值,第二遍按区域统一设置格式,而不是逐格设。如果数据源本身有条件判断,也可以在写入循环里只在条件成立时设置格式,其他行保持默认。关键点是减少格式对象的创建次数,而不是减少格式代码行数。

另一个容易忽略的是对象释放顺序。TExcelFile 内部持有整个工作簿结构,Free 之后才会真正释放内存。在窗口关闭事件里如果只关了窗体没 Free 组件,下次打开会出现内存地址持续增长。我一般用 try/finally 保证 Free 一定执行,像对待数据库连接一样对待组件实例。

5. 避坑排查:安装报错、中文乱码与格式丢失的常见现场

5.1 安装阶段:包版本不匹配与路径残留

现象:把 6.00.16 的包在 Tokyo 10.2 里编译,报 E2382 或提示包版本不一致,装不进组件面板。

原因:资源包明确写的是 for Tokyo 10.2,实际 IDE 版本不是 10.2,或机器上有旧版组件 dcu 残留在 Library Path,编译器优先拉到了旧文件。第三方组件安装 80% 的报错都出在这两类。

解决:先确认 IDE 版本代号是 Tokyo 10.2,再用 msbuild 重编两个包,Library Path 里移除指向旧版 XLSReadWrite 的目录,清理旧 dcu 后重新 Build。如果换了 IDE 大版本,不要指望老 bpl 直接兼容,重编是唯一可靠做法。

现象:bpl 能加载,新项目拖组件时报 unit not found。

原因:Library Path 没加 Source 目录,IDE 找不到实现单元。

解决:把 Source 目录追加到 Library Path 最前面,并确认路径无空格无中文。这两条是安装阶段的高频现场,处理完基本能通。

5.2 读写阶段:伪装文件、中文乱码与日期序列值

现象:LoadFromFile 一个 xlsx,读出来全是空表单,Excel 打开正常。

原因:文件实际是 HTML 表格或 CSV 改了扩展名,组件按 xlsx 解析自然没有结果。

解决:加载前读文件头判断真实格式,xlsx 以 PK 开头,xls 以 D0CF 开头,伪装的直接拒掉或换分支读取。判断函数可以复用 3.1 节里的工具代码。

现象:中文 Sheet 名和单元格中文变成 ?? 或乱码。

原因:文件声明编码和组件默认字符集不一致。

解决:加载前把组件编码选项切到 UTF-8,先设编码再 LoadFromFile,不要在文件打开后再改字符集,否则内部解析状态会不一致。

现象:日期列读出来是 44300 这样的浮点数。

原因:走了 AsFloat 路径,没有触发日期类型转换。

解决:用 CellType 判断分支,xlsCellDateTime 走 AsDateTime,写文件时也显式用 AsDateTime 加数字格式串,不要用裸浮点拼字符串。

5.3 格式类问题:合并边框消失与公式引用失效

现象:合并单元格保存后再打开,边框整块消失。

原因:先合并后设边框,Excel 只接受合并区域左上角的格式记录,其他区域在合并后失去了独立格式通道。

解决:先给范围内每个单元格设置边框格式,再调 MergeCells。不只是边框,填充色和字体也遵循这个顺序。

现象:公式引用带空格的表单名,保存后显示 REF 错误。

原因:公式文本里表单名没有加单引号,OOXML 解析时把表单名当成了单元格引用的一部分。

解决:按 Excel 规则写成 ='采样报告'!A1,不要在代码里手工拼裸名称。如果公式来源是业务侧配置的,做一层校验,至少替换掉空格外的特殊字符。

这五类问题覆盖了从安装到读写的大部分翻车现场。排查顺序建议按安装、读取、格式三步走,不要跳过文件头判断直接怀疑组件有 bug。多数情况是文件格式和代码路径的隐性冲突,不是组件本身的问题。

6. 模板填充实战:一次读取模板坐标,只写值不改格式

模板填充是导出报表时最省事的做法:把一份做好的 Excel 当模板,代码只负责填值,格式由模板文件自己留着。这个思路稳在格式和业务数据彻底分离,业务调整时改模板就行,代码不用跟着改。操作上分三步走:

var Xls: TExcelFile; begin Xls := TExcelFile.Create(nil); try Xls.LoadFromFile('C:\Templates\检测报告模板.xlsx'); Xls.Cells[0, 2].AsString := '设备编号:A-2024-017'; Xls.Cells[1, 3].AsInteger := 36; Xls.Cells[2, 3].AsFloat := 24.5; Xls.ExportToFile('C:\Output\检测报告_20241018.xlsx'); finally Xls.Free; end; end;

逻辑说明:模板加载后,只往固定坐标写值,不碰任何格式对象;ExportToFile 直接落盘,跳过格式再解析环节。参数说明:AsInteger 和 AsFloat 写入时要求目标单元格类型匹配,如果模板格子是文本格式,用 AsFloat 写进去会被当作字符串存储,最后显示出来的是文本而不是数字,直接在 Excel 里做后续计算时会出问题。

验证导出结果时,把生成文件和模板放在同一个 Excel 窗口里对照,重点看合并区域、边框和列宽是否一致。坐标要调整时,先在模板里数清行列再改代码,不要靠肉眼估计。从那以后我每次做报表导出都强制先跑一遍模板流程,不再从零堆格式。坐标对了、格式不丢,交付现场少一个沟通点。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 1:51:31

RoboMaster步兵机器人硬件选型与系统联调实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 1:51:02

【WebSocket】如何在 WebSocket 中实现用户认证和鉴权?

WebSocket 认证鉴权实战:从握手阶段到消息级别的安全控制 用户问题原文:如何在 WebSocket 中实现用户认证和鉴权? 在构建直播互动弹幕系统时,我们曾因认证机制缺陷导致恶意用户刷屏攻击,单日产生 200 万条垃圾弹幕。根本原因在于团队仅在 HTTP 握手阶段验证了 Cookie,却忽…

作者头像 李华
网站建设 2026/10/21 1:50:58

256×64图形点阵LCD模组驱动实战:SPI接口、初始化与显存映射

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 1:50:11

端侧AI芯片选型实战:AX8850性价比与部署全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 1:49:14

MapLibre Native 动态样式切换:3 行代码给地图换主题

MapLibre Native 动态样式切换:3 行代码给地图换主题 【免费下载链接】maplibre-native MapLibre Native - Interactive vector tile maps for iOS, Android and other platforms. 项目地址: https://gitcode.com/GitHub_Trending/ma/maplibre-native 地图应…

作者头像 李华
网站建设 2026/10/21 1:45:15

运输层考点地图:TCP/UDP可靠传输、拥塞控制与三次握手一次理清

计算机网络期末考试复习到第五章运输层,很多同学就开始头大了。这一章的考点密度特别高:端口、UDP/TCP、可靠传输、流量控制、拥塞控制、三次握手、四次挥手,哪一个拎出来都能出题。不少学校把这章当成整门课的"分水岭"&#xff0c…

作者头像 李华