调用流程: 数据包首先调用ethernet_input()函数到达数据链路层,去掉以太网头部;
根据以太网头部类型判断:如果是ARP报文传给调用arp_input()交给ARP协议处理,如果是IP报文就调用ip_input()进入IP层处理;
ip_input()函数中比较数据报文的目的IP地址,如果与某个网络接口的IP地址相同,则接收这个报文,依照IP头部的协议字段,调用各自协议的输入处理函数;
如果是TCP类型,调用tcp_input()。
函数简析:
tcp_input接收IP层递交上来的数据包,并根据数据包查找相应TCP控制块,并根据相关控制块所处的状态调用函数tcp_timewait_input、tcp_listen_input或tcp_process进行处理。
如果是调用的前两个函数,则tcp_input在这两个函数返回后就结束了,但若调用的是tcp_process函数,则函数返回后,tcp_input还要进行许多相应的处理。
具体分析: (在源码中有详细注释) 1.检查报文会否有效(pbuf大小是否小于TCP报头20字节,不包括选项);
2.判断是否是多播/单播报文,丢弃;
3.是否开启TCP校验(宏配置),若开启调用ip_chksum_pseudo函数进行TCP校验和验证;
4.获取TCP首部长度(包括选项部分);
5.将p指针移向pbuf的有效数据部分(除去TCP报文头和选项部分,若选项有一部分在第二个pbuf中,代码上操作较复杂);
6.网络字节序转主机字节序;
7.遍历tcp_active_pcbs链表,是否匹配(若匹配有操作将这个pcb移到链表前端,下次找到更快,可以借鉴);
8.若不在tcp_active_pcbs链表,遍历tcp_tw_pcbs和tcp_listen_pcbs链表,是否匹配,
若在tcp_tw_pcbs链表中匹配,调用tcp_timewait_input函数处理,若在tcp_listen_pcbs链表中匹配,调用tcp_listen_input函数处理,
在遍历tcp_listen_pcbs链表中还会判断本地ip是否设置任意IP地址,操作不同;
9.接上面第7.点,若在tcp_active_pcbs链表匹配,经过处理后进入tcp_process函数;
10.若是进入tcp_timewait_input函数,tcp_listen_input函数,tcp_input在这两个函数返回后就结束了,若是进入tcp_process函数,返回后还要做很多处理,
判断返回值类型(收到复位报文,双方连接成功关闭,收到对方的FIN请求),做对应操作,
11. 若在3张链表里都未找到匹配的pcb,则调用tcp_rst函数向源主机发送一个TCP复位数据包
源码:
void
tcp_input(struct pbuf *p, struct netif *inp)
{
struct tcp_pcb *pcb, *prev;
struct tcp_pcb_listen *lpcb;
#if SO_REUSE
struct tcp_pcb *lpcb_prev = NULL;
struct tcp_pcb_listen *lpcb_any = NULL;
#endif /* SO_REUSE */
u8_t hdrlen_bytes;
err_t err;
LWIP_UNUSED_ARG(inp);
PERF_START;
TCP_STATS_INC(tcp.recv);
MIB2_STATS_INC(mib2.tcpinsegs);
/*获取TCP头,这边使用了一个全局变量tcphdr*/
tcphdr = (struct tcp_hdr *)p->payload;
#if TCP_INPUT_DEBUG
tcp_debug_print(tcphdr);
#endif
/* 判断数据包长度是否小于TCP报头长度
* (检查报文是否有效) */
if (p->len < TCP_HLEN) {
/* 若报文无效,丢弃 */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded
", p->tot_len));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
/* 若是多播包/广播包,丢弃
* (不处理多播/广播报文)*/
if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) ||
ip_addr_ismulticast(ip_current_dest_addr())) {
TCP_STATS_INC(tcp.proterr);
goto dropped;
}
/* (通过宏配置是否开启TCP校验) */
#if CHECKSUM_CHECK_TCP
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) {
/* TCP校验和验证 */
u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
ip_current_src_addr(), ip_current_dest_addr());
if (chksum != 0) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"
",
chksum));
tcp_debug_print(tcphdr);
TCP_STATS_INC(tcp.chkerr);
goto dropped;
}
}
#endif /* CHECKSUM_CHECK_TCP */
/* 获取TCP首部长度
* (若无其他选项,最小为20字节) */
hdrlen_bytes = TCPH_HDRLEN(tcphdr) * 4;
/* 若获取到的TCP报头小于20字节或者TCP报头长度大于报文总长度,为无效报文,丢弃 */
if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")
", (u16_t)hdrlen_bytes));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
/* 将有效载荷指针移动到pbuf中,这样它就指向了 TCP数据而不是TCP报头。 */
tcphdr_optlen = hdrlen_bytes - TCP_HLEN; //tcphdr_optlen = TCP报头选项长度 (TCP报头总长度 - TCP标准报头20字节)
tcphdr_opt2 = NULL; //tcphdr_opt2 指向NULL
/* 判断TCP报头是否在一个pbuf中 */
if (p->len >= hdrlen_bytes) {
/* 若TCP报头在第一个pbuf中 */
tcphdr_opt1len = tcphdr_optlen; //tcphdr_opt1len = TCP报头选项长度
pbuf_header(p, -(s16_t)hdrlen_bytes); //将指针移动到pbuf数据中
} else {
u16_t opt2len;
/* 若TCP报头在多个pbuf中 */
LWIP_ASSERT("p->next != NULL", p->next != NULL);
/* 去除TCP标准报头(不会失败) */
pbuf_header(p, -TCP_HLEN);
/* 去掉第一个pbuf的长度 */
tcphdr_opt1len = p->len;
opt2len = tcphdr_optlen - tcphdr_opt1len;
/* options continue in the next pbuf: set p to zero length and hide the
options in the next pbuf (adjusting p->tot_len) */
pbuf_header(p, -(s16_t)tcphdr_opt1len);
/* 检查TCP报头选项部分是否在第二个pbuf中 */
if (opt2len > p->next->len) {
/* 丢弃过短的报文 */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)
", p->next->len));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
/* 记住指向TCP报头选项的第二部分的指针
* (有部分选项在第二个pbuf中,记录TCP报头选项的开始部分) */
tcphdr_opt2 = (u8_t*)p->next->payload;
/* 将第二个pbuf的指针指向pbuf 的数据部分 */
pbuf_header(p->next, -(s16_t)opt2len);
p->tot_len -= opt2len;
LWIP_ASSERT("p->len == 0", p->len == 0);
LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len);
}
/*将TCP报头中的数据由网络字节序转换为主机字节序 */
tcphdr->src = lwip_ntohs(tcphdr->src);
tcphdr->dest = lwip_ntohs(tcphdr->dest);
seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
/* 6位标志位 */
flags = TCPH_FLAGS(tcphdr);
/* TCP数据包中数据的总长度,对于有FIN或SYN标志的数据包,该长度要加1 */
tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
/* Demultiplex an incoming segment. First, we check if it is destined
* for an active connection.
* (以下就是对接收到的数据包进行分类处理,也就是寻找合适的接口,根据IP,port) */
prev = NULL;
/* 遍历tcp_active_pcbs链表 (活动状态下的链表)*/
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
/* 若端口号匹配,并且IP地址匹配 (就把这个移到pcb链表的前面,下次查找更快) 可以借鉴*/
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
/* 把这个PCB移到列表的前面,这样后面的 查找速度更快(我们在TCP段中使用了位置 移位) */
LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
if (prev != NULL) {
prev->next = pcb->next;
pcb->next = tcp_active_pcbs;
tcp_active_pcbs = pcb;
} else {
TCP_STATS_INC(tcp.cachehit);
}
LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
break;
}
prev = pcb;
}
/* 若不在tcp_active_pcbs链表中,遍历tcp_tw_pcbs和tcp_listen_pcbs链表 (等待状态下的链表) */
if (pcb == NULL) {
/* 如果它没有连接到一个活动连接,我们检查连接 时候的状态。*/
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
/* 若端口&IP匹配 */
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
/* 我们不太关心把PCB移到前面 因为我们不太可能接受这个列表 有许多段时间等待连接
* (在此处不需要把该链表移到前面,因为需要等待连接) */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.
"));
/* 等待状态下的pcb进入tcp_timewait_input */
tcp_timewait_input(pcb);
pbuf_free(p);
return;
}
}
/* 最后,如果我们仍然没有匹配,我们会检查所有的PCBs 正在收听即将到来的连接。(监听状态下的链表) */
prev = NULL;
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
/* 匹配端口号,否则是指向本地的端口 */
if (lpcb->local_port == tcphdr->dest) {
/* 本地IP是否设置的任意IP地址(这边是类型标志?) */
if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) {
/* 找到任何类型(ipv4-ipv6)匹配 */
#if SO_REUSE
lpcb_any = lpcb;
lpcb_prev = prev;
#else /* SO_REUSE */
break;
#endif /* SO_REUSE */
} else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) {
/* 不是任意类型的IP地址,匹配IP */
if (ip_addr_cmp(&lpcb->local_ip, ip_current_dest_addr())) {
/* 找到精确匹配,break */
break;
} else if (ip_addr_isany(&lpcb->local_ip)) {
/* 本地IP是否设置的任意IP地址,找到任意IP地址 */
#if SO_REUSE
lpcb_any = lpcb;
lpcb_prev = prev;
#else /* SO_REUSE */
break;
#endif /* SO_REUSE */
}
}
}
prev = (struct tcp_pcb *)lpcb;
}
#if SO_REUSE
/* 首先尝试任意的本地IP
* (本地IP地址为任意IP的pcb) */
if (lpcb == NULL) {
/* 如果没有找到特定的本地IP,则只传递给任何一个 */
lpcb = lpcb_any;
prev = lpcb_prev;
}
#endif /* SO_REUSE */
/* 如果是精确匹配的本地IP地址 */
if (lpcb != NULL) {
/* 把这个PCB移到列表的前面,这样后面的 查找速度更快. */
if (prev != NULL) {
((struct tcp_pcb_listen *)prev)->next = lpcb->next;
/* our successor is the remainder of the listening list */
lpcb->next = tcp_listen_pcbs.listen_pcbs;
/* put this listening pcb at the head of the listening list */
tcp_listen_pcbs.listen_pcbs = lpcb;
} else {
TCP_STATS_INC(tcp.cachehit);
}
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.
"));
/* 监听链表下的pcb进入tcp_listen_input */
tcp_listen_input(lpcb);
pbuf_free(p);
return;
}
}
#if TCP_INPUT_DEBUG
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"));
#endif /* TCP_INPUT_DEBUG */
/* 若在tcp_active_pcbs链表中找到了,则经过处理后进入tcp_process */
if (pcb != NULL) {
/* The incoming segment belongs to a connection. */
#if TCP_INPUT_DEBUG
tcp_debug_print_state(pcb->state);
#endif /* TCP_INPUT_DEBUG */
/* 建立一个tcpseg结构 */
inseg.next = NULL; // 关闭报文段队列功能
inseg.len = p->tot_len; // 设置该报文段的数据长度
inseg.p = p; // 设置报文段数据链表头指针
inseg.tcphdr = tcphdr; // 报文段的TCP头
recv_data = NULL; // 数据接收结果被保存在该全局变量,然后往上层提交
recv_flags = 0; // tcp_process执行完后的结果(控制块的状态变迁)将会被保存在该全局变量,首先在这里被清0
recv_acked = 0;
if (flags & TCP_PSH) {
p->flags |= PBUF_FLAG_PUSH;
}
/* If there is data which was previously "refused" by upper layer
* (tcp_pcb的refused_data指针上是否还记录有尚未往上层递交的数据)*/
if (pcb->refused_data != NULL) {
/* 有的话回调用户recv函数接收未递交的数据
* 判断处理recv函数的处理结果,成功refused_data指针清空,继续往下执行tcp_process
* 失败意味着tcp_pcb都被占用满,丢弃接收包不再处理,直接返回*/
if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
((pcb->refused_data != NULL) && (tcplen > 0))) {
/* pcb has been aborted or refused data is still refused and the new
segment contains data */
/* 若接收窗口声明为0,调动tcp_send_empty_ack,下次看 */
if (pcb->rcv_ann_wnd == 0) {
/* this is a zero-window probe, we respond to it with current RCV.NXT
and drop the data segment */
tcp_send_empty_ack(pcb);
}
TCP_STATS_INC(tcp.drop);
MIB2_STATS_INC(mib2.tcpinerrs);
goto aborted;
}
}
tcp_input_pcb = pcb; // 记录处理当前报文的控制块
/* 这里就是进入tcp_process处理接收包环节了(解析下次看),该函数实现了TCP状态转换功能 */
err = tcp_process(pcb);
/* (若返回值为ERR_ABRT,说明控制块已经被完全删除(tcp_abort()),什么也不需要做)
* 返回值不为ERR_ABRT时,判断报文处理的3种结果 */
if (err != ERR_ABRT) {
/* 接收到对方的复位报文 (第一种情况) */
if (recv_flags & TF_RESET) {
/* 回调用户的errf函数 */
TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
/* 删除控制块 */
tcp_pcb_remove(&tcp_active_pcbs, pcb);
/* 释放控制块空间 */
memp_free(MEMP_TCP_PCB, pcb);
} else {
err = ERR_OK;
/* If the application has registered a "sent" function to be
called when new send buffer space is available, we call it
now.
(如果应用程序已经注册了一个“发送”函数当新的发送缓冲区空间可用时,我们调用它现在)*/
if (recv_acked > 0) {
u16_t acked16;
#if LWIP_WND_SCALE
/* 被重新调用的是u32t,但是发送的回调只需要u16t, 所以我们可能要多次调用它。 */
u32_t acked = recv_acked;
while (acked > 0) {
acked16 = (u16_t)LWIP_MIN(acked, 0xffffu);
acked -= acked16;
#else
{
acked16 = recv_acked;
#endif
TCP_EVENT_SENT(pcb, (u16_t)acked16, err);
if (err == ERR_ABRT) {
goto aborted;
}
}
recv_acked = 0;
}
/* 双方连接成功断开(第二种情况) */
if (recv_flags & TF_CLOSED) {
/* 连接已经关闭,我们将重新分配 PCB */
if (!(pcb->flags & TF_RXCLOSED)) {
/* Connection closed although the application has only shut down the
tx side: call the PCB's err callback and indicate the closure to
ensure the application doesn't continue using the PCB.
(连接关闭尽管应用程序只关闭了 tx端:调用PCB的错误回调,并指示关闭 确保应用程序不会继续使用PCB。) */
TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);
}
tcp_pcb_remove(&tcp_active_pcbs, pcb);
memp_free(MEMP_TCP_PCB, pcb);
goto aborted;
}
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
while (recv_data != NULL) {
struct pbuf *rest = NULL;
pbuf_split_64k(recv_data, &rest);
#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
if (recv_data != NULL) {
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
if (pcb->flags & TF_RXCLOSED) {
/* 如果本地TCP控制块已经处于TF_RXCLOSED状态,则后续接收到的数据都作废 */
pbuf_free(recv_data);
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_free(rest);
}
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
tcp_abort(pcb);
goto aborted;
}
/* 通知应用程序已接收到数据。 */
TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
if (err == ERR_ABRT) {
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_free(rest);
}
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
goto aborted;
}
/* 如果上层无法接收到这些数据,就存储它 */
if (err != ERR_OK) {
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_cat(recv_data, rest);
}
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
pcb->refused_data = recv_data;
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is "full"
"));
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
break;
} else {
/* Upper layer received the data, go on with the rest if > 64K */
recv_data = rest;
#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
}
}
/* 如果收到对方的FIN请求 */
if (recv_flags & TF_GOT_FIN) {
if (pcb->refused_data != NULL) {
/* 如果我们拒绝了数据,就推迟这个时间。 */
pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
} else {
/* 纠正接收窗口 */
if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
pcb->rcv_wnd++;
}
/* 用一个NULL指针回调用户的recv函数,通过这种方式用户程序可以知道对方的关闭请求 */
TCP_EVENT_CLOSED(pcb, err);
if (err == ERR_ABRT) {
goto aborted;
}
}
}
tcp_input_pcb = NULL; //当前报文到此处理完毕,清空当前报文的控制块
/* Try to send something out. */
tcp_output(pcb); //输出报文
#if TCP_INPUT_DEBUG
#if TCP_DEBUG
tcp_debug_print_state(pcb->state);
#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */
}
}
/* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
Below this line, 'pcb' may not be dereferenced! */
aborted:
tcp_input_pcb = NULL;
recv_data = NULL;
/* give up our reference to inseg.p */
if (inseg.p != NULL)
{
pbuf_free(inseg.p);
inseg.p = NULL;
}
} else {
/* 如果在3张链表里都未找到匹配的pcb,则调用tcp_rst向源主机发送一个TCP复位数据包 */
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.
"));
if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
TCP_STATS_INC(tcp.proterr);
TCP_STATS_INC(tcp.drop);
tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
}
pbuf_free(p);
}
LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
PERF_STOP("tcp_input");
return;
dropped:
TCP_STATS_INC(tcp.drop);
MIB2_STATS_INC(mib2.tcpinerrs);
pbuf_free(p);
}