PC: 网卡tc-link 8139.
pctcp网卡驱动安装: rtspkt 0x60 ,ethdrv
使用pctcp带的ping,ftp程序,能够使用。
编译环境:BC4.5
程序运行 wLstnSock = socket (AF_INET, SOCK_STREAM, 0);就会死机。
这事什么情况啊。请各位大侠帮忙看看。
Gemini 解答
核心结论
该问题并非底层硬件或 Packet Driver 驱动故障(系统自带的
ping和ftp可用已证明网络链路正常),而是由于编译配置不匹配(内存模型/调用约定)或遗漏了 PC/TCP 的前置初始化函数,导致调用socket()时触发空指针解引用或堆栈崩溃。
技术原因深度分析
1. 遗漏 PC/TCP SDK 的底层初始化例程
在现代操作系统中,调用 Berkeley Sockets API 通常无需手动处理底层驱动握手;但在 DOS 实模式下,PC/TCP(FTP Software 开发的商业协议栈)采用“驻留程序(TSR)+ 静态库包裹层(Wrapper)”的架构。
- 初始化依赖:PC/TCP SDK 的 Sockets 接口并不是独立的纯用户态代码,它需要与常驻内存的内核(如
ETHDRV/INETTSR)建立会话。- 在调用
socket()创建套接字之前,必须先调用 SDK 提供的专有初始化函数(如pc_init()、pctcp_init()或相关的套接字环境初始化例程)。- 如果跳过初始化直接执行
socket(),内部保存驱动中断向量表入口、共享内存段或会话控制块的指针仍为全零或未初始化野指针,一旦执行CALL FAR或触发软中断,CPU 将直接跳转到非法内存地址导致死机。2. 内存模型(Memory Model)不匹配
这是 Borland C++(BC4.5)混合链接老旧 DOS 静态库时最常见的问题:
- PC/TCP SDK 附带的
.LIB库文件几乎都是使用 Large(大内存模型) 编译的(代码指针为 32 位far指针,数据指针也为 32 位far指针)。- BC4.5 的新建工程若默认为 Small 或 Medium 模型,编译器会按 16 位近指针(
near)生成函数调用指令和参数入栈逻辑。调用库函数时,栈帧内的返回地址与参数偏移全部错位,进入库函数或返回调用方时必定导致栈崩溃。3. 函数调用约定与字节对齐差异
- 调用约定:PC/TCP SDK 最早多基于 Microsoft C 6.0/7.0 或 Borland C++ 3.1 编写。部分函数原型可能显式指定了
__pascal、__cdecl或_far _pascal。如果头文件未正确处理导出修饰符,而 BC4.5 开启了非默认调用约定,会导致参数清理责任错误(Caller vs Callee 清栈冲突)。- 结构体字节对齐:BC4.5 默认的结构体对齐可能为 Word(2 字节)或 Dword(4 字节),而实模式网络驱动通常强制要求 Byte 对齐(1 字节,即
-a-或#pragma pack(1))。如果相关网络参数结构大小不匹配,会导致内核通信时解析错误。4. 实模式栈空间溢出(Stack Overflow)
DOS 实模式下网络驱动的 API 嵌套较深,部分调用会涉及从用户栈切换至 Packet Driver 的内部栈。BC4.5 默认分配的堆栈通常较小(例如 2KB–4KB),在调用复杂的网络创建例程时,极易因局部变量和中断现场压栈导致实模式栈溢出,覆盖代码段或 DOS 关键数据区引起死机。
排查与解决步骤
补全初始化与退出清理 检查 PC/TCP SDK 头文件中的示例,确认调用流程中是否存在前置配置函数。标准流程通常应包含:
- 初始化网络子系统接口(获取 TSR 入口与中断向量)。
- 调用
socket()、bind()、connect()等标准接口。- 退出前必须显式调用相应的关闭清理函数,以注销在 TSR 中申请的会话句柄。
调整 Borland C++ 4.5 编译参数
- 确保目标平台选择为 DOS (Standard),禁止编译为 16 位 Windows 或 32 位 DPMI 目标。
- 内存模型必须强制设置为 Large Model(编译器开关
-ml)。- 强制结构体以 1 字节对齐(
-a1或-a-)。- 扩大工程的运行时栈空间,建议通过设置
_stklen = 0x4000;(分配 16KB 堆栈)防止栈溢出。
更好的工程替代方案:WATTCP / Watt-32
PC/TCP 是 1980–1990 年代的商业闭源协议栈,其 SDK 缺少公开维护的文档,且深度依赖特定版本的专有 TSR(如
ethdrv)。在 DOS 环境下基于 Packet Driver(如帖中的
rtspkt.com)开发网络通信应用,业界标准且成熟的合法开源方案是 WATTCP(Waterloo TCP/IP)或其现代扩展 Watt-32:
- 去 TSR 依赖:直接与网卡 Packet Driver(中断向量
0x60)通过标准中断规范通信,无需常驻商业协议栈内核。- 开发环境适配:原生支持 Borland C++ 各版本(BC3.1 / BC4.5 / BC5.02)及 DJGPP,提供完整的 BSD Sockets 兼容接口,并随附完整的文档与源码,调试难度远低于 PC/TCP SDK。
