tcp_in.c的tcp_process函数

LWIP中tcp_in.c下tcp_process函数。

学习《嵌入式网络那些事lwip协议 深度剖析与实战演练》过程中
对tcp_process这个函数的理解

在这里插入图片描述

连接建立过程中的握手分为三个报文分别为①② ③
连接断开过程中的握手分为④ ⑤ ⑥ ⑦
此外还有双方同时关闭的情况需要考虑
在这里插入图片描述
函数前面部分属于对输入的报文进行判断,是否合法,是否为存在RST标志置位,并判断收到的握手包为超时重发

static err_t
tcp_process(struct tcp_pcb *pcb)
{
 struct tcp_seg *rseg;
 u8_t acceptable = 0;
 err_t err;

 err = ERR_OK;

 /* Process incoming RST segments. */
 if (flags & TCP_RST) {
   /* First, determine if the reset is acceptable. */
   if (pcb->state == SYN_SENT) {
     if (ackno == pcb->snd_nxt) {
       acceptable = 1;
     }
   } else {
     if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
                         pcb->rcv_nxt+pcb->rcv_wnd)) {
       acceptable = 1;
     }
   }

   if (acceptable) {
     LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
     LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
     recv_flags |= TF_RESET;
     pcb->flags &= ~TF_ACK_DELAY;
     return ERR_RST;
   } else {
     LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
      seqno, pcb->rcv_nxt));
     LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
      seqno, pcb->rcv_nxt));
     return ERR_OK;
   }
 }

 if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
   /* Cope with new connection attempt after remote end crashed */
   tcp_ack_now(pcb);
   return ERR_OK;
 }

 if ((pcb->flags & TF_RXCLOSED) == 0) {
   /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
   pcb->tmr = tcp_ticks;
 }
 pcb->keep_cnt_sent = 0;

 tcp_parseopt(pcb);

switch (pcb->state)

下面是实现TCP状态机的具体部分

  case SYN_SENT:
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
    /* received SYN ACK with expected sequence number? */
    if ((flags & TCP_ACK) && (flags & TCP_SYN)
        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
      pcb->snd_buf++;
      pcb->rcv_nxt = seqno + 1;
      pcb->rcv_ann_right_edge = pcb->rcv_nxt;
      pcb->lastack = ackno;
      pcb->snd_wnd = tcphdr->wnd;
      pcb->snd_wnd_max = tcphdr->wnd;
      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
      pcb->state = ESTABLISHED;

#if TCP_CALCULATE_EFF_SEND_MSS
      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */

      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
       * but for the default value of pcb->mss) */
      pcb->ssthresh = pcb->mss * 10;

      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
      --pcb->snd_queuelen;
      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
      rseg = pcb->unacked;
      pcb->unacked = rseg->next;
      tcp_seg_free(rseg);

      /* If there's nothing left to acknowledge, stop the retransmit
         timer, otherwise reset it to start again */
      if(pcb->unacked == NULL)
        pcb->rtime = -1;
      else {
        pcb->rtime = 0;
        pcb->nrtx = 0;
      }

      /* Call the user specified function to call when sucessfully
       * connected. */
      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
      if (err == ERR_ABRT) {
        return ERR_ABRT;
      }
      tcp_ack_now(pcb);
    }
    /* received ACK? possibly a half-open connection */
    else if (flags & TCP_ACK) {
      /* send a RST to bring the other side in a non-synchronized state. */
      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
        tcphdr->dest, tcphdr->src);
    }
    break;

SYN_SENT (此状态是客户端发送完SYN握手请求后的状态,等待SYN+ACK)
一、如果收到②SYN+ACK,且对确认序号ackno进行校验,符合预期。设置出口参数,
控制块进入ESTABLISHED状态,对控制块中unacked进行更新。调用connected函数(回调用tcp_enqueue_flags将握手报文段放到TCP控制块的unsent队列上。调用tcp_output(会调用tcp_output_segment判断报文是否在允许的发送窗口内,并将报文段从unsent转移到unacked队列上))发送ACK,结束三次握手。
二、如果收到的是ACK,返回一个RST

case SYN_RCVD:
    if (flags & TCP_ACK) {
      /* expected ACK number? */
      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
        u16_t old_cwnd;
        pcb->state = ESTABLISHED;
        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API
        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
#endif
        /* Call the accept function. */
        TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
        if (err != ERR_OK) {
          /* If the accept function returns with an error, we abort
           * the connection. */
          /* Already aborted? */
          if (err != ERR_ABRT) {
            tcp_abort(pcb);
          }
          return ERR_ABRT;
        }
        old_cwnd = pcb->cwnd;
        /* If there was any data contained within this ACK,
         * we'd better pass it on to the application as well. */
        tcp_receive(pcb);

        /* Prevent ACK for SYN to generate a sent event */
        if (pcb->acked != 0) {
          pcb->acked--;
        }

        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);

        if (recv_flags & TF_GOT_FIN) {
          tcp_ack_now(pcb);
          pcb->state = CLOSE_WAIT;
        }
      } else {
        /* incorrect ACK number, send RST */
        tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
                tcphdr->dest, tcphdr->src);
      }
    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
      /* Looks like another copy of the SYN - retransmit our SYN-ACK */
      tcp_rexmit(pcb);
    }
    break;

SYN_RCVD(此状态是服务器发送完SYN+ACK后的状态,等待ACK)
一、 如果收到③ACK,且确认序号ackno合法
控制块进入ESTABLISHED状态,开始接收数据。调用用户的accept函数(调用出错就关闭当前连接,返回出错),保持旧的阻塞窗口,调用函数tcp_receive处理报文中的数据,同时当本地的unacked被报文中的ACK确认时,重新设置阻塞窗口。
数据传输过程中,当接收到④FIN握手,响应对方的FIN握手,并回复一个ACK⑤,并且控制块进入CLOSE_WAIT状态。
二、 如果收到①SYN握手报文,说明自己发的②SYN+ACK丢失,重传②SYN+ACK

  case CLOSE_WAIT:
    /* FALLTHROUGH */

CLOSE_WAIT (此状态是服务器处于半关闭状态)

 case ESTABLISHED:
    tcp_receive(pcb);
    if (recv_flags & TF_GOT_FIN) { /* passive close */
      tcp_ack_now(pcb);
      pcb->state = CLOSE_WAIT;
    }
    break;

ESTABLISHED (握手建立后,传输数据的过程中)
一、接收到④FIN握手,响应对方的FIN握手,并回复一个ACK⑤,并且控制块进入CLOSE_WAIT状态。

  case FIN_WAIT_1:
    tcp_receive(pcb);
    if (recv_flags & TF_GOT_FIN) {
      if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
        LWIP_DEBUGF(TCP_DEBUG,
          ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
        tcp_ack_now(pcb);
        tcp_pcb_purge(pcb);
        TCP_RMV_ACTIVE(pcb);
        pcb->state = TIME_WAIT;
        TCP_REG(&tcp_tw_pcbs, pcb);
      } else {
        tcp_ack_now(pcb);
        pcb->state = CLOSING;
      }
    } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
      pcb->state = FIN_WAIT_2;
    }
    break;

FIN_WAIT1 (上层应用调用tcp_close函数,并发送FIN后的状态)
继续调用tcp_receive函数处理报文中的数据
一、 收到一个⑥FIN握手,且包含有效ACK。则响应此FIN握手,回复一个ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中
收到的这个FIN握手也可能是④,即双方同时执行关闭操作,则此时返回ACK,进入CLOSING状态
二、 收到一个只收到⑤ACK,则进入FIN_WAIT2状态

case FIN_WAIT_2:
    tcp_receive(pcb);
    if (recv_flags & TF_GOT_FIN) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_ack_now(pcb);
      tcp_pcb_purge(pcb);
      TCP_RMV_ACTIVE(pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;

FIN_WAIT2 (主动关闭,发送FIN握手且接受到ACK后的状态)
继续调用tcp_receive函数处理报文中的数据
一、收到一个⑥FIN握手,则响应此FIN握手,回复一个ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中

case CLOSING:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_pcb_purge(pcb);
      TCP_RMV_ACTIVE(pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;

CLOSING (双方同时执行主动关闭后的状态)
继续调用tcp_receive函数处理报文中的数据
当收到有效ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中

  case LAST_ACK:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
      recv_flags |= TF_CLOSED;
    }
    break;
  default:
    break;

LAST_ACK
继续调用tcp_receive函数处理报文中的数据
当收到有效ACK,设置recv_flags为TF_CLOSED,返回tcp_input对该控制块进行释放和清除工作。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值