⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp_out.c

📁 lwip-1.4.0
💻 C
📖 第 1 页 / 共 4 页
字号:
#if LWIP_NETIF_TX_SINGLE_PBUF  /* Always copy to try to create single pbufs for TX */  apiflags |= TCP_WRITE_FLAG_COPY;#endif /* LWIP_NETIF_TX_SINGLE_PBUF */  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",    (void *)pcb, arg, len, (u16_t)apiflags));  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",              arg != NULL, return ERR_ARG;);  err = tcp_write_checks(pcb, len);  if (err != ERR_OK) {    return err;  }  queuelen = pcb->snd_queuelen;#if LWIP_TCP_TIMESTAMPS  if ((pcb->flags & TF_TIMESTAMP)) {    optflags = TF_SEG_OPTS_TS;    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);  }#endif /* LWIP_TCP_TIMESTAMPS */  /*   * TCP segmentation is done in three phases with increasing complexity:   *   * 1. Copy data directly into an oversized pbuf.   * 2. Chain a new pbuf to the end of pcb->unsent.   * 3. Create new segments.   *   * We may run out of memory at any point. In that case we must   * return ERR_MEM and not change anything in pcb. Therefore, all   * changes are recorded in local variables and committed at the end   * of the function. Some pcb fields are maintained in local copies:   *   * queuelen = pcb->snd_queuelen   * oversize = pcb->unsent_oversize   *   * These variables are set consistently by the phases:   *   * seg points to the last segment tampered with.   *   * pos records progress as data is segmented.   */  /* Find the tail of the unsent queue. */  if (pcb->unsent != NULL) {    u16_t space;    u16_t unsent_optlen;    /* @todo: this could be sped up by keeping last_unsent in the pcb */    for (last_unsent = pcb->unsent; last_unsent->next != NULL;         last_unsent = last_unsent->next);    /* Usable space at the end of the last unsent segment */    unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);    space = pcb->mss - (last_unsent->len + unsent_optlen);    /*     * Phase 1: Copy data directly into an oversized pbuf.     *     * The number of bytes copied is recorded in the oversize_used     * variable. The actual copying is done at the bottom of the     * function.     */#if TCP_OVERSIZE#if TCP_OVERSIZE_DBGCHECK    /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",                pcb->unsent_oversize == last_unsent->oversize_left);#endif /* TCP_OVERSIZE_DBGCHECK */    oversize = pcb->unsent_oversize;    if (oversize > 0) {      LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);      seg = last_unsent;      oversize_used = oversize < len ? oversize : len;      pos += oversize_used;      oversize -= oversize_used;      space -= oversize_used;    }    /* now we are either finished or oversize is zero */    LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));#endif /* TCP_OVERSIZE */    /*     * Phase 2: Chain a new pbuf to the end of pcb->unsent.     *     * We don't extend segments containing SYN/FIN flags or options     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at     * the end.     */    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {      u16_t seglen = space < len - pos ? space : len - pos;      seg = last_unsent;      /* Create a pbuf with a copy or reference to seglen bytes. We       * can use PBUF_RAW here since the data appears in the middle of       * a segment. A header will never be prepended. */      if (apiflags & TCP_WRITE_FLAG_COPY) {        /* Data is copied */        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",                       seglen));          goto memerr;        }#if TCP_OVERSIZE_DBGCHECK        last_unsent->oversize_left = oversize;#endif /* TCP_OVERSIZE_DBGCHECK */        TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);#if TCP_CHECKSUM_ON_COPY        concat_chksummed += seglen;#endif /* TCP_CHECKSUM_ON_COPY */      } else {        /* Data is not copied */        if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,                      ("tcp_write: could not allocate memory for zero-copy pbuf\n"));          goto memerr;        }#if TCP_CHECKSUM_ON_COPY        /* calculate the checksum of nocopy-data */        tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,          &concat_chksum, &concat_chksum_swapped);        concat_chksummed += seglen;#endif /* TCP_CHECKSUM_ON_COPY */        /* reference the non-volatile payload data */        concat_p->payload = (u8_t*)arg + pos;      }      pos += seglen;      queuelen += pbuf_clen(concat_p);    }  } else {#if TCP_OVERSIZE    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",                pcb->unsent_oversize == 0);#endif /* TCP_OVERSIZE */  }  /*   * Phase 3: Create new segments.   *   * The new segments are chained together in the local 'queue'   * variable, ready to be appended to pcb->unsent.   */  while (pos < len) {    struct pbuf *p;    u16_t left = len - pos;    u16_t max_len = pcb->mss - optlen;    u16_t seglen = left > max_len ? max_len : left;#if TCP_CHECKSUM_ON_COPY    u16_t chksum = 0;    u8_t chksum_swapped = 0;#endif /* TCP_CHECKSUM_ON_COPY */    if (apiflags & TCP_WRITE_FLAG_COPY) {      /* If copy is set, memory should be allocated and data copied       * into pbuf */      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));        goto memerr;      }      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",                  (p->len >= seglen));      TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);    } else {      /* Copy is not set: First allocate a pbuf for holding the data.       * Since the referenced data is available at least until it is       * sent out on the link (as it has to be ACKed by the remote       * party) we can safely use PBUF_ROM instead of PBUF_REF here.       */      struct pbuf *p2;#if TCP_OVERSIZE      LWIP_ASSERT("oversize == 0", oversize == 0);#endif /* TCP_OVERSIZE */      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));        goto memerr;      }#if TCP_CHECKSUM_ON_COPY      /* calculate the checksum of nocopy-data */      chksum = ~inet_chksum((u8_t*)arg + pos, seglen);#endif /* TCP_CHECKSUM_ON_COPY */      /* reference the non-volatile payload data */      p2->payload = (u8_t*)arg + pos;      /* Second, allocate a pbuf for the headers. */      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {        /* If allocation fails, we have to deallocate the data pbuf as         * well. */        pbuf_free(p2);        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));        goto memerr;      }      /* Concatenate the headers and data pbufs together. */      pbuf_cat(p/*header*/, p2/*data*/);    }    queuelen += pbuf_clen(p);    /* Now that there are more segments queued, we check again if the     * length of the queue exceeds the configured maximum or     * overflows. */    if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));      pbuf_free(p);      goto memerr;    }    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {      goto memerr;    }#if TCP_OVERSIZE_DBGCHECK    seg->oversize_left = oversize;#endif /* TCP_OVERSIZE_DBGCHECK */#if TCP_CHECKSUM_ON_COPY    seg->chksum = chksum;    seg->chksum_swapped = chksum_swapped;    seg->flags |= TF_SEG_DATA_CHECKSUMMED;#endif /* TCP_CHECKSUM_ON_COPY */    /* first segment of to-be-queued data? */    if (queue == NULL) {      queue = seg;    } else {      /* Attach the segment to the end of the queued segments */      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);      prev_seg->next = seg;    }    /* remember last segment of to-be-queued data for next iteration */    prev_seg = seg;    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",      ntohl(seg->tcphdr->seqno),      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));    pos += seglen;  }  /*   * All three segmentation phases were successful. We can commit the   * transaction.   */  /*   * Phase 1: If data has been added to the preallocated tail of   * last_unsent, we update the length fields of the pbuf chain.   */#if TCP_OVERSIZE  if (oversize_used > 0) {    struct pbuf *p;    /* Bump tot_len of whole chain, len of tail */    for (p = last_unsent->p; p; p = p->next) {      p->tot_len += oversize_used;      if (p->next == NULL) {        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);        p->len += oversize_used;      }    }    last_unsent->len += oversize_used;#if TCP_OVERSIZE_DBGCHECK    last_unsent->oversize_left -= oversize_used;#endif /* TCP_OVERSIZE_DBGCHECK */  }  pcb->unsent_oversize = oversize;#endif /* TCP_OVERSIZE */  /*   * Phase 2: concat_p can be concatenated onto last_unsent->p   */  if (concat_p != NULL) {    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",      (last_unsent != NULL));    pbuf_cat(last_unsent->p, concat_p);    last_unsent->len += concat_p->tot_len;#if TCP_CHECKSUM_ON_COPY    if (concat_chksummed) {      tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,        &last_unsent->chksum_swapped);      last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;    }#endif /* TCP_CHECKSUM_ON_COPY */  }  /*   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that   * is harmless   */  if (last_unsent == NULL) {    pcb->unsent = queue;  } else {    last_unsent->next = queue;  }  /*   * Finally update the pcb state.   */  pcb->snd_lbb += len;  pcb->snd_buf -= len;  pcb->snd_queuelen = queuelen;  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",    pcb->snd_queuelen));  if (pcb->snd_queuelen != 0) {    LWIP_ASSERT("tcp_write: valid queue length",                pcb->unacked != NULL || pcb->unsent != NULL);  }  /* Set the PSH flag in the last segment that we enqueued. */  if (seg != NULL && 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 (concat_p != NULL) {    pbuf_free(concat_p);  }  if (queue != NULL) {    tcp_segs_free(queue);  }  if (pcb->snd_queuelen != 0) {    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||      pcb->unsent != NULL);  }  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));  return ERR_MEM;}/** * Enqueue TCP options for transmission. * * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). * * @param pcb Protocol control block for the TCP connection. * @param flags TCP header flags to set in the outgoing segment. * @param optdata pointer to TCP options, or NULL. * @param optlen length of TCP options in bytes. */err_ttcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags){  struct pbuf *p;  struct tcp_seg *seg;  u8_t optflags = 0;  u8_t optlen = 0;  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",              (flags & (TCP_SYN | TCP_FIN)) != 0);  /* check for configured max queuelen and possible overflow */  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",                                       pcb->snd_queuelen, TCP_SND_QUEUELEN));    TCP_STATS_INC(tcp.memerr);    pcb->flags |= TF_NAGLEMEMERR;    return ERR_MEM;  }  if (flags & TCP_SYN) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -