tcp_listen_input函数里的发送

本文详细介绍了TCP协议栈中tcp_listen_input函数的工作原理,特别是如何触发tcp_output进行数据发送。当输入处理代码调用tcp_listen_input时,会检查发送窗口大小、未发送队列及Nagle算法等因素,决定是否发送数据或ACK。通过对tcp_output函数的分析,揭示了TCP连接在监听状态下的数据传输机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tcp_listen_input函数里的发送

1.tcp_input 里调用tcp_listen_input

2.tcp_listen_input(struct tcp_pcb_listen *pcb)
{
、、、、、、、
最后有一句
return tcp_output(npcb);
}

3.tcp_output

/**

  • Find out what we can send and send it
  • @param pcb Protocol control block for the TCP connection to send data
  • @return ERR_OK if data has been sent or nothing to send
  •     another err_t on error
    

*/
err_t
*tcp_output(struct tcp_pcb pcb)
{
struct tcp_seg *seg, useg;
u32_t wnd, snd_nxt;
#if TCP_CWND_DEBUG
s16_t i = 0;
#endif /
TCP_CWND_DEBUG */

/* pcb->state LISTEN not allowed here */
LWIP_ASSERT(“don’t call tcp_output for listen-pcbs”,
pcb->state != LISTEN);

/* First, check if we are invoked by the TCP input processing
code. If so, we do not output anything. Instead, we rely on the
input processing code to call us when input processing is done
with. */
if (tcp_input_pcb == pcb) {
return ERR_OK;
}

wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);

seg = pcb->unsent;

/* If the TF_ACK_NOW flag is set and no data will be sent (either

  • because the ->unsent queue is empty or because the window does
  • not allow it), construct an empty ACK segment and send it.
  • If data is to be sent, we will just piggyback the ACK (see below).
    */
    if (pcb->flags & TF_ACK_NOW &&
    (seg == NULL ||
    ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
    return tcp_send_empty_ack(pcb);
    }

/* useg should point to last segment on unacked queue */
useg = pcb->unacked;
if (useg != NULL) {
for (; useg->next != NULL; useg = useg->next);
}

#if TCP_OUTPUT_DEBUG
if (seg == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, (“tcp_output: nothing to send (%p)\n”,
(void*)pcb->unsent));
}
#endif /* TCP_OUTPUT_DEBUG /
#if TCP_CWND_DEBUG
if (seg == NULL) {
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
", cwnd %“U16_F”, wnd %"U32_F
“, seg == NULL, ack %“U32_F”\n”,
pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
} else {
LWIP_DEBUGF(TCP_CWND_DEBUG,
("tcp_output: snd_wnd %“U16_F”, cwnd %“U16_F”, wnd %"U32_F
“, effwnd %“U32_F”, seq %“U32_F”, ack %“U32_F”\n”,
pcb->snd_wnd, pcb->cwnd, wnd,
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
ntohl(seg->tcphdr->seqno), pcb->lastack));
}
#endif /
TCP_CWND_DEBUG /
/
data available and window allows it to be sent? /
while (seg != NULL &&
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
LWIP_ASSERT(“RST not expected here!”,
(TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
/
Stop sending if the nagle algorithm would prevent it
* Don’t stop:
* - if tcp_write had a memory error before (prevent delayed ACK timeout) or
* - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
* either seg->next != NULL or pcb->unacked == NULL;
* RST is no sent using tcp_write/tcp_output.
/
if((tcp_do_output_nagle(pcb) == 0) &&
((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
break;
}
#if TCP_CWND_DEBUG
LWIP_DEBUGF(TCP_CWND_DEBUG, (“tcp_output: snd_wnd %“U16_F”, cwnd %“U16_F”, wnd %“U32_F”, effwnd %“U32_F”, seq %“U32_F”, ack %“U32_F”, i %“S16_F”\n”,
pcb->snd_wnd, pcb->cwnd, wnd,
ntohl(seg->tcphdr->seqno) + seg->len -
pcb->lastack,
ntohl(seg->tcphdr->seqno), pcb->lastack, i));
++i;
#endif /
TCP_CWND_DEBUG */

pcb->unsent = seg->next;

if (pcb->state != SYN_SENT) {
  TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
  pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}

tcp_output_segment(seg, pcb);
snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
  pcb->snd_nxt = snd_nxt;
}
/* put segment on unacknowledged list if length > 0 */
if (TCP_TCPLEN(seg) > 0) {
  seg->next = NULL;
  /* unacked list is empty? */
  if (pcb->unacked == NULL) {
    pcb->unacked = seg;
    useg = seg;
  /* unacked list is not empty? */
  } else {
    /* In the case of fast retransmit, the packet should not go to the tail
     * of the unacked queue, but rather somewhere before it. We need to check for
     * this case. -STJ Jul 27, 2004 */
    if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
      /* add segment to before tail of unacked list, keeping the list sorted */
      struct tcp_seg **cur_seg = &(pcb->unacked);
      while (*cur_seg &&
        TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
          cur_seg = &((*cur_seg)->next );
      }
      seg->next = (*cur_seg);
      (*cur_seg) = seg;
    } else {
      /* add segment to tail of unacked list */
      useg->next = seg;
      useg = useg->next;
    }
  }
/* do not queue empty segments on the unacked list */
} else {
  tcp_seg_free(seg);
}
seg = pcb->unsent;

}
#if TCP_OVERSIZE
if (pcb->unsent == NULL) {
/* last unsent has been removed, reset unsent_oversize /
pcb->unsent_oversize = 0;
}
#endif /
TCP_OVERSIZE */

pcb->flags &= ~TF_NAGLEMEMERR;
return ERR_OK;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值