【LWIP】tcpip_input函数解析(协议栈入口)

收录于:

【LWIP】LWIP协议|相关知识汇总|LWIP学习笔记


索引:

(*input)().->.tcpip_input().->.ethernet_input()

........................................->.ip_input()


相关链接:

【LWIP】tcpip_input函数解析(协议栈入口)

【LWIP】Ip4_input函数分析

——|【LWIP】pbuf_realloc函数分析

【LWIP】tcp_input()函数分析

——|【LWIP】tcp_timewait_input函数解析

——|【LWIP】tcp_listen_input函数分析

【LWIP】tcp_receive函数分析

【LWIP】udp_input函数分析


调用流程: 

    当网卡收到数据后,调用数据帧接收函数进行数据帧的接收,最后通过(*input)()函数传至协议栈(实际是netif->input,有一层封装)。

    在添加网络接口时将netif->input()指向tcpip_input函数,即:网络协议栈入口为tcpip_input()函数

    tcpip_input()函数并不直接进行数据处理,通过tcpip_inpkt()函数将数据传递到tcpip_thread()函数进行处理。

    (可能挺绕的,流程就是:(*input)() -> tcpip_input() -> tcpip_inpkt() -> tcpip_thread())


函数简析:

    在注册网络接口netif_add()时,将netif->input指向tcpip_input()函数,通过调用netif->input()执行到该处。

    将收到的数据包传递到tcpip_thread(),用于进行ethernet_input()ip_input()的输入处理(不直接调用)


具体分析:

    (在源码中有详细注释)

    1.收到数据后,先经过一层回调函数(SylixOS 添加的回调函数,用于数据包的过滤)

    2.根据inp->flags标志位判断是否包含以太网报头(是否含有NETIF_FLAG_ETHARP或NETIF_FLAG_ETHERNET标志)

    3.若包含以太网报头,调用ethernet_input();若不包含以太网报头,调用ip_input()

     (注:不是直接调用,通过tcpip_inpkt()将数据传递至tcpip_thread()函数,在lwip主线程函数中调用)

    4.tcpip_inpkt()函数填充tcpip_msg结构体,发送一个邮箱消息(在tcpip_thread处进行处理)

    5.tcpip_thread()函数收到邮箱消息后,根据msg->type类型,进行不同操作。

     (tcpip_thread()函数第一次被调用时,会初始化所有子模块,并进入while循环,等待邮箱消息


流程图:


源码:

/**
 * @ingroup lwip_os
 * 将收到的数据包传递到tcpip_thread,用于进行ethernet_input或ip_input的输入处理(不直接调用),
 * 在注册网络接口netif_add()时,将netif->input指向tcpip_input()函数,通过调用netif->input()执行到该处。
 *
 * @参数 p   接收到的数据包,p->payload指向以太网头
 *           或IP报头(如果inp没有NETIF_FLAG_ETHARP或NETIF_FLAG_ETHERNET标记)。
 * @参数 inp 接收数据包的网络接口
 */
err_t
tcpip_input(struct pbuf *p, struct netif *inp)
{
#if defined(SYLIXOS) && defined(LWIP_HOOK_LINK_INPUT)
  /* SylixOS 添加的回调函数(数据包过滤) */
  if (LWIP_HOOK_LINK_INPUT(p, inp)) {
    pbuf_free(p);
    return ERR_OK;
  }
#endif /* SYLIXOS && LWIP_HOOK_TCPIP_INPUT */
  
#if LWIP_ETHERNET
  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
    return tcpip_inpkt(p, inp, ethernet_input);
  } else
#endif /* LWIP_ETHERNET */
  return tcpip_inpkt(p, inp, ip_input);
}
/**
 * 将收到的数据包传递到tcpip_thread进行输入处理
 *
 * @参数 p 收到的数据包
 * @参数 inp 接收数据包的网络接口
 * @参数 input_fn 要调用的函数(ethernet_input或ip_input)
 */
err_t
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
#if LWIP_TCPIP_CORE_LOCKING_INPUT
  err_t ret;
  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
  LOCK_TCPIP_CORE();
  ret = input_fn(p, inp);
  UNLOCK_TCPIP_CORE();
  return ret;
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
  struct tcpip_msg *msg;

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));

  /* 申请一个邮箱消息结构体(tcpip_msg共用体) */
  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
  if (msg == NULL) {
    return ERR_MEM;
  }

  /* 填充tcpip_msg结构体,发送一个邮箱消息(在tcpip_thread处进行处理) */
  msg->type = TCPIP_MSG_INPKT;
  msg->msg.inp.p = p;
  msg->msg.inp.netif = inp;
  msg->msg.inp.input_fn = input_fn;
  if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
    return ERR_MEM;
  }
  return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
}
/**
 * 主lwIP线程。该线程具有对lwIP核心函数的独占访问权(除非对它们的访问没有被锁定)。
 * 其他线程使用邮箱与此线程进行通信。
 *
 * 它还启动所有计时器,以确保它们在正确的线程上下文中运行。
 *
 * @参数 arg 未使用
 */
static void
tcpip_thread(void *arg)
{
  struct tcpip_msg *msg;
  LWIP_UNUSED_ARG(arg);

  if (tcpip_init_done != NULL) {
    tcpip_init_done(tcpip_init_done_arg);
  }

  LOCK_TCPIP_CORE();
  /* 主循环 */
  while (1) {
    UNLOCK_TCPIP_CORE();
    LWIP_TCPIP_THREAD_ALIVE();    //把它定义为触发一个看门狗的东西(目前为空)。
    /* 等待消息,在等待时处理超时 */
    TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
    LOCK_TCPIP_CORE();
    if (msg == NULL) {
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      continue;
    }
    switch (msg->type) {
#if !LWIP_TCPIP_CORE_LOCKING  //SylixOS中此处未执行(宏配置)
    case TCPIP_MSG_API:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
      msg->msg.api_msg.function(msg->msg.api_msg.msg);
      break;
    case TCPIP_MSG_API_CALL:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
      sys_sem_signal(msg->msg.api_call.sem);
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */

#if !LWIP_TCPIP_CORE_LOCKING_INPUT
    /* msg->type == TCPIP_MSG_INPKT,执行input_fn函数 */
    case TCPIP_MSG_INPKT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
      msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */

#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS  SylixOS中此处未执行(宏配置)
    case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */

    /* msg->type == TCPIP_MSG_CALLBACK,执行function函数 */
    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    /* msg->type == TCPIP_MSG_CALLBACK_STATIC,执行function函数 */
    case TCPIP_MSG_CALLBACK_STATIC:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      break;

    default:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      break;
    }
  }
}

 

Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它提供了丰富的模块,可以轻松实现TCP/IP协议。在Node.js中,可以使用net模块来创建TCP服务器和客户端,使用dgram模块来创建UDP服务器和客户端。TCP服务是面向连接的服务,需要进行三次握手形成对话,而UDP服务则是无连接的服务。下面是一个简单的TCP服务器的实现代码: ``` const net = require('net'); const server = net.createServer(); server.on('connection', (socket) => { console.log('有人连接了'); socket.write('hello'); socket.on('data', (data) => { console.log(data); }); }); server.listen(12345, '127.0.0.1'); ``` 这个代码创建了一个TCP服务器,监听本地的12345端口。当有客户端连接时,会输出“有人连接了”,并向客户端发送“hello”消息。当客户端发送数据时,会输出数据内容。 类似地,可以使用dgram模块来创建UDP服务器和客户端。下面是一个简单的UDP服务器的实现代码: ``` const dgram = require('dgram'); const server = dgram.createSocket('udp4'); server.on('message', (msg, rinfo) => { console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`); }); server.on('listening', () => { const address = server.address(); console.log(`server listening ${address.address}:${address.port}`); }); server.bind(41234); ``` 这个代码创建了一个UDP服务器,监听本地的41234端口。当有客户端发送数据时,会输出数据内容和客户端的IP地址和端口号。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Evan_ZGYF丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值