netbuf 结构体

  1. struct netbuf
  2. {
  3. struct pbuf *p, *ptr; (1)
  4. ip_addr_t addr; (2)
  5. u16_t port; (3)
  6. };

**

p 字段 的指针指向 pbuf 链表,这是基于 pbuf 上封装的结构体
ptr 也是指向 pbuf,但是它与 p 字段的指针有一点不一样,因为它可以指向任意的 pbuf,由 netbuf_next()与 netbuf_first()函数来控制。
addr 字段记录了数据发送方的 IP 地址。
port 记录了数据发送方的端口号。

处理 netbuf 字段的带参宏

  1. #define netbuf_fromaddr(buf) (&((buf)->addr))
  2. #define netbuf_set_fromaddr(buf, fromaddr) \
  3. ip_addr_set(&((buf)->addr), fromaddr)
  4. #define netbuf_fromport(buf) ((buf)->port)

指向不同类型的pbuf链表

image.png

指向相同类型的pbuf链表

image.png

netbuf相关函数

netbuf_new() 申请结构体空间

  1. struct netbuf *netbuf_new(void)
  2. {
  3. struct netbuf *buf;
  4. buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
  5. if (buf != NULL)
  6. {
  7. memset(buf, 0, sizeof(struct netbuf));
  8. }
  9. return buf;
  10. }

netbuf_alloc() 申请pbuf内存空间


  1. void * netbuf_alloc(struct netbuf *buf, u16_t size)
  2. {
  3. LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
  4. /* Deallocate any previously allocated memory. */
  5. if (buf->p != NULL)
  6. {
  7. pbuf_free(buf->p);
  8. }
  9. buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
  10. if (buf->p == NULL)
  11. {
  12. return NULL;
  13. }
  14. LWIP_ASSERT("check that first pbuf can hold size",
  15. (buf->p->len >= size));
  16. buf->ptr = buf->p;
  17. return buf->p->payload;
  18. }


netbuf_free()释放pbuf的空间


  1. void netbuf_free(struct netbuf *buf)
  2. {
  3. LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
  4. if (buf->p != NULL)
  5. {
  6. pbuf_free(buf->p);
  7. }
  8. buf->p = buf->ptr = NULL;
  9. }


netbuf_ref()申请pbuf首部的内存空间

  1. err_t netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
  2. {
  3. LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
  4. if (buf->p != NULL)
  5. {
  6. pbuf_free(buf->p);
  7. }
  8. buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
  9. if (buf->p == NULL)
  10. {
  11. buf->ptr = NULL;
  12. return ERR_MEM;
  13. }
  14. ((struct pbuf_rom *)buf->p)->payload = dataptr;
  15. buf->p->len = buf->p->tot_len = size;
  16. buf->ptr = buf->p;
  17. return ERR_OK;
  18. }

netbuf_chain()

是将 tail 中的 pbuf 数据连接到 head 中的 pbuf 后面,形成一个 pbuf链表,在调用此函数之后,会将 tail 结构删除。

  1. void netbuf_chain(struct netbuf *head, struct netbuf *tail)
  2. {
  3. LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
  4. LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
  5. pbuf_cat(head->p, tail->p);
  6. head->ptr = head->p;
  7. memp_free(MEMP_NETBUF, tail);
  8. }

netbuf_data()

将 netbuf 结构体中的 ptr 指针指向的 pbuf 数据起始地址填写到 dataptr 中,
同时将数据长度填入 len 中,
netbuf 结构体中 p 字段的指针指向的数据可能是一个 pbuf 链表,但是这个函数的操作只能是将 ptr 指针指向的 pbuf 数据填写到 dataptr 中,
如果想要操作 netbuf 中 p 指向的链表数据,想要使用 netbuf_next()或者 netbuf_first()函数来调整 ptr 指针指向的 pbuf.

  1. err_tm netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
  2. {
  3. LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
  4. LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
  5. LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
  6. if (buf->ptr == NULL)
  7. {
  8. return ERR_BUF;
  9. }
  10. *dataptr = buf->ptr->payload;
  11. *len = buf->ptr->len;
  12. return ERR_OK;
  13. }

netbuf_next()与 netbuf_first()

netbuf_next()用于移动 netbuf 的 ptr 数据指针,使 ptr 指针指向 pbuf 链表的下一个 pbuf。
netbuf_first()函数可以将 ptr 指针指向 pbuf 链表的第一个 pbuf。

  1. s8_t netbuf_next(struct netbuf *buf)
  2. {
  3. LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
  4. if (buf->ptr->next == NULL)
  5. {
  6. return -1;
  7. }
  8. buf->ptr = buf->ptr->next;
  9. if (buf->ptr->next == NULL)
  10. {
  11. return 1;
  12. }
  13. return 0;
  14. }
  15. void netbuf_first(struct netbuf *buf)
  16. {
  17. LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
  18. buf->ptr = buf->p;
  19. }

netbuf_copy()

将 netbuf 结构体数据区域 pbuf 中的所有数据拷贝到 dataptr 指针指向的存储区,
即使 pbuf(链表)中的数据被保存在多个 pbuf 中,它也会完全拷贝出来,len 参数指定要拷贝数据的最大长度,
如果 netbuf 的数据区域空间小于 len 指定的大小,那么内核只会拷贝 netbuf 数据区域大小的数据,

  1. #define netbuf_copy_partial(buf, dataptr, len, offset) \
  2. pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
  3. #define netbuf_copy(buf,dataptr,len) \
  4. netbuf_copy_partial(buf, dataptr, len, 0)
  5. u16_t pbuf_copy_partial(const struct pbuf *buf, void *dataptr,
  6. u16_t len, u16_t offset)
  7. {
  8. const struct pbuf *p;
  9. u16_t left = 0;
  10. u16_t buf_copy_len;
  11. u16_t copied_total = 0;
  12. for (p = buf; len != 0 && p != NULL; p = p->next)
  13. {
  14. if ((offset != 0) && (offset >= p->len))
  15. {
  16. offset = (u16_t)(offset - p->len);
  17. }
  18. else
  19. {
  20. buf_copy_len = (u16_t)(p->len - offset);
  21. if (buf_copy_len > len)
  22. {
  23. buf_copy_len = len;
  24. }
  25. MEMCPY(&((char *)dataptr)[left],
  26. &((char *)p->payload)[offset], buf_copy_len);
  27. copied_total = (u16_t)(copied_total + buf_copy_len);
  28. left = (u16_t)(left + buf_copy_len);
  29. len = (u16_t)(len - buf_copy_len);
  30. offset = 0;
  31. }
  32. }
  33. return copied_total;
  34. }

netbuf_take() 拷贝数据

  1. #define netbuf_take(buf, dataptr, len) \
  2. pbuf_take((buf)->p, dataptr, len)
  3. err_t
  4. pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
  5. {
  6. struct pbuf *p;
  7. size_t buf_copy_len;
  8. size_t total_copy_len = len;
  9. size_t copied_total = 0;
  10. if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len))
  11. {
  12. return ERR_ARG;
  13. }
  14. /* 拷贝数据 */
  15. for (p = buf; total_copy_len != 0; p = p->next)
  16. {
  17. LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
  18. buf_copy_len = total_copy_len;
  19. if (buf_copy_len > p->len)
  20. {
  21. /* 此 pbuf 无法保存所有剩余数据 */
  22. buf_copy_len = p->len;
  23. }
  24. /* 从 dataptr 拷贝数据到 p->payload*/
  25. MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len);
  26. total_copy_len -= buf_copy_len;
  27. copied_total += buf_copy_len;
  28. }
  29. return ERR_OK;
  30. }

其他操作 netbuf 的宏定义

  1. //获取数据的总长度
  2. #define netbuf_len(buf) ((buf)->p->tot_len)
  3. //得到远端 IP 地址(目标 IP 地址)
  4. #define netbuf_fromaddr(buf) (&((buf)->addr))
  5. //设置远端 IP 地址(目标 IP 地址)
  6. #define netbuf_set_fromaddr(buf, fromaddr) \
  7. ip_addr_set(&((buf)->addr), fromaddr)
  8. //得到远端端口号
  9. #define netbuf_fromport(buf) ((buf)->port)

netconn 结构体

它能描述一个连接,供应用程序使用,同时内核的 NETCONN API 接口也对各种连接操作函数进行了统一的封装。

  1. struct netconn
  2. {
  3. /** netconn 类型 */
  4. enum netconn_type type;
  5. /** 当前 netconn 状态 */
  6. enum netconn_state state;
  7. /** LwIP 的控制块指针,如 TCP 控制块、UDP 控制块 */
  8. union
  9. {
  10. 10 struct ip_pcb *ip;
  11. struct tcp_pcb *tcp;
  12. struct udp_pcb *udp;
  13. struct raw_pcb *raw;
  14. } pcb;
  15. err_t pending_err;/** 这个 netconn 最后一个异步未报告的错误 */
  16. sys_sem_t op_completed; //信号量
  17. /** 消息邮箱,存储接收的数据,直到它们被提取 */
  18. sys_mbox_t recvmbox;
  19. /** 用于 TCP 服务器上的请求连接缓冲区 */
  20. sys_mbox_t acceptmbox;
  21. /** socket 描述符,用于 Socket API */
  22. #if LWIP_SOCKET
  23. int socket;
  24. #endif /* LWIP_SOCKET */
  25. /** 标志 */
  26. u8_t flags;
  27. #if LWIP_TCP
  28. /** 当调用 netconn_write()函数发送的数据不适合发送缓冲区时,
  29. 数据会暂时存储在 current_msg 中,等待数据合适的时候进行发送 */
  30. struct api_msg *current_msg;
  31. #endif /* LWIP_TCP */35 /** 连接相关的回调函数 */
  32. netconn_callback callback;
  33. };

描述 netconn 的类型、状态及回调函数

  1. enum netconn_type
  2. {
  3. NETCONN_INVALID = 0,
  4. /** TCP */
  5. NETCONN_TCP = 0x10,
  6. /** UDP */
  7. NETCONN_UDP = 0x20,
  8. /** UDP lite */
  9. NETCONN_UDPLITE = 0x21,
  10. /** 无校验 UDP */
  11. NETCONN_UDPNOCHKSUM = 0x22,
  12. /** Raw */
  13. NETCONN_RAW = 0x40
  14. };
  15. enum netconn_state
  16. {
  17. NETCONN_NONE, //不处于任何状态
  18. NETCONN_WRITE, //正在写(发送)数据
  19. NETCONN_LISTEN, //处于监听状态
  20. NETCONN_CONNECT, //处于连接状态
  21. NETCONN_CLOSE //处于关闭状态
  22. };
  23. typedef void (* netconn_callback)(struct netconn *,
  24. enum netconn_evt,
  25. u16_t len);

netconn 函数接口说明

netconn_new() 创建新的连接结构

  1. //该函数本质是宏定义
  2. #define netconn_new(t) \
  3. netconn_new_with_proto_and_callback(t, 0, NULL)
  4. //真正实现的函数
  5. struct netconn * netconn_new_with_proto_and_callback(enum netconn_type t,
  6. u8_t proto,
  7. netconn_callback callback)
  8. {
  9. struct netconn *conn;
  10. API_MSG_VAR_DECLARE(msg);
  11. API_MSG_VAR_ALLOC_RETURN_NULL(msg);
  12. conn = netconn_alloc(t, callback); (1)
  13. if (conn != NULL)
  14. {
  15. err_t err;
  16. API_MSG_VAR_REF(msg).msg.n.proto = proto;
  17. API_MSG_VAR_REF(msg).conn = conn;
  18. err = netconn_apimsg(lwip_netconn_do_newconn,
  19. &API_MSG_VAR_REF(msg)); (2)
  20. if (err != ERR_OK)
  21. {
  22. sys_sem_free(&conn->op_completed);
  23. sys_mbox_free(&conn->recvmbox);
  24. memp_free(MEMP_NETCONN, conn);
  25. API_MSG_VAR_FREE(msg);
  26. return NULL;
  27. }
  28. }
  29. API_MSG_VAR_FREE(msg);
  30. return conn;
  31. }

netconn_delete() 终止连接

它用于删除一个 netconn 连接结构,对于 TCP 连接,如果此时是处于连接状态的,在调用该函数后,将请求内核执行终止连接操作。

  1. err_t netconn_delete(struct netconn *conn)
  2. {
  3. err_t err;
  4. /* 判断一下 netconn 结构是否正确 */
  5. if (conn == NULL)
  6. {
  7. return ERR_OK;
  8. }
  9. err = netconn_prepare_delete(conn); (1)
  10. if (err == ERR_OK)
  11. {
  12. netconn_free(conn);
  13. }
  14. return err;
  15. }
  16. err_t netconn_prepare_delete(struct netconn *conn)
  17. {
  18. err_t err;
  19. API_MSG_VAR_DECLARE(msg);
  20. if (conn == NULL)
  21. {
  22. return ERR_OK;
  23. }
  24. API_MSG_VAR_ALLOC(msg);
  25. API_MSG_VAR_REF(msg).conn = conn;
  26. //记录时间
  27. API_MSG_VAR_REF(msg).msg.sd.polls_left =
  28. ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT
  29. + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
  30. err = netconn_apimsg(lwip_netconn_do_delconn,
  31. &API_MSG_VAR_REF(msg)); (2)
  32. API_MSG_VAR_FREE(msg);
  33. if (err != ERR_OK)
  34. {
  35. return err;
  36. }
  37. return ERR_OK;
  38. }

netconn_getaddr() 获取IP

连接结构的源 IP 地址、端口号与目标 IP 地址、端口号等信息

  1. err_t netconn_getaddr(struct netconn *conn,
  2. ip_addr_t *addr,
  3. u16_t *port,
  4. u8_t local)
  5. {
  6. API_MSG_VAR_DECLARE(msg);
  7. err_t err;
  8. API_MSG_VAR_ALLOC(msg);
  9. API_MSG_VAR_REF(msg).conn = conn;
  10. API_MSG_VAR_REF(msg).msg.ad.local = local;
  11. msg.msg.ad.ipaddr = addr;
  12. msg.msg.ad.port = port;
  13. err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
  14. API_MSG_VAR_FREE(msg);
  15. return err;
  16. }

netconn_bind() 绑定IP

netconn_bind()函数用于将一个 IP 地址及端口号与 netconn 连接结构进行绑定,如果作为服务器端,这一步操作是必然需要的。

  1. err_t netconn_bind(struct netconn *conn,
  2. const ip_addr_t *addr,
  3. u16_t port)
  4. {
  5. API_MSG_VAR_DECLARE(msg);
  6. err_t err;
  7. /* 如果 IP 地址为空,将设置为 */
  8. if (addr == NULL)
  9. {
  10. addr = IP4_ADDR_ANY;
  11. }
  12. API_MSG_VAR_ALLOC(msg);
  13. API_MSG_VAR_REF(msg).conn = conn;
  14. API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
  15. API_MSG_VAR_REF(msg).msg.bc.port = port;
  16. err = netconn_apimsg(lwip_netconn_do_bind,
  17. &API_MSG_VAR_REF(msg));
  18. API_MSG_VAR_FREE(msg);
  19. return err;
  20. }

netconn_connect()客户端主动建立连接

netconn_connect()函数是一个主动建立连接的函数,它一般在客户端中调用,
将服务器端的 IP 地址和端口号与本地的 netconn 连接结构绑定,

  • TCP 协议
    • 使用该函数的时候就是进行握手的过程,调用的应用线程将阻塞至握手完成;
  • UDP 协议
    • 调用该函数只是设置 UDP 控制块的目标 IP 地址与目标端口号
  1. err_t netconn_connect(struct netconn *conn,
  2. const ip_addr_t *addr,
  3. u16_t port)
  4. {
  5. API_MSG_VAR_DECLARE(msg);
  6. err_t err;
  7. if (addr == NULL)
  8. {
  9. addr = IP4_ADDR_ANY;
  10. }
  11. API_MSG_VAR_ALLOC(msg);
  12. API_MSG_VAR_REF(msg).conn = conn;
  13. API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
  14. API_MSG_VAR_REF(msg).msg.bc.port = port;
  15. err = netconn_apimsg(lwip_netconn_do_connect,
  16. &API_MSG_VAR_REF(msg));
  17. API_MSG_VAR_FREE(msg);
  18. return err;
  19. }

netconn_disconnect() 终止UDP 协议


  1. err_t netconn_disconnect(struct netconn *conn)
  2. {
  3. API_MSG_VAR_DECLARE(msg);
  4. err_t err;
  5. API_MSG_VAR_ALLOC(msg);
  6. API_MSG_VAR_REF(msg).conn = conn;
  7. err = netconn_apimsg(lwip_netconn_do_disconnect,
  8. &API_MSG_VAR_REF(msg));
  9. API_MSG_VAR_FREE(msg);
  10. return err;
  11. }


netconn_accept()

该函数用于 TCP 服务器中,接受远端主机的连接,内核会在 acceptmbox 邮箱中获取一个连接请求

  1. err_t netconn_accept(struct netconn *conn, struct netconn **new_conn)
  2. {
  3. #if LWIP_TCP
  4. err_t err;
  5. void *accept_ptr;
  6. struct netconn *newconn;
  7. err = netconn_err(conn);
  8. if (err != ERR_OK)
  9. {
  10. /* return pending error */
  11. return err;
  12. }
  13. if (!NETCONN_ACCEPTMBOX_WAITABLE(conn))
  14. {
  15. /* 如果 acceptmbox 无效 */
  16. return ERR_CLSD;
  17. }
  18. //如果 netconn 是不阻塞的
  19. if (netconn_is_nonblocking(conn))
  20. {
  21. //从 acceptmbox 邮箱获取远端主机的连接请求
  22. if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr)
  23. == SYS_ARCH_TIMEOUT)
  24. {
  25. //如果超时
  26. return ERR_WOULDBLOCK;
  27. }
  28. }
  29. else
  30. {
  31. //一直等待着远端的连接请求
  32. sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
  33. }
  34. /* 触发连接事件的回调函数 */
  35. API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
  36. if (lwip_netconn_is_err_msg(accept_ptr, &err))
  37. {
  38. /* 如果连接错误 */
  39. return err;
  40. }
  41. if (accept_ptr == NULL)
  42. {
  43. /* 连接已终止 */
  44. return ERR_CLSD;
  45. }
  46. newconn = (struct netconn *)accept_ptr;
  47. *new_conn = newconn;
  48. return ERR_OK;
  49. }

netconn_recv() 获取数据包

从 recvmbox 邮箱中获取数据包,如果该邮箱中没有数据包,
那么线程调用这个函数将会进入阻塞状态以等待消息的到来,
如果在等待 TCP 连接上的数据时,远端主机终止连接,将返回一个终止连接的错误代码(ERR_CLSD)