EMS 知识库
首页 / knowledge/代码解析/midware/ethernt/lan

socket.h

源文件路径:midware/ethernt/lan/socket.h 所属模块:midware/ethernt/lan(W5500 以太网协议栈 — Socket API 契约层) 代码行数:470

1. 文件职责

本文件是 WIZnet 官方 ioLibrary 的 socket.h(文件头标注 V1.0.2 / 2013-10-21 / 作者 MidnightCow,见 socket.h:6、socket.h:9、socket.h:13),把 W5500 的 8 个硬件 socket 封装成一套类 Berkeley 接口。它是纯契约头:470 行中约 300 行是 Doxygen 注释,全部内容只有宏定义、三个枚举类型和 13 个函数原型,没有任何函数体、没有结构体定义、没有任何 extern 全局变量。

在本工程中它是应用层与 W5500 之间的唯一接口层(socket.c 是它唯一的实现者)。数据流方向为:业务层(云端 TCP 客户端、Modbus TCP 从站、DHCP/DNS)→ 本头文件声明的 API → socket.c → w5500.h 的寄存器读写宏 → SPI → W5500 芯片。

1.1 重要澄清:寄存器地址、状态枚举、命令码、结构体都不在本文件内

任务描述中提到的"哈希表寄存器地址、SOCK_* 状态枚举、命令码、ioLibrary 结构体"均不在 socket.h 中定义,它们分布在同目录的另外两个文件里。本文件只是通过 #include "wizchip_conf.h"(socket.h:84)间接获得这些符号。经 Grep 逐个确认,其真实定义位置如下(供交叉阅读):

内容类别真实定义位置关键符号(行号)
W5500 寄存器哈希表地址(含 Sn 块偏移)midware/ethernt/lan/w5500.hSn_CR(N)(441)、Sn_SR(N)(482)、Sn_DIPR(N)(508)、Sn_DPORT(N)(518)、Sn_MSSR(525)、Sn_TOS(535)、Sn_TTL(542)、Sn_TX_FSR(583)、Sn_RX_RSR(618)、Sn_IMR(N)(650);块偏移由 WIZCHIP_SREG_BLOCK(N) << 3 与 _W5500_IO_BASE_ 组合而成
socket 状态枚举 SOCK_*w5500.hSOCK_CLOSED(1037)、SOCK_INIT(1045)、SOCK_LISTEN(1053)、SOCK_SYNSENT(1062)、SOCK_SYNRECV(1070)、SOCK_ESTABLISHED(1079)、SOCK_FIN_WAIT(1087)、SOCK_CLOSING(1095)、SOCK_TIME_WAIT(1103)、SOCK_CLOSE_WAIT(1111)、SOCK_LAST_ACK(1118)、SOCK_UDP(1126)、SOCK_MACRAW(1136)
socket 命令码 Sn_CR_*w5500.hSn_CR_OPEN(922)、Sn_CR_LISTEN(933)、Sn_CR_CONNECT(945)、Sn_CR_DISCON(958)、Sn_CR_CLOSE(964)、Sn_CR_SEND(972)、Sn_CR_SEND_MAC(982)、Sn_CR_SEND_KEEP(990)、Sn_CR_RECV(998)
socket 中断位 Sn_IR_*w5500.hSn_IR_SENDOK(1005)、Sn_IR_TIMEOUT(1011)、Sn_IR_RECV(1017)、Sn_IR_DISCON(1023)、Sn_IR_CON(1029)
协议模式位 Sn_MR_*w5500.hSn_MR_MULTI(802)、Sn_MR_BCASTB(811)、Sn_MR_ND(821)、Sn_MR_UCASTB(829)、Sn_MR_MACRAW(836)、Sn_MR_UDP(844)、Sn_MR_TCP(850)、Sn_MR_CLOSE(856);别名 Sn_MR_MFEN(869)、Sn_MR_MMB(878)、Sn_MR_MIP6B(886)、Sn_MR_MC(895)
ioLibrary 结构体wizchip_conf.h__WIZCHIP(151-195)、wiz_PhyConf_t(299)、wiz_NetInfo_t(324, 类型名 wiz_NetInfo)、wiz_NetTimeout_t(353-357);枚举 ctlwizchip_type(224)、ctlnetwork_type(238)、intr_kind(276)、netmode_type(347)
芯片选型与 socket 数量wizchip_conf.h_WIZCHIP_ 5500(59)、_WIZCHIP_SOCK_NUM_ 8(138,条件 _WIZCHIP_ > 5100)

结论:阅读本文件时,凡注释里出现的 @ref Sn_MR_*、@ref SOCK_ESTABLISHED、@ref setSn_IR() 等,都是 Doxygen 交叉引用标签,指向 w5500.h/wizchip_conf.h,本文件并不定义它们。这也解释了为什么本文件只有 27 KB 却号称"寄存器"头文件——真正 80 KB 的寄存器定义在 w5500.h。

2. 关键数据结构与宏

2.1 类型与结果码

名称类型值说明(行号)
SOCKET宏(充当类型别名)uint8_t无 typedef,直接 #define SOCKET uint8_t,注释为"legacy driver"兼容(socket.h:86)。名字与 Winsock 的 SOCKET 类型冲突,属官方遗留风险
SOCK_OK宏1操作成功(socket.h:88)
SOCK_BUSY宏0非阻塞模式下"操作尚未完成"(socket.h:89)。与 SOCK_ERROR 同值
SOCK_FATAL宏-1000致命错误基准(socket.h:90)
SOCK_ERROR宏0错误基准值,后续 SOCKERR_* 全部是它减 N(socket.h:92)
SOCKFATAL_PACKLEN宏SOCK_FATAL - 1 = -1001包长非法(socket.h:107)

2.2 错误码全集

以下 13 个错误码全部以 SOCK_ERROR(0) 为基准递减,落在 -1 ~ -15 区间(socket.h:93-105):

宏值含义
SOCKERR_SOCKNUM-1非法 socket 号
SOCKERR_SOCKOPT-2非法 socket 选项
SOCKERR_SOCKINIT-3socket 未初始化(Sn_SR == SOCK_CLOSED)
SOCKERR_SOCKCLOSED-4socket 被意外关闭
SOCKERR_SOCKMODE-5该操作不支持当前 socket 模式
SOCKERR_SOCKFLAG-6非法 socket 标志
SOCKERR_SOCKSTATUS-7当前 Sn_SR 不允许该操作
SOCKERR_ARG-10参数非法(注意跳过了 -8、-9,官方留空)
SOCKERR_PORTZERO-11端口号为 0
SOCKERR_IPINVALID-12IP 地址非法
SOCKERR_TIMEOUT-13超时
SOCKERR_DATALEN-14数据长度为 0 或超过缓冲上限
SOCKERR_BUFFER-15socket 缓冲区不足

注意 SOCKERR_ARG 直接跳到 -10(socket.h:100),因此不能用"错误码 = -8/-9"或"范围连续性"做判断;正确做法是与符号比较(如 dhcp.c:916 的 if(ret == SOCKERR_TIMEOUT))。

2.3 SF_* socket 标志位(映射到 Sn_MR 高位)

名称定义(行号)展开后的实际值(本工程 _WIZCHIP_ == 5500)说明
SF_ETHER_OWN(Sn_MR_MFEN)(socket.h:112)0x80MACRAW 下只收广播/组播/本机包
SF_IGMP_VER2(Sn_MR_MC)(socket.h:113)0x20UDP+多播时选 IGMP v2
SF_TCP_NODELAY(Sn_MR_ND)(socket.h:114)0x20TCP 关闭延迟 ACK
SF_MULTI_ENABLE(Sn_MR_MULTI)(socket.h:115)0x80UDP 多播使能
SF_BROAD_BLOCK(Sn_MR_BCASTB)(socket.h:118)0x40W5500 专有:屏蔽广播包(受 #if _WIZCHIP_ == 5500 门控,socket.h:117)
SF_MULTI_BLOCK(Sn_MR_MMB)(socket.h:119)0x20W5500 专有:屏蔽组播包
SF_IPv6_BLOCK(Sn_MR_MIP6B)(socket.h:120)0x10W5500 专有:屏蔽 IPv6 包
SF_UNI_BLOCK(Sn_MR_UCASTB)(socket.h:121)0x10W5500 专有:UDP+多播下屏蔽单播包
SF_IO_NONBLOCK0x01(socket.h:124)0x01不是 Sn_MR 位,是 socket() 的 flag 参数里的独立控制位,代表非阻塞 IO

由于 w5500.h 把 Sn_MR_MFEN、Sn_MR_MMB、Sn_MR_MIP6B、Sn_MR_MC 定义为其它位的别名(w5500.h:869/878/886/895),本工程中出现了三组 SF_* 常量值完全重合的情况(见 7 节风险点)。

2.4 非 TCP 包分片标志 PACK_*

名称值说明(行号)
PACK_FIRST0x80非 TCP 包(UDP/MACRAW):本包刚开始被读取(socket.h:129)
PACK_REMAINED0x01该包还有剩余数据未取完(socket.h:130)
PACK_COMPLETED0x00一整包已读完(socket.h:131)

三者被拼装进 socket.c 的静态数组 sock_pack_info[_WIZCHIP_SOCK_NUM_](socket.c:60),通过 getsockopt(sn, SO_PACKINFO, ...) 读出。注意头文件 V1.0.2 的修订记录(socket.h:10)就是把这组宏的历史名 SO_REMAINED 改名为 PACK_REMAINED。

2.5 IO 模式宏

名称值说明(行号)
SOCK_IO_BLOCK0阻塞模式,ctlsocket() 的 CS_SET_IOMODE 参数(socket.h:321)
SOCK_IO_NONBLOCK1非阻塞模式(socket.h:322)

2.6 枚举 sockint_kind(socket 中断类型位掩码)

定义于 socket.h:333-341,注释明确标注它对应寄存器 Sn_IR / Sn_IMR。与 w5500.h 的 Sn_IR_* 宏逐位一一对应:

枚举量值对应 w5500.h 宏(行号)含义
SIK_CONNECTED1 << 0 = 0x01Sn_IR_CON(1029)连接建立
SIK_DISCONNECTED1 << 1 = 0x02Sn_IR_DISCON(1023)连接断开
SIK_RECEIVED1 << 2 = 0x04Sn_IR_RECV(1017)收到数据
SIK_TIMEOUT1 << 3 = 0x08Sn_IR_TIMEOUT(1011)超时
SIK_SENT1 << 4 = 0x10Sn_IR_SENDOK(1005)发送完成
SIK_ALL0x1F上述五者按位或全部中断(低 5 位)

2.7 枚举 ctlsock_type(ctlsocket() 操作码)

定义于 socket.h:347-357,无显式赋值,实际序数为 0~7:

枚举量序数参数类型(按 socket.h:393-398 的表格)含义
CS_SET_IOMODE0uint8_t* = SOCK_IO_BLOCK/SOCK_IO_NONBLOCK设置 IO 模式
CS_GET_IOMODE1uint8_t*读 IO 模式
CS_GET_MAXTXBUF2uint16_t*(0~16K)读该 socket TX 内存分配上限
CS_GET_MAXRXBUF3uint16_t*(0~16K)读该 socket RX 内存分配上限
CS_CLR_INTERRUPT4uint8_t*(sockint_kind)清中断(写 Sn_IR)
CS_GET_INTERRUPT5uint8_t*(sockint_kind)读中断(读 Sn_IR)
CS_SET_INTMASK6uint8_t*(sockint_kind)设中断屏蔽(写 Sn_IMR)
CS_GET_INTMASK7uint8_t*(sockint_kind)读中断屏蔽(读 Sn_IMR)

2.8 枚举 sockopt_type(setsockopt() / getsockopt() 选项码)

定义于 socket.h:364-383,受两层条件编译裁剪。本工程 _WIZCHIP_ == 5500(wizchip_conf.h:59),因此 _WIZCHIP_ != 5100 与 _WIZCHIP_ > 5200 两个条件都成立,SO_KEEPALIVESEND 与 SO_KEEPALIVEAUTO 都被编译进来:

枚举量本工程序数条件门控方向参数类型对应寄存器(w5500.h)
SO_FLAG0无仅 getuint8_t*Sn_MR(get 时返回 & 0xF0 高四位,socket.c:676)
SO_TTL1无set/getuint8_tSn_TTL(w5500.h:542)
SO_TOS2无set/getuint8_tSn_TOS(w5500.h:535)
SO_MSS3无set/getuint16_tSn_MSSR(w5500.h:525)
SO_DESTIP4无set/getuint8_t[4]Sn_DIPR(w5500.h:508)
SO_DESTPORT5无set/getuint16_tSn_DPORT(w5500.h:518)
SO_KEEPALIVESEND6#if _WIZCHIP_ != 5100(socket.h:372)仅 setNULLSn_CR_SEND_KEEP + Sn_IR_TIMEOUT
SO_KEEPALIVEAUTO7#if _WIZCHIP_ > 5200(socket.h:374)set/getuint8_tSn_KPALVTR(w5500.h:670)
SO_SENDBUF8无仅 getuint16_t*Sn_TX_FSR(w5500.h:583)
SO_RECVBUF9无仅 getuint16_t*Sn_RX_RSR(w5500.h:618)
SO_STATUS10无仅 getuint8_t*Sn_SR(w5500.h:482),返回 SOCK_* 状态
SO_REMAINSIZE11无仅 getuint16_t*TCP 取 Sn_RX_RSR;非 TCP 取 sock_remained_size[sn]
SO_PACKINFO12无仅 getuint8_t*软件状态 sock_pack_info[sn],非寄存器

关键约束:这个枚举的序数是 _WIZCHIP_ 的函数。若 socket.h 与 socket.c 编译时看到的 _WIZCHIP_ 不一致(例如某处重复定义了 _WIZCHIP_),SO_SENDBUF 之后的选项会整体错位,静默读写错误的寄存器。本工程只有一处 #define _WIZCHIP_(wizchip_conf.h:59),风险受控。

2.9 文件内的大段契约性注释

socket.h:45-80 是一段 @defgroup WIZnet_socket_APIs 说明,含一张 WIZnet 与 Berkeley API 的对照表。这是理解本工程 socket 使用方式的前提,内容见 4.6 节。

3. 函数清单

本文件只有函数声明,没有函数定义;13 个原型全部是外部链接(无 static),全部由 socket.c 实现(send_u32() 除外,见 7 节)。

函数名(原型)作用域声明行适用模式说明
int8_t socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag)全局(声明)socket.h:150全部打开并初始化 socket;与 Berkeley 不同,同时完成绑定本地端口。成功返回该 socket 号。失败:SOCKERR_SOCKNUM/SOCKERR_SOCKMODE/SOCKERR_SOCKFLAG
int8_t close(uint8_t sn)全局(声明)socket.h:162全部关闭 socket,清中断并等待回到 SOCK_CLOSED。失败:SOCKERR_SOCKNUM
int8_t listen(uint8_t sn)全局(声明)socket.h:175仅 TCPTCP 被动模式监听并同时接受连接(无独立 accept());成功后进入 SOCK_LISTEN。失败:SOCKERR_SOCKINIT/SOCKERR_SOCKCLOSED
int8_t connect(uint8_t sn, uint8_t * addr, uint16_t port)全局(声明)socket.h:198仅 TCP 客户端向 addr:port 发起连接;阻塞模式等连接完成,非阻塞模式立即返回 SOCK_BUSY。失败还有 SOCKERR_IPINVALID/SOCKERR_PORTZERO/SOCKERR_TIMEOUT
int8_t disconnect(uint8_t sn)全局(声明)socket.h:215仅 TCP主动向对端发送断开请求。非阻塞模式立即返回 SOCK_BUSY
int32_t send(uint8_t sn, uint8_t * buf, uint32_t len)全局(声明)socket.h:235仅 TCPTCP 发送,返回实际写入字节数;不能超过 socket TX 缓冲。失败含 SOCKERR_SOCKSTATUS/SOCKERR_DATALEN/SOCKERR_TIMEOUT/SOCK_BUSY
int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len)全局(声明)socket.h:257仅 TCPTCP 接收,返回值是 uint32_t 而 len 是 uint16_t(宽度不对称);阻塞模式会一直等到收满 len。失败含 SOCKERR_SOCKSTATUS/SOCKERR_SOCKMODE/SOCKERR_DATALEN/SOCK_BUSY
int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port)全局(声明)socket.h:285UDP / MACRAW发送数据报,目的地址与端口由参数给出,即使 socket 已 connect 过也会被本次覆盖。失败含 SOCKERR_IPINVALID/SOCKERR_PORTZERO/SOCKERR_SOCKCLOSED
int32_t recvfrom(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port)全局(声明)socket.h:315UDP / MACRAW接收数据报,自带包头处理;支持分多次取出一整包,addr/port 仅首包有效,续包用 SO_PACKINFO/SO_REMAINSIZE 判断。MACRAW 下 addr/port 被忽略。失败含 SOCKERR_DATALEN/SOCKERR_SOCKMODE/SOCK_BUSY
int8_t ctlsocket(uint8_t sn, ctlsock_type cstype, void* arg)全局(声明)socket.h:402全部控制 IO 模式、清/读中断、设/读中断屏蔽,查询收发缓冲上限。第三参类型随 cstype 变化(见 2.7);失败仅 SOCKERR_ARG
int8_t setsockopt(uint8_t sn, sockopt_type sotype, void* arg)全局(声明)socket.h:430依选项写 TTL/TOS/MSS/目的 IP/目的端口/手工保活/保活自动周期。参数类型随 sotype 变化;失败含 SOCKERR_SOCKNUM/SOCKERR_SOCKMODE/SOCKERR_SOCKOPT/SOCKERR_TIMEOUT
int8_t getsockopt(uint8_t sn, sockopt_type sotype, void* arg)全局(声明)socket.h:464依选项读 FLAG/TOS/MSS/目的 IP/目的端口/保活周期/TX 空闲/已收字节/状态/剩余包长/包分片信息。失败含 SOCKERR_SOCKNUM/SOCKERR_SOCKOPT/SOCKERR_SOCKMODE
int32_t send_u32(uint8_t sn, uint8_t * buf, uint32_t len)全局(声明)socket.h:467—仅声明,全工程无实现也无调用(Grep send_u32 全仓仅命中 socket.h:467 一处),属遗留悬空接口;且与 send() 签名完全相同,属重复声明

4. 核心逻辑解析

本文件无代码逻辑,以下解析的是它所约定的契约,以及这些契约在实现层 socket.c 中的实际落点。

4.1 返回值三级语义(> 0 / = 0 / < 0)

结构性问题:SOCK_BUSY == SOCK_ERROR == 0。调用方若只做 ret > 0 / ret < 0 判断,会把"忙"与"错误基准值"混同。本工程正踩在此处——g_stNetSysInfo.s8SocketOk 用 SOCK_OK / SOCK_ERROR 双态表示连接状态,判定点见 midware/ethernt/ethernet.c:698、ethernet.c:715、ethernet.c:1007、ethernet.c:1054、ethernet.c:1301、ethernet.c:1310、midware/ethernt/wireless/wireless_driver.c:150、net/ems.c:787、net/ems.c:1886、net/ems.c:1890。

4.2 void* arg 的类型不安全契约

ctlsocket() / setsockopt() / getsockopt() 的第三参是 void*,实际写入宽度完全由 cstype/sotype 决定:SO_TTL 用 uint8_t*、SO_MSS 用 uint16_t*、SO_DESTIP 用 uint8_t[4]、SO_KEEPALIVESEND 忽略该参数。头文件用三张表格把映射写明(socket.h:393-398、socket.h:412-421、socket.h:440-452),编译器无法校验,类型错误不会报错只会静默写越界或截断。

4.3 非 TCP 包分片状态机(PACK_* 三态)

UDP / MACRAW 是数据报语义,但 W5500 的 RX 缓冲可能装不下一整包,故 recvfrom() 允许"分次取出"。状态转移依据是 socket.c 的静态数组 sock_remained_size[](socket.c:59)与 sock_pack_info[](socket.c:60):

  1. 首包:sock_remained_size[sn] == 0 时,recvfrom() 从 W5500 读包头,设置 sock_pack_info[sn] = PACK_FIRST,并回填 *addr/*port。
  2. 取走 pack_len 后执行 sock_remained_size[sn] -= pack_len(socket.c:566);若仍非 0,则 sock_pack_info[sn] |= PACK_REMAINED(socket.c:567-568)。
  3. 取完时 sock_pack_info[sn] 复位为 PACK_COMPLETED(0),下一次 recvfrom() 重新按首包处理。

对应的读接口是 getsockopt(sn, SO_PACKINFO, &v)(返回三态标志)与 getsockopt(sn, SO_REMAINSIZE, &v)(返回 sock_remained_size[sn],socket.c:705-710)。头文件 socket.h:459-463 的 @note 特别给出一个边界条件:当 SO_PACKINFO == PACK_FIRST 而 recvfrom() 返回 0 时,表示收到的是零长度 UDP 数据(只有 UDP 头)——这是 V1.0.2 版新增的说明(socket.h:11)。本工程的 dhcp.c/dns.c 均一次性收包,未使用续收路径。

4.4 sockint_kind 与寄存器 Sn_IR / Sn_IMR 的位对应

SIK_* 的 5 个位(0x01~0x10)与 w5500.h 的 Sn_IR_CON~Sn_IR_SENDOK 完全同值(见 2.6 表),所以 ctlsocket() 可以直接把 arg 透传给 setSn_IR()/setSn_IMR():

由于 SIK_ALL = 0x1F 只覆盖低 5 位,而 w5500.h 还定义了 IM_IR4~IM_IR7(0x10/0x20/0x40/0x80,w5500.h:770-791),Sn_IR/Sn_IMR 的第 5~7 位(Magic Packet、PPPoE Close、Destination unreachable 等)无法通过本 API 设置,会被 > SIK_ALL 检查直接拒绝。

4.5 recvfrom() 的 addr/port 有效性契约

头文件 socket.h:302-307 声明 addr 与 port "仅在本包第一次 recvfrom() 调用时有效",判断方式是在调用后用 getsockopt(sn, SO_PACKINFO, &packinfo) 检查 packinfo == PACK_FIRST。这条契约对应实现里的 if(sock_remained_size[sn] == 0) 分支。头文件的 @param port 段落文字被误并入 addr 段(socket.h:305-307),描述不完整,见 7 节。

4.6 WIZnet 与 Berkeley API 的差异(socket.h:45-80)

这是本文件最有信息量的注释段,直接决定了本工程的 socket 编号设计:

APIWIZnetBerkeley说明
socket()有有WIZnet 版同时完成绑定本地端口
bind()无有被 socket() 吸收
listen()有有WIZnet 版同时完成接受连接
accept()无有被 listen() 吸收
connect()/recv()/send()/recvfrom()/sendto()有有语义基本一致
closesocket()有有WIZnet 拆成 close() 与 disconnect()

后果(socket.h:72-79):WIZnet 允许同一个端口号上同时存在最多 8 个监听 socket;客户端连接到来时,编号最小的 socket 接受该连接,并从监听态转为通信态,此后不再监听。因此在 WIZnet 模型里"一个 socket 号 = 一个固定角色",而不是 Berkeley 那种"监听 socket 反复派生新连接 socket"。本工程据此把角色写死为:SOCK_TCP_CLIENT = 2(midware/ethernt/ethernet.h:97,云端 TCP 客户端)、SOCK_TCP_SERVER = 3(midware/ethernt/ethernet.h:98,Modbus TCP 从站);DHCP、DNS 另有自己的 socket 号(DHCP_SOCKET、DNS_SOCKET)。

该注释引用的两张图片 Berkeley_SOCKET.jpg / WIZnet_SOCKET.jpg(socket.h:71、socket.h:79)在仓库中不存在。

4.7 时序与全局变量读写

本文件不声明任何全局变量,也无线程/时序逻辑;但通过 #include "wizchip_conf.h"(socket.h:84)→ w5500.h → 工程公共头,间接引入 uCOS-II 环境,故 socket.c 能在内部使用 OSTimeGet() 做超时判定(socket.c:324、socket.c:354,2000 tick 门限)和 OSTimeDlyHMSM(0,0,0,1) 让出 CPU(socket.c:431)。任何包含本头文件的文件都隐式依赖 uCOS-II 头文件链。

实现层实际维护的全局状态(均未在本头文件暴露):

数组维度来自 _WIZCHIP_SOCK_NUM_,本工程为 8(wizchip_conf.h:138)。

5. 对外接口

5.1 本文件导出

5.2 被谁包含(8 处,已 Grep 确认)

包含者行号使用方式
midware/ethernt/lan/socket.csocket.c:52唯一实现者
midware/ethernt/lan/dhcp.cdhcp.c:52UDP:socket(700)、sendto(409/505/565/911)、recvfrom(581)、close(828)
midware/ethernt/lan/dns.cdns.c:55UDP:socket(520)、sendto(527)、recvfrom(539)、close(544/558)
midware/ethernt/ethernet.cethernet.c:15云端 TCP 客户端:connect(688)、setsockopt(699)、disconnect(708/970)、socket(723)、close(732)、send(593)
midware/ethernt/wireless/wireless_driver.cwireless_driver.c:24仅用 SOCK_OK 判连接态(150)
midware/interface/modbus/modbus.cmodbus.c:16Modbus TCP 从站:listen(9647)、setsockopt(9657)、recv(9515/9536)、send(9090 等 12 处)、close(9737)、socket(9744)
net/ems.cems.c:13用 SOCK_OK 判状态(787/1886/1890)、send(6241)
net/server.cserver.c:15用 recv(6298/6326)

5.3 各函数的实际调用者汇总

函数外部调用点
socket()ethernet.c:723、dhcp.c:700、dns.c:520、modbus.c:9744
close()ethernet.c:732、dhcp.c:828、dns.c:544、dns.c:558、modbus.c:9737
listen()modbus.c:9647(唯一)
connect()ethernet.c:688(唯一)
disconnect()ethernet.c:708、ethernet.c:970
send()ethernet.c:593、modbus.c 12 处、net/ems.c:6241
recv()modbus.c:9515、modbus.c:9536、net/server.c:6298、net/server.c:6326
sendto()dhcp.c:409/505/565/911、dns.c:527
recvfrom()dhcp.c:581、dns.c:539
setsockopt()ethernet.c:699、modbus.c:9657,均只用 SO_KEEPALIVEAUTO
getsockopt()无外部调用者(仅 socket.c:670 定义)
ctlsocket()无任何调用者(仅 socket.c:574 定义);枚举量 SIK_ALL 仅在 socket.c:599/606 的参数校验中被引用
send_u32()无实现、无调用,悬空

5.4 调用方向

socket.h 本身不调用任何函数。被它声明的 API 向下依赖:socket.c → w5500.h 的 getSn_*/setSn_* 宏 → wizchip_conf.h 的 _WIZCHIP_IO_MODE_ 总线/SPI 回调 → 硬件层。

6. 依赖关系

7. 备注与风险点

  1. send_u32() 悬空声明(socket.h:467):与 send() 签名完全重复,全工程无定义无调用(Grep 全仓仅一处命中)。若被调用会链接失败。建议删除。
  2. SOCK_BUSY == SOCK_ERROR == 0:无法区分"非阻塞未完成"与"错误基准值",是本套 API 的结构性缺陷。工程内 g_stNetSysInfo.s8SocketOk 正是贴在这个歧义上做双态连接标志(ethernet.c:698/715 等)。
  3. SF_* 常量在 W5500 下大面积别名重合(已用 w5500.h:869/878/886/895 逐条核对):
    • SF_ETHER_OWN == SF_MULTI_ENABLE == 0x80
    • SF_IGMP_VER2 == SF_TCP_NODELAY == SF_MULTI_BLOCK == 0x20
    • SF_IPv6_BLOCK == SF_UNI_BLOCK == 0x10

即传入 flag 时无法只表达其中一个语义;把 SF_TCP_NODELAY 用在 MACRAW socket 上,芯片会把该位解释为 MMB(屏蔽组播),反之亦然。头文件的条件编译(socket.h:117-122)在 W5500 上同时提供了两组名字,更容易让调用者误以为它们是互相独立的位。

  1. SF_IO_NONBLOCK(0x01) 不属于 Sn_MR 位空间:它与其余 SF_* 混在同一个 flag 参数里(socket() 的 flag)。0x01 落在 Sn_MR 的协议选择域(Sn_MR_TCP = 0x01)内,若实现层没有先剥掉这一位就直接写 Sn_MR,会把 socket 意外配成 TCP。使用时应只按头文件 socket.h:124 的约定传给 socket()。
  2. sockopt_type 序数随 _WIZCHIP_ 漂移(socket.h:372-376):SO_SENDBUF 及之后的选项序数在 5100/5200/5500 下不同。本工程只在一处定义 _WIZCHIP_(wizchip_conf.h:59),头/实现一致,暂无实害;但任何新增的"局部重定义 _WIZCHIP_"都会造成选项静默错位。
  3. ctlsocket() 的 CS_GET_INTMASK 在实现层缺少 break:socket.c:609-612 中 case CS_GET_INTMASK 执行完赋值后直接落到 default: return SOCKERR_ARG;,即已经读到了 Sn_IMR 却仍返回失败。头文件 socket.h:399-400 承诺"成功返回 SOCK_OK",与该实现不符。目前无调用者,未被触发。
  4. getsockopt() 存在两处 fall-through(socket.c:684-704):SO_MSS→SO_DESTIP、SO_SENDBUF→SO_RECVBUF→SO_STATUS。结果:查 SO_MSS 会把 4 字节 IP 写进 uint16_t*(写越界),查 SO_SENDBUF 实际得到的是 Sn_RX_RSR。头文件 socket.h:440-452 的表格逐项列出了这些选项,但没有提示实现缺陷,按表格使用会拿到错误结果。目前这两个选项无调用者。
  5. recvfrom() 的 port 参数文档缺失:socket.h:302-307 把 port 的"仅首包有效"说明误写进了 addr 段,@param port 段落不存在。调用方(dhcp.c:581、dns.c:539)依赖 *port 回填服务端端口,误在续包时读取会拿到旧值。另注 recv() 的返回值是 int32_t 而长度参数是 uint16_t,宽度不对称。
  6. SIK_ALL = 0x1F 覆盖不全:Sn_IR/Sn_IMR 的第 5~7 位有对应宏(w5500.h:770-791 的 IM_IR5~IM_IR7),但 ctlsocket() 在 socket.c:599/606 用 > SIK_ALL 拦截,这些中断无法通过本 API 管理。
  7. #define SOCKET uint8_t(socket.h:86):宏名 SOCKET 与 Windows/Winsock 的类型名冲突;在宿主机工具链上编译任何包含链含有 Winsock 的代码时会直接编译错误。嵌入式目标上无实害,但不利于代码移植或 PC 侧单元测试。
  8. 包含保护名 _SOCKET_H_(socket.h:81):过于通用,易与第三方库冲突,属官方遗留。
  9. SOCK_ANY_PORT_NUM 带尾分号的宏定义(socket.c:54,#define SOCK_ANY_PORT_NUM 0xC000;)不在本头文件内,但影响本头文件 API 的行为:socket() 传 port = SOCK_ANY_PORT_NUM 时由实现层替换为动态端口。该写法本身是风格缺陷(若出现在头文件中会污染所有包含者)。
  10. Doxygen 图片缺失:socket.h:71、socket.h:79 引用 Berkeley_SOCKET.jpg / WIZnet_SOCKET.jpg,仓库中不存在,只在生成文档时报错,属官方遗留。
  11. 枚举类型本身未被用作类型:sockint_kind 与 ctlsock_type 在本工程中没有任何变量声明使用(ctlsocket() 无调用者);仅 sockint_kind 的枚举量 SIK_ALL 在 socket.c:599/606 被引用做参数上界校验。若做代码裁剪,删除这两个枚举需同步修改 socket.c。