NuttX Socket 源码学习

发布于:2025-06-22 ⋅ 阅读:(16) ⋅ 点赞:(0)

概述

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)

核心组件

  1. Socket 抽象层: 提供统一的接口抽象,屏蔽不同协议族的差异

  2. 协议族接口: 为不同协议族提供统一的接口实现

  3. 连接管理: 管理 socket 连接的生命周期和状态

  4. 数据传输: 处理数据的发送和接收

核心数据结构

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)

流程:

  1. 参数验证和类型掩码处理

  2. 初始化 socket 基本结构

  3. 尝试 usrsock 接口(如果启用)

  4. 通过 net_sockif() 获取协议族接口

  5. 调用协议族的 si_setup() 进行特定初始化

  6. 设置非阻塞标志和初始化标志

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)

流程:

  1. 验证 socket 和地址有效性

  2. 调用协议族的 si_bind() 实现

  3. 设置 _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. 主要协议族

  1. INET 协议族 (IPv4/IPv6):

    • 支持 TCP、UDP、ICMP 等协议

    • 完整的网络功能支持

  2. LOCAL 协议族 (Unix 域套接字):

    • 进程间通信

    • 支持 SOCK_STREAM 和 SOCK_DGRAM

  3. CAN 协议族:

    • CAN 总线通信

    • 汽车电子应用

  4. 其他协议族:

    • 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 实现展现了优秀的软件架构设计:

  1. 分层设计: 清晰的抽象层次,良好的模块化

  2. 多态支持: 通过函数指针表实现协议族的多态性

  3. 可扩展性: 易于添加新的协议族支持

  4. 标准兼容: 遵循 POSIX/BSD socket API 标准

  5. 性能优化: 零拷贝、异步I/O等优化技术

  6. 健壮性: 完善的错误处理和资源管理

这个实现为嵌入式系统提供了功能完整、性能优秀的网络编程接口,是学习网络协议栈设计的优秀范例。

参考文件列表

  • 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 - 网络核心数据结构定义


网站公告

今日签到

点亮在社区的每一天
去签到