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_t
pbuf_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_SOCKET
int 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_TCP
err_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)