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

📄 tcp_out.c

📁 lwip-1.4.0
💻 C
📖 第 1 页 / 共 4 页
字号:
/** * @file * Transmission Control Protocol, outgoing traffic * * The output functions of TCP. * *//* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, *    this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, *    this list of conditions and the following disclaimer in the documentation *    and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels <adam@sics.se> * */#include "lwip/opt.h"#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */#include "lwip/tcp_impl.h"#include "lwip/def.h"#include "lwip/mem.h"#include "lwip/memp.h"#include "lwip/sys.h"#include "lwip/ip_addr.h"#include "lwip/netif.h"#include "lwip/inet_chksum.h"#include "lwip/stats.h"#include "lwip/snmp.h"#include <string.h>/* Define some copy-macros for checksum-on-copy so that the code looks   nicer by preventing too many ifdef's. */#if TCP_CHECKSUM_ON_COPY#define TCP_DATA_COPY(dst, src, len, seg) do { \  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \                     len, &seg->chksum, &seg->chksum_swapped); \  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);#else /* TCP_CHECKSUM_ON_COPY*/#define TCP_DATA_COPY(dst, src, len, seg)                     MEMCPY(dst, src, len)#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)#endif /* TCP_CHECKSUM_ON_COPY*//** Define this to 1 for an extra check that the output checksum is valid * (usefule when the checksum is generated by the application, not the stack) */#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0#endif/* Forward declarations.*/static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);/** Allocate a pbuf and create a tcphdr at p->payload, used for output * functions other than the default tcp_output -> tcp_output_segment * (e.g. tcp_send_empty_ack, etc.) * * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) * @param optlen length of header-options * @param datalen length of tcp data to reserve in pbuf * @param seqno_be seqno in network byte order (big-endian) * @return pbuf with p->payload being the tcp_hdr */static struct pbuf *tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,                      u32_t seqno_be /* already in network byte order */){  struct tcp_hdr *tcphdr;  struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);  if (p != NULL) {    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",                 (p->len >= TCP_HLEN + optlen));    tcphdr = (struct tcp_hdr *)p->payload;    tcphdr->src = htons(pcb->local_port);    tcphdr->dest = htons(pcb->remote_port);    tcphdr->seqno = seqno_be;    tcphdr->ackno = htonl(pcb->rcv_nxt);    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);    tcphdr->wnd = htons(pcb->rcv_ann_wnd);    tcphdr->chksum = 0;    tcphdr->urgp = 0;    /* If we're sending a packet, update the announced right window edge */    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;  }  return p;}/** * Called by tcp_close() to send a segment including FIN flag but not data. * * @param pcb the tcp_pcb over which to send a segment * @return ERR_OK if sent, another err_t otherwise */err_ttcp_send_fin(struct tcp_pcb *pcb){  /* first, try to add the fin to the last unsent segment */  if (pcb->unsent != NULL) {    struct tcp_seg *last_unsent;    for (last_unsent = pcb->unsent; last_unsent->next != NULL;         last_unsent = last_unsent->next);    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);      return ERR_OK;    }  }  /* no data, no length, flags, copy=1, no optdata */  return tcp_enqueue_flags(pcb, TCP_FIN);}/** * Create a TCP segment with prefilled header. * * Called by tcp_write and tcp_enqueue_flags. * * @param pcb Protocol control block for the TCP connection. * @param p pbuf that is used to hold the TCP header. * @param flags TCP flags for header. * @param seqno TCP sequence number of this packet * @param optflags options to include in TCP header * @return a new tcp_seg pointing to p, or NULL. * The TCP header is filled in except ackno and wnd. * p is freed on failure. */static struct tcp_seg *tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags){  struct tcp_seg *seg;  u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);  if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));    pbuf_free(p);    return NULL;  }  seg->flags = optflags;  seg->next = NULL;  seg->p = p;  seg->len = p->tot_len - optlen;#if TCP_OVERSIZE_DBGCHECK  seg->oversize_left = 0;#endif /* TCP_OVERSIZE_DBGCHECK */#if TCP_CHECKSUM_ON_COPY  seg->chksum = 0;  seg->chksum_swapped = 0;  /* check optflags */  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);#endif /* TCP_CHECKSUM_ON_COPY */  /* build TCP header */  if (pbuf_header(p, TCP_HLEN)) {    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));    TCP_STATS_INC(tcp.err);    tcp_seg_free(seg);    return NULL;  }  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;  seg->tcphdr->src = htons(pcb->local_port);  seg->tcphdr->dest = htons(pcb->remote_port);  seg->tcphdr->seqno = htonl(seqno);  /* ackno is set in tcp_output */  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);  /* wnd and chksum are set in tcp_output */  seg->tcphdr->urgp = 0;  return seg;} /** * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. * * This function is like pbuf_alloc(layer, length, PBUF_RAM) except * there may be extra bytes available at the end. * * @param layer flag to define header size. * @param length size of the pbuf's payload. * @param max_length maximum usable size of payload+oversize. * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. * @param pcb The TCP connection that willo enqueue the pbuf. * @param apiflags API flags given to tcp_write. * @param first_seg true when this pbuf will be used in the first enqueued segment. * @param  */#if TCP_OVERSIZEstatic struct pbuf *tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,                  u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,                  u8_t first_seg){  struct pbuf *p;  u16_t alloc = length;#if LWIP_NETIF_TX_SINGLE_PBUF  LWIP_UNUSED_ARG(max_length);  LWIP_UNUSED_ARG(pcb);  LWIP_UNUSED_ARG(apiflags);  LWIP_UNUSED_ARG(first_seg);  /* always create MSS-sized pbufs */  alloc = TCP_MSS;#else /* LWIP_NETIF_TX_SINGLE_PBUF */  if (length < max_length) {    /* Should we allocate an oversized pbuf, or just the minimum     * length required? If tcp_write is going to be called again     * before this segment is transmitted, we want the oversized     * buffer. If the segment will be transmitted immediately, we can     * save memory by allocating only length. We use a simple     * heuristic based on the following information:     *     * Did the user set TCP_WRITE_FLAG_MORE?     *     * Will the Nagle algorithm defer transmission of this segment?     */    if ((apiflags & TCP_WRITE_FLAG_MORE) ||        (!(pcb->flags & TF_NODELAY) &&         (!first_seg ||          pcb->unsent != NULL ||          pcb->unacked != NULL))) {      alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));    }  }#endif /* LWIP_NETIF_TX_SINGLE_PBUF */  p = pbuf_alloc(layer, alloc, PBUF_RAM);  if (p == NULL) {    return NULL;  }  LWIP_ASSERT("need unchained pbuf", p->next == NULL);  *oversize = p->len - length;  /* trim p->len to the currently used size */  p->len = p->tot_len = length;  return p;}#else /* TCP_OVERSIZE */#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)#endif /* TCP_OVERSIZE */#if TCP_CHECKSUM_ON_COPY/** Add a checksum of newly added data to the segment */static voidtcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,                   u8_t *seg_chksum_swapped){  u32_t helper;  /* add chksum to old chksum and fold to u16_t */  helper = chksum + *seg_chksum;  chksum = FOLD_U32T(helper);  if ((len & 1) != 0) {    *seg_chksum_swapped = 1 - *seg_chksum_swapped;    chksum = SWAP_BYTES_IN_WORD(chksum);  }  *seg_chksum = chksum;}#endif /* TCP_CHECKSUM_ON_COPY *//** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). * * @param pcb the tcp pcb to check for * @param len length of data to send (checked agains snd_buf) * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise */static err_ttcp_write_checks(struct tcp_pcb *pcb, u16_t len){  /* connection is in invalid state for data transmission? */  if ((pcb->state != ESTABLISHED) &&      (pcb->state != CLOSE_WAIT) &&      (pcb->state != SYN_SENT) &&      (pcb->state != SYN_RCVD)) {    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));    return ERR_CONN;  } else if (len == 0) {    return ERR_OK;  }  /* fail on too much data */  if (len > pcb->snd_buf) {    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",      len, pcb->snd_buf));    pcb->flags |= TF_NAGLEMEMERR;    return ERR_MEM;  }  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));  /* If total number of pbufs on the unsent/unacked queues exceeds the   * configured maximum, return an error */  /* 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_write: 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 (pcb->snd_queuelen != 0) {    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",      pcb->unacked != NULL || pcb->unsent != NULL);  } else {    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",      pcb->unacked == NULL && pcb->unsent == NULL);  }  return ERR_OK;}/** * Write data for sending (but does not send it immediately). * * It waits in the expectation of more data being sent soon (as * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). * * @param pcb Protocol control block for the TCP connection to enqueue data for. * @param arg Pointer to the data to be enqueued for sending. * @param len Data length in bytes * @param apiflags combination of following flags : * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, * @return ERR_OK if enqueued, another err_t on error */err_ttcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags){  struct pbuf *concat_p = NULL;  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;  u16_t pos = 0; /* position in 'arg' data */  u16_t queuelen;  u8_t optlen = 0;  u8_t optflags = 0;#if TCP_OVERSIZE  u16_t oversize = 0;  u16_t oversize_used = 0;#endif /* TCP_OVERSIZE */#if TCP_CHECKSUM_ON_COPY  u16_t concat_chksum = 0;  u8_t concat_chksum_swapped = 0;  u16_t concat_chksummed = 0;#endif /* TCP_CHECKSUM_ON_COPY */  err_t err;

⌨️ 快捷键说明

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