LwIP代码收发报流程分析(2)数据包的接收
上一篇博客,LwIP代码收发报流程分析(1)数据包的发送 我们一起分析了 LwIP 是如何将 ICMP echo 也就是 Ping 报文发送出去的过程,在这篇博客中我们将一起分析 LwIP 是如何接收网络数据包的,为后面我们将其移植到 CH32 MCU 上做做预习。
这次还是选 LwIP 官方代码中的 ping 例程作为代码分析的对象,代码位置在 LWIP\contrib-2.1.0\apps\ping\ping.c
文件中。
由于本博客主要是分析 LwIP 的接收数据包流程的,相关的代码也只选取围绕这一主旨的,其他的一些方面的细节,只要在流程清晰的情况下再找到代码自己看看一般就能明白。或者大家也可以论可以留言~ 咱们一起讨论、学习。
网络数据包的接收
上一篇博客中说到发包使用 ping_send
函数,那么与之对应的就是 ping_recv
函数了。
static void
ping_recv(int s)
{
char buf[64];
int len;
struct sockaddr_storage from;
int fromlen = sizeof(from);
/* 可以看出是使用 lwip_recvfrom 来进行报文的接收 */
while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
...
}
...
}
在下面的 lwip_recvfrom
函数中可以看出里面实际上是数据结构的转换,数据是调用了另外一个函数来获得的。
ssize_t
lwip_recvfrom(int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
...
sock = get_socket(s);
/* 组合为 msghdr 结构,用于传递给接收函数当参数*/
u16_t datagram_len = 0;
struct iovec vec;
struct msghdr msg;
err_t err;
vec.iov_base = mem;
vec.iov_len = len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_name = from;
msg.msg_namelen = (fromlen ? *fromlen : 0);
/* 网络数据包的来源实际上是通过这个函数 */
err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
...
}
}
可以看出实际上数据包的接收是 lwip_recvfrom_udp_raw
函数通过 msghdr 结构进行接收的。
这个函数从名字就可以看出来是用来接收像 udp 和 raw 这样的无连接协议的数据包使用的。
static err_t
lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
{
...
/*这里可以看出 flag 参数的目的就是对 socket 是否阻塞进行设置用的 */
if (flags & MSG_DONTWAIT) {
apiflags = NETCONN_DONTBLOCK;
} else {
apiflags = 0;
}
/* 这里看到只有把上一次的接收到的数据全部取走才会开启新的一次数据包的接收,
* 这也就是为什么要在接收的时候使用循环接收的原因。*/
buf = sock->lastdata.netbuf;
if (buf == NULL) {
err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
sock->lastdata.netbuf = buf;
}
...
/* 将接收到的 buf 转换为 iovec 结构 */
for (i = 0; (i < msg->msg_iovlen) && (copied < buflen)