文章目录
一个有着接近900行代码的函数,庞大的函数,需要我们有庖丁解牛的耐心
一,简介
tcp_receive(struct tcp_pcb *pcb)
是tcp数据输入的末端。在之前的tcp_process()函数中,对输入tcp报文已经确定了其对应的tcp控制块。
该实现了tcp通信中的滑动窗口,快速重传与恢复,拥塞控制算法,对无序报文的处理等。并将有效数据传递给应用层。
这个函数必须分成以下几个步骤来解。
二,代码流程
1,更新发送窗口
由于接收到对方的报文中会有ackno确认序号,所以根据ackno和通告窗口anno_wnd可更新本地接收窗口的大小。当遇到0窗口通告时,还要开启坚持定时器。
//本地发送窗口的右边界=上次窗口更新时的确认序号+当前发送窗口大小
right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
//snd_wl1 = last seqno
//snd_wl2 = last ackno
//更新窗口三个条件:
if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || //对方有发送新的数据
(pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || //对方无发送新的数据但接收到我方发出的数据
(pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) {
//无收到我方的新数据且,通告对方接收窗口大于我方发送窗口
pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd); //根据通告窗口设置发送窗口
//限制发送窗口
if (pcb->snd_wnd_max < pcb->snd_wnd) {
pcb->snd_wnd_max = pcb->snd_wnd;
}
//更新发送窗口的参数
pcb->snd_wl1 = seqno;
pcb->snd_wl2 = ackno;
//如果发送窗口为0,即通告窗口是0,需要开启坚持定时器
if (pcb->snd_wnd == 0) {
if (pcb->persist_backoff == 0) {
/* start persist timer */
pcb->persist_cnt = 0;
pcb->persist_backoff = 1;
}
} else if (pcb->persist_backoff > 0) {
//否则停止坚持定时器
pcb->persist_backoff = 0;
}
2,快速重传与恢复
通过检查ackno与上一次更新的ackno是否一致等条件,判断是否需要开启快速重传。退出快重传后,进入快恢复,更新重传参数。
//!快速重传实现原理(当重复确认超过三次则认为需要启动快速重传)
//ackno<lastack,没有确认新数据
if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
//
/* Clause 2 */
if (tcplen == 0) {
/* Clause 3 */
if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
//通告窗口大小没变
/* Clause 4 */
if (pcb->rtime >= 0) {
//重传定时器开启,有数据等待确认
/* Clause 5 */
if (pcb->lastack == ackno) {
//确认号等于最高确认号
//当这里说明该ack是一个空的ack,在重复的确认某一个序号,而发送方同时有数据未被ack,说明之前报文可能丢失
found_dupack = 1; //出现了重复的ack
//dupacks变量加1同时防止value overflows
if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
++pcb->dupacks;
}
//如果重复确认超过3次,可能是报文丢失了
if (pcb->dupacks > 3) {
if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
pcb->cwnd += pcb->mss;//拥塞窗口增加一个报文段大小
}
} else if (pcb->dupacks == 3) {
//执行快速重传丢失报文,初始化cwnd和ssthresh
tcp_rexmit_fast(pcb);
}
}
}
}
}
/*-------------------------------快速重传算法------------------------------------*/
//不是重复ack
if (!found_dupack) {
pcb->dupacks = 0;
}
} else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
//ackno在last_ack和snd_nxt之间,正确
//!若tcp处于快速重传,则退出,设置cwnd为ssthresh,这就是快恢复算法
if (pcb->flags & TF_INFR) {
pcb->flags &= ~TF_INFR;
pcb->cwnd = pcb->ssthresh;
}
//正确的接收到数据,更新重传的参数
pcb->nrtx = 0;
pcb->rto = (pcb->sa >> 3) + pcb->sv;
pcb->dupacks = 0;
pcb->lastack = ackno;
3,拥塞控制算法
根据cwnd与ssthresh的大小,调节cwnd的大小。
/*---------------------------------------- 拥塞控制算法 ------------------------------------------*/
if (pcb->state >= ESTABLISHED) {
//!慢启动算法cwnd<ssthresh cwnd增加一个报文段大小
if (pcb->cwnd < pcb->ssthresh) {
if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
pcb->cwnd += pcb->mss;
}
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
} else {
//!拥塞避免算法
tcpwnd_size_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
if (new_cwnd > pcb->cwnd) {
pcb->cwnd = new_cwnd;
}
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
}
}
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
ackno,
pcb->unacked != NULL?
lwip_ntohl(pcb->unacked->tcphdr->seqno): 0,
pcb->unacked != NULL?
lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
4,更新unacked队列
由于收到了新的acked,unacked队列就要更新,删除掉被ack的报文,检查是否还有未ack的报文,决定是否开启重传定时器。
//将unacked队列中已经确认的报文删除
while (pcb->unacked != NULL && //有未确认的报文
TCP_SEQ_LEQ(lwip_ntohl(pcb->unacked->tcphdr->seqno) + //unacked报文尾的序号小于输入报文确认序号?
TCP_TCPLEN(pcb->unacked), ackno)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
lwip_ntohl(pcb->unacked->tcphdr->seqno),
lwip_ntohl(pcb->unacked->tcphdr->seqno) +
TCP_TCPLEN(pcb->unacked)));
//找到被确认的报文:
next = pcb->unacked;
pcb->unacked = pcb->unacked->next; //下一个检查的unacked报文
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen));
LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
pcb->snd_queuelen -= pbuf_clen(next->p); //因为lwip中未确认队列中的报文被放在了unsent队列中
recv_acked += next->len; //确认数据增加next->len
tcp_seg_free(next); //删除该报文
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unacked)\n", (tcpwnd_size_t)pcb->snd_queuelen));
if (pcb->snd_queuelen != 0) {
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
pcb->unsent != NULL);
}
}
//若没有等待确认的报文,则关闭重传定时
if (pcb->unacked == NULL) {
pcb->rtime = -1;
} else {
pcb->rtime = 0;
}
pcb->polltmr = 0;
5,更新unsent队列
由于lwip将unacked队列中的部分报文放到unsent队列的首部,所以也要检查unsent对列中被确认的报文。
//由于lwip将超时且需要重传的报文放到了unsent队列,所以需要在unsent中检查\
是否有的报文已经被确认,代码与以上相似
while (pcb->unsent != NULL &&
TCP_SEQ_BETWEEN(ackno, lwip_ntohl(pcb->unsent->tcphdr->seqno) +
TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
lwip_ntohl(pcb->unsent->tcphdr->seqno), lwip_ntohl(pcb->unsent->tcphdr->seqno) +
TCP_TCPLEN(pcb->unsent)));
//找到已经被确认的报文:
next = pcb->unsent;
pcb->unsent = pcb->unsent->next;
if (pcb->unsent == NULL) {
pcb->unsent_oversize = 0;
}
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen));
LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
pcb->snd_queuelen -= pbuf_clen(next->p);
recv_acked += next->len;
tcp_seg_free(next);
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unsent)\n", (tcpwnd_size_t)pcb->snd_queuelen));
if (pcb->snd_queuelen != 0) {
LWIP_ASSERT("tcp_receive: valid queue length",
pcb->unacked != NULL || pcb->unsent != NULL);
}
}
6,rtt测试
若接收的ackno大于用于rtt测试的序号,则可进行rto的计算。
//TODO pcb的rtt测试开启,且进行rtt测试的序号小于确认序号则可用计算rto,计算rto超时重传时间
if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
m = (s16_t)(tcp_ticks - pcb->rttest);
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
m, (u16_t)(m * TCP_SLOW_INTERVAL)));
m = m - (pcb->sa >> 3);
pcb->sa += m;
if (m < 0) {
m = -m;
}
m = m - (pcb->sv >> 2);
pcb->sv += m;
pcb->rto = (pcb->sa >> 3) + pcb->sv;
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
pcb->rttest = 0; //关闭rtt测量
}
}
7,滑动窗口
根据接收到的数据序号在接收窗口的不同位置,对不同位置的数据有不一样的裁剪:
a,接收的数据一部分已经接受过,另一部分是新数据
此时要把已经接收的部分丢弃,只保留新数据。
//若期待接收的数据处于接收到的数据的中间\
/*------接收到的数据既有已经接受过的数据,也有新的数据,将输入数据从rcv_nxt截断,取rcv_nxt及其之后的新数据-----*/
if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
struct pbuf *p = inseg.p; //p是输入报文第一个pbuf
off = pcb->rcv_nxt - seqno; //p中将要舍弃的数据的偏移 即payload向后移动的距离
LWIP_ASSERT("inseg.p != NULL", inseg.p);
LWIP_ASSERT("insane offset!", (off < 0x7fff));
//若输入报文的第一个pbuf里的数据都是应该舍弃的数据
if (inseg.p->len < off) {
LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
new_tot_len = (u16_t)(inseg.p->tot_len - off); //截取后的pbuf总长度
//则找到recv_nxt所在那个pbuf,在这个pbuf之前的buffer都要被释放
while (p->len < off) {
off -= p->len;
//!注意下面的注释,inseg.p->tot_len -= p->len;被注释是因为这些pbuf都将被释放
/* KJM following line changed (with addition of new_tot_len var)
to fix bug #9076
inseg.p->tot_len -= p->len; */
p->tot_len = new_tot_len;
p->len = 0;
p = p->next;
}
//移动输入报文的pbuf的payload指针到其recv_nxt所在的地址
if (pbuf_header(p, (s16_t)-off)) {
/* Do we need to cope with this failing? Assert for now */
LWIP_ASSERT("pbuf_header failed", 0);
}
} else {
if (pbuf_header(inseg.p, (s16_t)-off)) {
/* Do we need to cope with this failing? Assert for now */
LWIP_ASSERT("pbuf_header failed", 0);
}
}
inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); //报文长度被减少了
inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; //tcp报文首部的序号页要改成截断后的序号
}
/*------接收到的数据既有已经接受过的数据,也有新的数据,将输入数据从rcv_nxt截断,取rcv_nxt及其之后的新数据-----*/
else {
//整个报文都是已接收的数据,回复ack即可
if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
/* the whole segment is < rcv_nxt */
/* must be a duplicate of a packet that has already been correctly handled */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
tcp_ack_now(pcb);
}
}
b,数据正好在接收窗口左边界
当数据长度超过窗口大小,则将超出部分丢弃。将报文插入osseq队列,若报文与osseq队列的内容重复,则将报文重复部分丢弃。将osseq队列的有序数据赋值给recv_data指针,上层应用通告该指针读取接收的数据。
if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
//第一种情况:接收序号正好是期待接收的,说明数据与上次是连续的
if (pcb->rcv_nxt == seqno) {
tcplen = TCP_TCPLEN(&inseg);
//报文内容大于接收窗口,将超过接收窗口部分的内容截掉
if (tcplen > pcb->rcv_wnd) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: other end overran receive window"
"seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
//!若输入报文中有fin,必须从报头中删除FIN,因为我们要修剪输入报文的数据
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
/* Must remove the FIN from the header as we're trimming
* that byte of sequence-space from the packet */
TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
}
/* Adjust length of segment to fit in the window. */
TCPWND_CHECK16(pcb->rcv_wnd);
inseg.len = (u16_t)pcb->rcv_wnd; //修剪后的报文大小就是窗口大小
//若有syn标志,则报文长度-1
if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
inseg.len -= 1;
}
pbuf_realloc(inseg.p, inseg.len); //给报文的pbuf重新分配内存
tcplen = TCP_TCPLEN(&inseg); //更新修剪后的报文大小
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
(seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
}
/*---------------------------------------根据接收窗口修建接收的报文长度-----------------------------------*/
//!现在接收的数据是正常排序的,先放到osseq队列首部,具体情况见p397第九点
if (pcb->ooseq != NULL) {
//fin标志说明后续无数据来了,该报文段后的报文都要删除
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: received in-order FIN, binning ooseq queue\n"));
while (pcb->ooseq != NULL) {
struct tcp_seg *old_ooseq = pcb->ooseq;
pcb->ooseq = pcb->ooseq->next;
tcp_seg_free(old_ooseq);
}
} else {
/*--------------------根据输入报文,修改osseq中的数据,修剪输入数据------------------------*/
next = pcb->ooseq;
//!因为接收的数据与上一次接收的数据是连续的,以下情况的报文段是要删除
while (next &&
TCP_SEQ_GEQ(seqno + tcplen,
next->tcphdr->seqno + next->len)) {
/* inseg cannot have FIN here (already processed above) */
/* 如果这些即将被删除的报文段带FIN标志且输入报文段不带SYN标志 */
if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
(TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); //在输入报文段的TCP头中添加FIN标志
tcplen = TCP_TCPLEN(&inseg);
}
prev = next;
next = next->next;
tcp_seg_free(prev);
}
//!此时seqno在osseq是最小的序号
/* Now trim right side of inseg if it overlaps with the first
* segment on ooseq */
//如果输入报文段与osseq中出现部分重叠,则删除输入报文段中重叠部分
if (next &&
TCP_SEQ_GT(seqno + tcplen,
next->tcphdr->seqno)) {
/* inseg cannot have FIN here (already processed above) */
inseg.len = (u16_t)(next->tcphdr->seqno - seqno); //得到输入报文中不重叠部分的长度
if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
inseg.len -= 1;
}
pbuf_realloc(inseg.p, inseg.len); //给输入报文段调整长度
tcplen = TCP_TCPLEN(&inseg); //再次更新报文长度
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
(seqno + tcplen) == next->tcphdr->seqno);
}
pcb->ooseq = next;
}
}
/*--------------------根据输入报文,修改osseq中的数据,修剪输入数据------------------------*/
pcb->rcv_nxt = seqno + tcplen; //更新下一个期待收到的序号
/* Update the receiver's (our) window. */
LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
pcb->rcv_wnd -= tcplen; //本地接收窗口变小
tcp_update_rcv_ann_wnd(pcb); //更新通告窗口
//将输入报文段的数据上传给应用层
if (inseg.p->tot_len > 0) {
recv_data = inseg.p;
/* Since this pbuf now is the responsibility of the
application, we delete our reference to it so that we won't
(mistakingly) deallocate it. */
inseg.p = NULL;
}
//如果报文段有fin标志
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
recv_flags |= TF_GOT_FIN; //给接收处理结果置位
}
/*----------------------------------ooseq有序数据发送到应用层------------------------------------*/
//(通过比较ooseq队列中报文段的seqno和当前TCP控制块中保存的rcv_nxt来判定该报文段是否有序)
while (pcb->ooseq != NULL &&
pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
cseg = pcb->ooseq;
seqno = pcb->ooseq->tcphdr->seqno;
pcb->rcv_nxt += TCP_TCPLEN(cseg);
LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
pcb->rcv_wnd >= TCP_TCPLEN(cseg));
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
tcp_update_rcv_ann_wnd(pcb);
//如果该报文段有数据,则将数据连接到recv_data上,同时将报文的pbuf指针赋值为null
if (cseg->p->tot_len > 0) {
/* Chain this pbuf onto the pbuf that we will pass to
the application. */
/* With window scaling, this can overflow recv_data->tot_len, but
that's not a problem since we explicitly fix that before passing
recv_data to the application. */
if (recv_data) {
pbuf_cat(recv_data, cseg->p); //将有序的pbuf拼接起来
} else {
recv_data = cseg->p;
}
cseg->p = NULL;
}
//若报文段中有fin标志,则设置处理结果,修改pcb的状态
if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
recv_flags |= TF_GOT_FIN;
if (pcb->state == ESTABLISHED) {
/* force passive close or we can move to active close */
pcb->state = CLOSE_WAIT;
}
}
pcb->ooseq = cseg->next; //处理下一个ooseq报文段
tcp_seg_free(cseg); //释放被拼接的报文段
}
c,数据不在左边界
说明接收的数据不是有序到达的。则将该报文插入osseq队列。接下来的代码就是如何将报文正确的插入osseq队列。
//!seqno!=recv->nxt 说明收到无序报文,将报文插入合适的位置
tcp_send_empty_ack(pcb);
if (pcb->ooseq == NULL) {
pcb->ooseq = tcp_seg_copy(&inseg);
} else {
/*-----------------------------------处理osseq无序报文-------------------------------------*/
prev = NULL;
//遍历ooseq队列
for (next = pcb->ooseq; next != NULL; next = next->next) {
//若输入的报文序号与osseq队列中的某个报文一样
if (seqno == next->tcphdr->seqno) {
//若输入的报文长度大。则将输入报文插入
if (inseg.len > next->len) {
cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
if (prev != NULL) {
prev->next = cseg;
} else {
pcb->ooseq = cseg;
}
tcp_oos_insert_segment(cseg, next); //替换next
}
break;
} else {
//输入报文段比原报文小,啥不用干了
break;
}
} else {
//输入报文段的序号在osseq队列中无
//next是第一个osseq报文段
if (prev == NULL) {
//输入报文序号小于第一个osseq报文,则将输入报文放到osseq首部
if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
pcb->ooseq = cseg;
tcp_oos_insert_segment(cseg, next); //替换next的位置
}
break;
}
} else {
//若输入序号在前一个报文与后一个报文之间,修剪前一个报文,删除下一个报文中重复的部分
if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
//前一个报文太长。修剪
if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
/* We need to trim the prev segment. */
prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
pbuf_realloc(prev->p, prev->len);
}
prev->next = cseg;
tcp_oos_insert_segment(cseg, next); //替换next
}
break;
}
}
//若next是最后一个报文,且输入报文的数据在next之后
if (next->next == NULL &&
TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
//若next报文有fin,说明其后不应该有数据,直接退出
if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
/* segment "next" already contains all data */
break;
}
next->next = tcp_seg_copy(&inseg); //输入报文插入最后
if (next->next != NULL) {
//如果next与输入报文有覆盖,则修改next报文的长度
if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
/* We need to trim the last segment. */
next->len = (u16_t)(seqno - next->tcphdr->seqno);
pbuf_realloc(next->p, next->len);
}
/* check if the remote side overruns our receive window */
//检查远程是不是超支我们的接收窗口
if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: other end overran receive window"
"seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
//!若输入报文有fin,则删除,因为我们正修改输入报文的大小来适应接收窗口
if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
/* Must remove the FIN from the header as we're trimming
* that byte of sequence-space from the packet */
TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
}
/* Adjust length of segment to fit in the window. */
next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno); //经过接收窗口筛选后的报文大小
pbuf_realloc(next->next->p, next->next->len); //重新分配报文内存
tcplen = TCP_TCPLEN(next->next);
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
(seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
}
}
break;
}
}
prev = next;
}
}