📄 tcp_out.c
字号:
if (pcb->unsent == NULL) { useg = NULL; } else { for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); } /* { useg is last segment on the unsent queue, NULL if list is empty } */ /* If there is room in the last pbuf on the unsent queue, chain the first pbuf on the queue together with that. */ if (useg != NULL && TCP_TCPLEN(useg) != 0 && !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && !(flags & (TCP_SYN | TCP_FIN)) && /* fit within max seg size */ useg->len + queue->len <= pcb->mss) { /* Remove TCP header from first segment of our to-be-queued list */ if(pbuf_header(queue->p, -TCP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\n", 0); TCP_STATS_INC(tcp.err); goto memerr; } pbuf_cat(useg->p, queue->p); useg->len += queue->len; useg->next = queue->next; LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); if (seg == queue) { seg = NULL; } memp_free(MEMP_TCP_SEG, queue); } else { /* empty list */ if (useg == NULL) { /* initialize list with this segment */ pcb->unsent = queue; } /* enqueue segment */ else { useg->next = queue; } } if ((flags & TCP_SYN) || (flags & TCP_FIN)) { ++len; } if (flags & TCP_FIN) { pcb->flags |= TF_FIN; } pcb->snd_lbb += len; pcb->snd_buf -= len; /* update number of segments on the queues */ pcb->snd_queuelen = queuelen; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); if (pcb->snd_queuelen != 0) { LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } /* Set the PSH flag in the last segment that we enqueued, but only if the segment has data (indicated by seglen > 0). */ if (seg != NULL && seglen > 0 && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); } return ERR_OK;memerr: pcb->flags |= TF_NAGLEMEMERR; TCP_STATS_INC(tcp.memerr); if (queue != NULL) { tcp_segs_free(queue); } if (pcb->snd_queuelen != 0) { LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); return ERR_MEM;}/** * 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_ttcp_output(struct tcp_pcb *pcb){ struct pbuf *p; struct tcp_hdr *tcphdr; struct tcp_seg *seg, *useg; u32_t wnd;#if TCP_CWND_DEBUG s16_t i = 0;#endif /* TCP_CWND_DEBUG */ /* 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; /* useg should point to last segment on unacked queue */ useg = pcb->unacked; if (useg != NULL) { for (; useg->next != NULL; useg = useg->next); } /* 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)) { p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); if (p == NULL) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); return ERR_BUF; } LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); /* remove ACK flags from the PCB, as we send an empty ACK now */ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); tcphdr = p->payload; tcphdr->src = htons(pcb->local_port); tcphdr->dest = htons(pcb->remote_port); tcphdr->seqno = htonl(pcb->snd_nxt); tcphdr->ackno = htonl(pcb->rcv_nxt); TCPH_FLAGS_SET(tcphdr, TCP_ACK); tcphdr->wnd = htons(pcb->rcv_ann_wnd); tcphdr->urgp = 0; TCPH_HDRLEN_SET(tcphdr, 5); tcphdr->chksum = 0;#if CHECKSUM_GEN_TCP tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), IP_PROTO_TCP, p->tot_len);#endif#if LWIP_NETIF_HWADDRHINT { struct netif *netif; netif = ip_route(&pcb->remote_ip); if(netif != NULL){ netif->addr_hint = &(pcb->addr_hint); ip_output_if(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP, netif); netif->addr_hint = NULL; } }#else /* LWIP_NETIF_HWADDRHINT*/ ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP);#endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); return ERR_OK; }#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_enqueue 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_enqueue/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); pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { pcb->snd_max = pcb->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 at the head. 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 head of unacked list */ seg->next = pcb->unacked; pcb->unacked = 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 (seg != NULL && pcb->persist_backoff == 0 && ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { /* prepare for persist timer */ pcb->persist_cnt = 0; pcb->persist_backoff = 1; } pcb->flags &= ~TF_NAGLEMEMERR; return ERR_OK;}/** * Called by tcp_output() to actually send a TCP segment over IP. * * @param seg the tcp_seg to send * @param pcb the tcp_pcb for the TCP connection used to send the segment */static voidtcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb){ u16_t len; struct netif *netif; /** @bug Exclude retransmitted segments from this count. */ snmp_inc_tcpoutsegs(); /* The TCP header has already been constructed, but the ackno and wnd fields remain. */ seg->tcphdr->ackno = htonl(pcb->rcv_nxt); /* advertise our receive window size in this TCP segment */ seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); /* If we don't have a local IP address, we get one by calling ip_route(). */ if (ip_addr_isany(&(pcb->local_ip))) { netif = ip_route(&(pcb->remote_ip)); if (netif == NULL) { return; } ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); } /* Set retransmission timer running if it is not currently enabled */ if(pcb->rtime == -1) pcb->rtime = 0; if (pcb->rttest == 0) { pcb->rttest = tcp_ticks; pcb->rtseq = ntohl(seg->tcphdr->seqno); LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); } LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + seg->len)); len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -