概述
NuttX 的 socket 实现是一个精心设计的网络编程接口,提供了标准的 BSD socket API。该实现采用分层架构设计,支持多种网络协议族(如 TCP/IP、UDP、Unix域套接字等),具有良好的可扩展性和模块化特性。
整体架构设计
分层架构
NuttX socket 采用经典的分层架构设计:
应用层 API (socket, bind, connect, etc.)
↓
Socket 抽象层 (socket.c, net_sockif.c)
↓
协议族接口层 (inet, local, can, netlink, etc.)
↓
具体协议实现层 (TCP, UDP, etc.)
↓
设备接口层 (netdev)
核心组件
Socket 抽象层: 提供统一的接口抽象,屏蔽不同协议族的差异
协议族接口: 为不同协议族提供统一的接口实现
连接管理: 管理 socket 连接的生命周期和状态
数据传输: 处理数据的发送和接收
核心数据结构
1. struct socket - Socket 结构体
// 位置: nuttx/include/nuttx/net/net.h
struct socket
{
uint8_t s_domain; // IP 域(协议族)
uint8_t s_type; // 协议类型(SOCK_STREAM, SOCK_DGRAM等)
uint16_t s_proto; // Socket 协议
FAR void *s_conn; // 连接对象(继承自 socket_conn_s)
// Socket 接口函数表
FAR const struct sock_intf_s *s_sockif;
};
作用:
表示一个 socket 实例
包含协议族、类型、协议等基本信息
通过 s_sockif 指向具体的协议实现接口
2. struct socket_conn_s - Socket 连接基类
// 位置: nuttx/include/nuttx/net/net.h
struct socket_conn_s
{
dq_entry_t node; // 双向链表节点
// 回调函数链表(用于异步事件处理)
FAR struct devif_callback_s *list;
FAR struct devif_callback_s *list_tail;
// Socket 选项
#ifdef CONFIG_NET_SOCKOPTS
int16_t s_error; // 最后的错误码
sockopt_t s_options; // 选择的 socket 选项
socktimeo_t s_rcvtimeo; // 接收超时值(决秒)
socktimeo_t s_sndtimeo; // 发送超时值(决秒)
#ifdef CONFIG_NET_SOLINGER
socktimeo_t s_linger; // Linger 超时值
#endif
#ifdef CONFIG_NET_BINDTODEVICE
uint8_t s_boundto; // 绑定的网络接口索引
#endif
#endif
uint8_t s_flags; // Socket 状态标志位
uint8_t s_tos; // IPv4 服务类型/IPv6 流量类
#if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)
uint8_t s_ttl; // 默认生存时间
#endif
};
状态标志位说明:
_SF_INITD
: Socket 结构已初始化_SF_NONBLOCK
: 非阻塞模式_SF_LISTENING
: 正在监听(SOCK_STREAM)_SF_BOUND
: 已绑定到地址_SF_CONNECTED
: 已连接_SF_CLOSED
: 已关闭
3. struct sock_intf_s - Socket 接口函数表
// 位置: nuttx/include/nuttx/net/net.h
struct sock_intf_s
{
// 基本操作
CODE int (*si_setup)(FAR struct socket *psock);
CODE sockcaps_t (*si_sockcaps)(FAR struct socket *psock);
CODE void (*si_addref)(FAR struct socket *psock);
// 连接管理
CODE int (*si_bind)(FAR struct socket *psock,
FAR const struct sockaddr *addr, socklen_t addrlen);
CODE int (*si_listen)(FAR struct socket *psock, int backlog);
CODE int (*si_connect)(FAR struct socket *psock,
FAR const struct sockaddr *addr, socklen_t addrlen);
CODE int (*si_accept)(FAR struct socket *psock,
FAR struct sockaddr *addr, FAR socklen_t *addrlen,
FAR struct socket *newsock, int flags);
// 数据传输
CODE ssize_t (*si_sendmsg)(FAR struct socket *psock,
FAR struct msghdr *msg, int flags);
CODE ssize_t (*si_recvmsg)(FAR struct socket *psock,
FAR struct msghdr *msg, int flags);
// 信息获取
CODE int (*si_getsockname)(FAR struct socket *psock,
FAR struct sockaddr *addr, FAR socklen_t *addrlen);
CODE int (*si_getpeername)(FAR struct socket *psock,
FAR struct sockaddr *addr, FAR socklen_t *addrlen);
// 其他操作
CODE int (*si_poll)(FAR struct socket *psock,
FAR struct pollfd *fds, bool setup);
CODE int (*si_close)(FAR struct socket *psock);
CODE int (*si_ioctl)(FAR struct socket *psock,
int cmd, unsigned long arg);
CODE int (*si_shutdown)(FAR struct socket *psock, int how);
// 可选功能
#ifdef CONFIG_NET_SOCKOPTS
CODE int (*si_getsockopt)(FAR struct socket *psock, int level,
int option, FAR void *value, FAR socklen_t *value_len);
CODE int (*si_setsockopt)(FAR struct socket *psock, int level,
int option, FAR const void *value, socklen_t value_len);
#endif
#ifdef CONFIG_NET_SENDFILE
CODE ssize_t (*si_sendfile)(FAR struct socket *psock,
FAR struct file *infile, FAR off_t *offset, size_t count);
#endif
};
作用: 定义了所有 socket 操作的虚函数表,实现多态性支持不同协议族。
Socket 生命周期管理
1. Socket 创建 (socket.c)
函数: psock_socket(int domain, int type, int protocol, FAR struct socket *psock)
流程:
参数验证和类型掩码处理
初始化 socket 基本结构
尝试 usrsock 接口(如果启用)
通过
net_sockif()
获取协议族接口调用协议族的
si_setup()
进行特定初始化设置非阻塞标志和初始化标志
int psock_socket(int domain, int type, int protocol, FAR struct socket *psock)
{
FAR const struct sock_intf_s *sockif = NULL;
int ret;
// 参数验证
if (type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_TYPE_MASK))
{
return -EINVAL;
}
// 初始化 socket 结构
psock->s_domain = domain;
psock->s_proto = protocol;
psock->s_conn = NULL;
psock->s_type = type & SOCK_TYPE_MASK;
// 获取协议族接口
sockif = net_sockif(domain, psock->s_type, psock->s_proto);
if (sockif == NULL)
{
return -EAFNOSUPPORT;
}
// 协议族特定初始化
psock->s_sockif = sockif;
ret = sockif->si_setup(psock);
if (ret >= 0)
{
FAR struct socket_conn_s *conn = psock->s_conn;
if (type & SOCK_NONBLOCK)
{
conn->s_flags |= _SF_NONBLOCK;
}
conn->s_flags |= _SF_INITD;
}
return ret;
}
2. Socket 绑定 (bind.c)
函数: psock_bind(FAR struct socket *psock, const struct sockaddr *addr, socklen_t addrlen)
流程:
验证 socket 和地址有效性
调用协议族的
si_bind()
实现设置
_SF_BOUND
标志
int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, socklen_t addrlen)
{
int ret = OK;
// 验证参数
if (!psock || psock->s_conn == NULL)
{
return -EBADF;
}
if (addr == NULL)
{
return -EFAULT;
}
// 调用协议族的绑定实现
if (psock->s_sockif->si_bind == NULL)
{
return -EOPNOTSUPP;
}
ret = psock->s_sockif->si_bind(psock, addr, addrlen);
// 设置绑定标志
if (ret >= 0)
{
FAR struct socket_conn_s *conn = psock->s_conn;
conn->s_flags |= _SF_BOUND;
}
return ret;
}
3. Socket 连接 (connect.c)
函数: psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen)
特点:
支持阻塞和非阻塞连接
取消点支持(可被信号中断)
连接状态管理
4. Socket 监听和接受 (listen.c, accept.c)
监听: psock_listen()
- 设置 _SF_LISTENING
标志 接受: psock_accept()
- 创建新的连接 socket
数据传输机制
1. 发送数据架构
send() -> sendto() -> sendmsg() -> psock_sendmsg() -> si_sendmsg()
统一接口: 所有发送函数最终都通过 psock_sendmsg()
实现
// send.c 中的实现
ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags)
{
struct msghdr msg;
struct iovec iov;
// 构造 msghdr 结构
iov.iov_base = (FAR void *)buf;
iov.iov_len = len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
// 统一调用 sendmsg
return psock_sendmsg(psock, &msg, flags);
}
2. 接收数据架构
recv() -> recvfrom() -> recvmsg() -> psock_recvmsg() -> si_recvmsg()
特点:
支持阻塞和非阻塞接收
超时控制
取消点支持
协议族支持
1. 协议族注册机制
通过 net_sockif()
函数进行协议族路由:
FAR const struct sock_intf_s *net_sockif(sa_family_t family, int type, int protocol)
{
switch (family)
{
case PF_INET: // IPv4
case PF_INET6: // IPv6
return inet_sockif(family, type, protocol);
case PF_LOCAL: // Unix 域套接字
return &g_local_sockif;
case PF_CAN: // CAN 总线
return &g_can_sockif;
case PF_NETLINK: // Netlink 套接字
return &g_netlink_sockif;
case PF_PACKET: // 原始包套接字
return &g_pkt_sockif;
case PF_BLUETOOTH: // 蓝牙套接字
return &g_bluetooth_sockif;
case PF_IEEE802154: // IEEE 802.15.4
return &g_ieee802154_sockif;
case PF_RPMSG: // RPMSG 套接字
return &g_rpmsg_sockif;
default:
return NULL;
}
}
2. 主要协议族
INET 协议族 (IPv4/IPv6):
支持 TCP、UDP、ICMP 等协议
完整的网络功能支持
LOCAL 协议族 (Unix 域套接字):
进程间通信
支持 SOCK_STREAM 和 SOCK_DGRAM
CAN 协议族:
CAN 总线通信
汽车电子应用
其他协议族:
NETLINK: 内核通信
PACKET: 原始数据包
BLUETOOTH: 蓝牙通信
Socket 选项管理
1. Socket 选项位定义
// socket.h 中的定义
#define _SO_BIT(o) (1 << (o))
#define _SO_BROADCAST _SO_BIT(SO_BROADCAST) // 广播权限
#define _SO_DEBUG _SO_BIT(SO_DEBUG) // 调试信息
#define _SO_DONTROUTE _SO_BIT(SO_DONTROUTE) // 绕过路由
#define _SO_KEEPALIVE _SO_BIT(SO_KEEPALIVE) // 保持连接活跃
#define _SO_LINGER _SO_BIT(SO_LINGER) // 延迟关闭
#define _SO_OOBINLINE _SO_BIT(SO_OOBINLINE) // 带外数据内联
#define _SO_RCVBUF _SO_BIT(SO_RCVBUF) // 接收缓冲区大小
#define _SO_RCVTIMEO _SO_BIT(SO_RCVTIMEO) // 接收超时
#define _SO_REUSEADDR _SO_BIT(SO_REUSEADDR) // 地址重用
#define _SO_SNDBUF _SO_BIT(SO_SNDBUF) // 发送缓冲区大小
#define _SO_SNDTIMEO _SO_BIT(SO_SNDTIMEO) // 发送超时
#define _SO_TIMESTAMP _SO_BIT(SO_TIMESTAMP) // 时间戳
#define _SO_BINDTODEVICE _SO_BIT(SO_BINDTODEVICE) // 绑定到设备
2. 选项操作宏
#define _SO_SETOPT(s,o) ((s) |= _SO_BIT(o)) // 设置选项
#define _SO_CLROPT(s,o) ((s) &= ~_SO_BIT(o)) // 清除选项
#define _SO_GETOPT(s,o) (((s) & _SO_BIT(o)) != 0) // 检查选项
3. 错误处理宏
#ifdef CONFIG_NET_SOCKOPTS
#define _SO_SETERRNO(s,e) \
do { \
if (s != NULL) { \
_SO_CONN_SETERRNO((s)->s_conn, e); \
} \
} while (0)
#endif
超时控制机制
1. 超时值定义
typedef uint16_t socktimeo_t; // 超时类型(决秒为单位,1决秒=0.1秒)
#define _SO_TIMEOUT(t) ((t) ? (t) * MSEC_PER_DSEC : UINT_MAX)
2. 超时检查函数
// net_timeo.c
int net_timeo(clock_t start_time, socktimeo_t timeo)
{
// 检查是否超时
if (timeo != 0)
{
clock_t elapsed = clock() - start_time;
if (elapsed >= timeo * MSEC_PER_DSEC)
{
return 1; // 超时
}
}
return 0; // 未超时
}
错误处理机制
1. 错误码设置
每个 socket 连接结构都包含错误字段:
int16_t s_error; // 最后发生的错误
2. 错误传播
内核错误通过返回负的错误码
用户空间API通过设置 errno 并返回 -1
3. 常见错误码
EBADF
: 无效的文件描述符EAFNOSUPPORT
: 不支持的地址族EADDRINUSE
: 地址已被使用ECONNREFUSED
: 连接被拒绝ETIMEDOUT
: 连接超时EAGAIN
: 资源暂时不可用
内存管理
1. 连接结构分配
每个协议族负责分配和管理自己的连接结构:
// 例如 TCP 连接分配
FAR struct tcp_conn_s *conn = tcp_alloc();
psock->s_conn = conn;
2. 缓冲区管理
使用 IOB (I/O Buffer) 进行数据缓冲
支持零拷贝优化
动态内存分配和释放
同步机制
1. 网络锁
net_lock(); // 获取网络全局锁
// 临界区代码
net_unlock(); // 释放网络全局锁
2. 回调机制
使用 devif_callback_s 结构进行异步事件处理:
struct devif_callback_s
{
FAR struct devif_callback_s *nxtdev; // 下一个回调
uint16_t flags; // 事件标志
uint16_t priv; // 私有数据
devif_callback_event_t event; // 回调函数
};
调试和监控
1. 调试宏
#ifdef CONFIG_DEBUG_NET
# define ninfo(format, ...) info(format, ##__VA_ARGS__)
# define nerr(format, ...) err(format, ##__VA_ARGS__)
#else
# define ninfo(x...)
# define nerr(x...)
#endif
2. 统计信息
NuttX 提供网络统计信息用于监控和调试:
连接数统计
数据包统计
错误统计
性能优化
1. 零拷贝支持
sendfile() 实现零拷贝文件传输
IOB 链表减少内存拷贝
2. 异步I/O
非阻塞操作支持
poll/select 多路复用
回调驱动的事件处理
3. 缓冲区优化
可配置的缓冲区大小
动态缓冲区分配
内存池管理
配置选项
1. 主要配置项
CONFIG_NET // 启用网络支持
CONFIG_NET_SOCKOPTS // 启用 socket 选项
CONFIG_NET_SOLINGER // 启用 linger 选项
CONFIG_NET_BINDTODEVICE // 启用绑定到设备
CONFIG_NET_SENDFILE // 启用 sendfile 支持
CONFIG_NET_USRSOCK // 启用用户空间 socket
2. 协议族配置
CONFIG_NET_LOCAL // Unix 域套接字
CONFIG_NET_CAN // CAN 总线
CONFIG_NET_NETLINK // Netlink 套接字
CONFIG_NET_PKT // 原始包套接字
CONFIG_NET_BLUETOOTH // 蓝牙支持
CONFIG_NET_IEEE802154 // IEEE 802.15.4 支持
扩展新协议族的方法
1. 定义协议族接口
const struct sock_intf_s g_myproto_sockif =
{
myproto_setup, // 初始化
myproto_sockcaps, // 能力查询
myproto_addref, // 引用计数
myproto_bind, // 绑定
myproto_getsockname, // 获取本地地址
myproto_getpeername, // 获取对端地址
myproto_listen, // 监听
myproto_connect, // 连接
myproto_accept, // 接受连接
myproto_poll, // 轮询
myproto_sendmsg, // 发送消息
myproto_recvmsg, // 接收消息
myproto_close, // 关闭
myproto_ioctl, // 控制操作
myproto_socketpair, // 创建套接字对
myproto_shutdown // 关闭连接
};
2. 在 net_sockif() 中注册
case PF_MYPROTO:
sockif = &g_myproto_sockif;
break;
3. 实现连接结构
struct myproto_conn_s
{
struct socket_conn_s sconn; // 基类(必须是第一个成员)
// 协议特定的字段
// ...
};
最佳实践
1. 错误处理
始终检查函数返回值
适当设置和传播错误码
提供有意义的错误信息
2. 内存管理
及时释放分配的资源
避免内存泄漏
使用引用计数管理共享资源
3. 并发安全
适当使用网络锁保护临界区
避免死锁和竞态条件
考虑中断上下文的安全性
4. 性能考虑
减少不必要的内存拷贝
使用适当的缓冲区大小
考虑异步操作的使用
总结
NuttX 的 socket 实现展现了优秀的软件架构设计:
分层设计: 清晰的抽象层次,良好的模块化
多态支持: 通过函数指针表实现协议族的多态性
可扩展性: 易于添加新的协议族支持
标准兼容: 遵循 POSIX/BSD socket API 标准
性能优化: 零拷贝、异步I/O等优化技术
健壮性: 完善的错误处理和资源管理
这个实现为嵌入式系统提供了功能完整、性能优秀的网络编程接口,是学习网络协议栈设计的优秀范例。
参考文件列表
nuttx/net/socket/socket.h
- Socket 模块头文件nuttx/net/socket/socket.c
- Socket 创建实现nuttx/net/socket/net_sockif.c
- 协议族接口路由nuttx/net/socket/bind.c
- Socket 绑定实现nuttx/net/socket/connect.c
- Socket 连接实现nuttx/net/socket/accept.c
- 连接接受实现nuttx/net/socket/send.c
- 数据发送实现nuttx/net/socket/recv.c
- 数据接收实现nuttx/net/socket/setsockopt.c
- Socket 选项设置nuttx/net/socket/getsockopt.c
- Socket 选项获取nuttx/include/nuttx/net/net.h
- 网络核心数据结构定义