netbuf 结构体
struct netbuf{struct pbuf *p, *ptr; (1)ip_addr_t addr; (2)u16_t port; (3)};
**
| p 字段 | 的指针指向 pbuf 链表,这是基于 pbuf 上封装的结构体 |
|---|---|
| ptr | 也是指向 pbuf,但是它与 p 字段的指针有一点不一样,因为它可以指向任意的 pbuf,由 netbuf_next()与 netbuf_first()函数来控制。 |
| addr | 字段记录了数据发送方的 IP 地址。 |
| port | 记录了数据发送方的端口号。 |
处理 netbuf 字段的带参宏
#define netbuf_fromaddr(buf) (&((buf)->addr))#define netbuf_set_fromaddr(buf, fromaddr) \ip_addr_set(&((buf)->addr), fromaddr)#define netbuf_fromport(buf) ((buf)->port)
指向不同类型的pbuf链表
指向相同类型的pbuf链表

netbuf相关函数
netbuf_new() 申请结构体空间
struct netbuf *netbuf_new(void){struct netbuf *buf;buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);if (buf != NULL){memset(buf, 0, sizeof(struct netbuf));}return buf;}
netbuf_alloc() 申请pbuf内存空间
void * netbuf_alloc(struct netbuf *buf, u16_t size){LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);/* Deallocate any previously allocated memory. */if (buf->p != NULL){pbuf_free(buf->p);}buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);if (buf->p == NULL){return NULL;}LWIP_ASSERT("check that first pbuf can hold size",(buf->p->len >= size));buf->ptr = buf->p;return buf->p->payload;}
netbuf_free()释放pbuf的空间
void netbuf_free(struct netbuf *buf){LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);if (buf->p != NULL){pbuf_free(buf->p);}buf->p = buf->ptr = NULL;}
netbuf_ref()申请pbuf首部的内存空间
err_t netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size){LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);if (buf->p != NULL){pbuf_free(buf->p);}buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);if (buf->p == NULL){buf->ptr = NULL;return ERR_MEM;}((struct pbuf_rom *)buf->p)->payload = dataptr;buf->p->len = buf->p->tot_len = size;buf->ptr = buf->p;return ERR_OK;}
netbuf_chain()
是将 tail 中的 pbuf 数据连接到 head 中的 pbuf 后面,形成一个 pbuf链表,在调用此函数之后,会将 tail 结构删除。
void netbuf_chain(struct netbuf *head, struct netbuf *tail){LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);pbuf_cat(head->p, tail->p);head->ptr = head->p;memp_free(MEMP_NETBUF, tail);}
netbuf_data()
将 netbuf 结构体中的 ptr 指针指向的 pbuf 数据起始地址填写到 dataptr 中,
同时将数据长度填入 len 中,
netbuf 结构体中 p 字段的指针指向的数据可能是一个 pbuf 链表,但是这个函数的操作只能是将 ptr 指针指向的 pbuf 数据填写到 dataptr 中,
如果想要操作 netbuf 中 p 指向的链表数据,想要使用 netbuf_next()或者 netbuf_first()函数来调整 ptr 指针指向的 pbuf.
err_tm netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len){LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);if (buf->ptr == NULL){return ERR_BUF;}*dataptr = buf->ptr->payload;*len = buf->ptr->len;return ERR_OK;}
netbuf_next()与 netbuf_first()
netbuf_next()用于移动 netbuf 的 ptr 数据指针,使 ptr 指针指向 pbuf 链表的下一个 pbuf。
netbuf_first()函数可以将 ptr 指针指向 pbuf 链表的第一个 pbuf。
s8_t netbuf_next(struct netbuf *buf){LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);if (buf->ptr->next == NULL){return -1;}buf->ptr = buf->ptr->next;if (buf->ptr->next == NULL){return 1;}return 0;}void netbuf_first(struct netbuf *buf){LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);buf->ptr = buf->p;}
netbuf_copy()
将 netbuf 结构体数据区域 pbuf 中的所有数据拷贝到 dataptr 指针指向的存储区,
即使 pbuf(链表)中的数据被保存在多个 pbuf 中,它也会完全拷贝出来,len 参数指定要拷贝数据的最大长度,
如果 netbuf 的数据区域空间小于 len 指定的大小,那么内核只会拷贝 netbuf 数据区域大小的数据,
#define netbuf_copy_partial(buf, dataptr, len, offset) \pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))#define netbuf_copy(buf,dataptr,len) \netbuf_copy_partial(buf, dataptr, len, 0)u16_t pbuf_copy_partial(const struct pbuf *buf, void *dataptr,u16_t len, u16_t offset){const struct pbuf *p;u16_t left = 0;u16_t buf_copy_len;u16_t copied_total = 0;for (p = buf; len != 0 && p != NULL; p = p->next){if ((offset != 0) && (offset >= p->len)){offset = (u16_t)(offset - p->len);}else{buf_copy_len = (u16_t)(p->len - offset);if (buf_copy_len > len){buf_copy_len = len;}MEMCPY(&((char *)dataptr)[left],&((char *)p->payload)[offset], buf_copy_len);copied_total = (u16_t)(copied_total + buf_copy_len);left = (u16_t)(left + buf_copy_len);len = (u16_t)(len - buf_copy_len);offset = 0;}}return copied_total;}
netbuf_take() 拷贝数据
#define netbuf_take(buf, dataptr, len) \pbuf_take((buf)->p, dataptr, len)err_tpbuf_take(struct pbuf *buf, const void *dataptr, u16_t len){struct pbuf *p;size_t buf_copy_len;size_t total_copy_len = len;size_t copied_total = 0;if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)){return ERR_ARG;}/* 拷贝数据 */for (p = buf; total_copy_len != 0; p = p->next){LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);buf_copy_len = total_copy_len;if (buf_copy_len > p->len){/* 此 pbuf 无法保存所有剩余数据 */buf_copy_len = p->len;}/* 从 dataptr 拷贝数据到 p->payload*/MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len);total_copy_len -= buf_copy_len;copied_total += buf_copy_len;}return ERR_OK;}
其他操作 netbuf 的宏定义
//获取数据的总长度#define netbuf_len(buf) ((buf)->p->tot_len)//得到远端 IP 地址(目标 IP 地址)#define netbuf_fromaddr(buf) (&((buf)->addr))//设置远端 IP 地址(目标 IP 地址)#define netbuf_set_fromaddr(buf, fromaddr) \ip_addr_set(&((buf)->addr), fromaddr)//得到远端端口号#define netbuf_fromport(buf) ((buf)->port)
netconn 结构体
它能描述一个连接,供应用程序使用,同时内核的 NETCONN API 接口也对各种连接操作函数进行了统一的封装。
struct netconn{/** netconn 类型 */enum netconn_type type;/** 当前 netconn 状态 */enum netconn_state state;/** LwIP 的控制块指针,如 TCP 控制块、UDP 控制块 */union{10 struct ip_pcb *ip;struct tcp_pcb *tcp;struct udp_pcb *udp;struct raw_pcb *raw;} pcb;err_t pending_err;/** 这个 netconn 最后一个异步未报告的错误 */sys_sem_t op_completed; //信号量/** 消息邮箱,存储接收的数据,直到它们被提取 */sys_mbox_t recvmbox;/** 用于 TCP 服务器上的请求连接缓冲区 */sys_mbox_t acceptmbox;/** socket 描述符,用于 Socket API */#if LWIP_SOCKETint socket;#endif /* LWIP_SOCKET *//** 标志 */u8_t flags;#if LWIP_TCP/** 当调用 netconn_write()函数发送的数据不适合发送缓冲区时,数据会暂时存储在 current_msg 中,等待数据合适的时候进行发送 */struct api_msg *current_msg;#endif /* LWIP_TCP */35 /** 连接相关的回调函数 */netconn_callback callback;};
描述 netconn 的类型、状态及回调函数
enum netconn_type{NETCONN_INVALID = 0,/** TCP */NETCONN_TCP = 0x10,/** UDP */NETCONN_UDP = 0x20,/** UDP lite */NETCONN_UDPLITE = 0x21,/** 无校验 UDP */NETCONN_UDPNOCHKSUM = 0x22,/** Raw */NETCONN_RAW = 0x40};enum netconn_state{NETCONN_NONE, //不处于任何状态NETCONN_WRITE, //正在写(发送)数据NETCONN_LISTEN, //处于监听状态NETCONN_CONNECT, //处于连接状态NETCONN_CLOSE //处于关闭状态};typedef void (* netconn_callback)(struct netconn *,enum netconn_evt,u16_t len);
netconn 函数接口说明
netconn_new() 创建新的连接结构
//该函数本质是宏定义#define netconn_new(t) \netconn_new_with_proto_and_callback(t, 0, NULL)//真正实现的函数struct netconn * netconn_new_with_proto_and_callback(enum netconn_type t,u8_t proto,netconn_callback callback){struct netconn *conn;API_MSG_VAR_DECLARE(msg);API_MSG_VAR_ALLOC_RETURN_NULL(msg);conn = netconn_alloc(t, callback); (1)if (conn != NULL){err_t err;API_MSG_VAR_REF(msg).msg.n.proto = proto;API_MSG_VAR_REF(msg).conn = conn;err = netconn_apimsg(lwip_netconn_do_newconn,&API_MSG_VAR_REF(msg)); (2)if (err != ERR_OK){sys_sem_free(&conn->op_completed);sys_mbox_free(&conn->recvmbox);memp_free(MEMP_NETCONN, conn);API_MSG_VAR_FREE(msg);return NULL;}}API_MSG_VAR_FREE(msg);return conn;}
netconn_delete() 终止连接
它用于删除一个 netconn 连接结构,对于 TCP 连接,如果此时是处于连接状态的,在调用该函数后,将请求内核执行终止连接操作。
err_t netconn_delete(struct netconn *conn){err_t err;/* 判断一下 netconn 结构是否正确 */if (conn == NULL){return ERR_OK;}err = netconn_prepare_delete(conn); (1)if (err == ERR_OK){netconn_free(conn);}return err;}err_t netconn_prepare_delete(struct netconn *conn){err_t err;API_MSG_VAR_DECLARE(msg);if (conn == NULL){return ERR_OK;}API_MSG_VAR_ALLOC(msg);API_MSG_VAR_REF(msg).conn = conn;//记录时间API_MSG_VAR_REF(msg).msg.sd.polls_left =((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT+ TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;err = netconn_apimsg(lwip_netconn_do_delconn,&API_MSG_VAR_REF(msg)); (2)API_MSG_VAR_FREE(msg);if (err != ERR_OK){return err;}return ERR_OK;}
netconn_getaddr() 获取IP
连接结构的源 IP 地址、端口号与目标 IP 地址、端口号等信息
err_t netconn_getaddr(struct netconn *conn,ip_addr_t *addr,u16_t *port,u8_t local){API_MSG_VAR_DECLARE(msg);err_t err;API_MSG_VAR_ALLOC(msg);API_MSG_VAR_REF(msg).conn = conn;API_MSG_VAR_REF(msg).msg.ad.local = local;msg.msg.ad.ipaddr = addr;msg.msg.ad.port = port;err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);API_MSG_VAR_FREE(msg);return err;}
netconn_bind() 绑定IP
netconn_bind()函数用于将一个 IP 地址及端口号与 netconn 连接结构进行绑定,如果作为服务器端,这一步操作是必然需要的。
err_t netconn_bind(struct netconn *conn,const ip_addr_t *addr,u16_t port){API_MSG_VAR_DECLARE(msg);err_t err;/* 如果 IP 地址为空,将设置为 */if (addr == NULL){addr = IP4_ADDR_ANY;}API_MSG_VAR_ALLOC(msg);API_MSG_VAR_REF(msg).conn = conn;API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);API_MSG_VAR_REF(msg).msg.bc.port = port;err = netconn_apimsg(lwip_netconn_do_bind,&API_MSG_VAR_REF(msg));API_MSG_VAR_FREE(msg);return err;}
netconn_connect()客户端主动建立连接
netconn_connect()函数是一个主动建立连接的函数,它一般在客户端中调用,
将服务器端的 IP 地址和端口号与本地的 netconn 连接结构绑定,
- TCP 协议
- 使用该函数的时候就是进行握手的过程,调用的应用线程将阻塞至握手完成;
- UDP 协议
- 调用该函数只是设置 UDP 控制块的目标 IP 地址与目标端口号
err_t netconn_connect(struct netconn *conn,const ip_addr_t *addr,u16_t port){API_MSG_VAR_DECLARE(msg);err_t err;if (addr == NULL){addr = IP4_ADDR_ANY;}API_MSG_VAR_ALLOC(msg);API_MSG_VAR_REF(msg).conn = conn;API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);API_MSG_VAR_REF(msg).msg.bc.port = port;err = netconn_apimsg(lwip_netconn_do_connect,&API_MSG_VAR_REF(msg));API_MSG_VAR_FREE(msg);return err;}
netconn_disconnect() 终止UDP 协议
err_t netconn_disconnect(struct netconn *conn){API_MSG_VAR_DECLARE(msg);err_t err;API_MSG_VAR_ALLOC(msg);API_MSG_VAR_REF(msg).conn = conn;err = netconn_apimsg(lwip_netconn_do_disconnect,&API_MSG_VAR_REF(msg));API_MSG_VAR_FREE(msg);return err;}
netconn_accept()
该函数用于 TCP 服务器中,接受远端主机的连接,内核会在 acceptmbox 邮箱中获取一个连接请求
err_t netconn_accept(struct netconn *conn, struct netconn **new_conn){#if LWIP_TCPerr_t err;void *accept_ptr;struct netconn *newconn;err = netconn_err(conn);if (err != ERR_OK){/* return pending error */return err;}if (!NETCONN_ACCEPTMBOX_WAITABLE(conn)){/* 如果 acceptmbox 无效 */return ERR_CLSD;}//如果 netconn 是不阻塞的if (netconn_is_nonblocking(conn)){//从 acceptmbox 邮箱获取远端主机的连接请求if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr)== SYS_ARCH_TIMEOUT){//如果超时return ERR_WOULDBLOCK;}}else{//一直等待着远端的连接请求sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);}/* 触发连接事件的回调函数 */API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);if (lwip_netconn_is_err_msg(accept_ptr, &err)){/* 如果连接错误 */return err;}if (accept_ptr == NULL){/* 连接已终止 */return ERR_CLSD;}newconn = (struct netconn *)accept_ptr;*new_conn = newconn;return ERR_OK;}
netconn_recv() 获取数据包
从 recvmbox 邮箱中获取数据包,如果该邮箱中没有数据包,
那么线程调用这个函数将会进入阻塞状态以等待消息的到来,
如果在等待 TCP 连接上的数据时,远端主机终止连接,将返回一个终止连接的错误代码(ERR_CLSD)
