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

📄 tcpout.c

📁 在ARM7和UC/OSII的平台上实现了GPS自动报站的功能,涉及GPS模块LEA_4S的驱动,位置速寻算法,语音芯片ISD4004的录放音驱动,LED页面管理等等.从启动代码到操作系统的移植以及到业
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * FILENAME: tcp_out.c
 *
 * Copyright 1997- 2000 By InterNiche Technologies Inc. All rights reserved
 *
 *
 * MODULE: MTCP
 *
 * ROUTINES: tcp_output(), tcp_send(),
 *
 * PORTABLE: yes
 */

/* Additional Copyrights: */
/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
*/

#define TCPOUTFLAGS 1

#include "ipport.h"
#include "mtcp.h"

#include "minip.h"      /* (yaxon add) */


/*
 * Initial options.
 */
u_char   tcp_initopt[4] =  {  TCPOPT_MAXSEG, 4, 0x0,  0x0,  };

/* Size limits on socket buffer data. If a small number of bugbufs per
 * socket is used, these defaults should be adjusted downward by the port's 
 * initialization code.
 */
unsigned   mt_deftxwin = 1024 * 8;      /* default send window */
unsigned   mt_defrxwin = 1024 * 8;      /* default receive window */

/* Flags used when sending segments in tcp_output.
 * Basic flags (TH_RST,TH_ACK,TH_SYN,TH_FIN) are totally
 * determined by state, with the proviso that TH_FIN is sent only
 * if all data queued for output is included in the segment.
 */
u_char   tcp_outflags[TCP_NSTATES]  =
{
   TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
   TH_ACK, TH_ACK,
   TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_ACK, TH_ACK,
};

/* FUNCTION: tcp_output()
 *
 * Tcp output routine: figure out what should be sent and send it.
 *
 * PARAM1: struct tcpcb *tp
 *
 * RETURNS: 0 if OK, else BSD sockets error code.
 */

int
tcp_output(struct tcpcb * tp)
{
   struct msocket * so = (struct msocket *)tp->t_inpcb;
   int      len;     /* length of data to try to send */
   int      win;     /* scratch wind holder */
   int      off;     /* 0ffset (in bytes) into sendq queue of first unsent data */
   int      flags, error;  /* scratch */
   struct ip * pip;        /* pointer to IP header to send */
   struct tcphdr * ptcp;   /* tcp header to send */
   u_char * opt;
   unsigned optlen = 0;
   int      idle, sendalot;
   PACKET   sendp;

   /*
    * Determine length of data that should be transmitted,
    * and flags that will be used.
    * If there is some data or critical controls (SYN, RST)
    * to send, then transmit; otherwise, investigate further.
    */
   idle = (tp->snd_max == tp->snd_una);
again:
   sendalot = 0;
   off = (int)(tp->snd_nxt - tp->snd_una);

   /* figure out other guys window */
   win = (int)tp->snd_wnd;
   if (win > (long)tp->snd_cwnd) /* see if we need congestion control */
   {
      if (tp->snd_cwnd < tp->t_maxseg)
         dprintf("tcp_out: congested; snd_cwnd %u\n", tp->snd_cwnd);
      win = (int)(tp->snd_cwnd & ~(ALIGN_TYPE-1)); /* keep data aligned */
   }

   len = (int)MIN(so->sendq.sb_cc, (unsigned)win) - off;

#ifdef NPDEBUG
   if(off < 0) { dtrap("tcpout 0\n"); }
#endif

   flags = tcp_outflags[tp->t_state];

   /* see if we want to reset peer */
   if(tp->t_flags & TF_SENDRST)
   {
      flags |= TH_RST;
      len = 0;
   }

   /*
    * Before ESTABLISHED, force sending of initial options
    * unless TCP set to not do any options.
    */
   opt = NULL;
   if (flags & TH_SYN && ((tp->t_flags & TF_NOOPT) == 0))
   {
      opt = tcp_initopt;   /* always send MSS option! */
      optlen = sizeof (tcp_initopt);
      opt[2] = (u_char) ((TCP_MSS & 0xff00) >> 8);
      opt[3] = (u_char) (TCP_MSS & 0xff);
   }

   if (len < 0) 
   {
      /*
       * If FIN has been sent but not acked,
       * but we haven't been called to retransmit,
       * len will be -1.  Otherwise, window shrank
       * after we sent into it.  If window shrank to 0,
       * cancel pending retransmit and pull snd_nxt
       * back to (closed) window.  We will enter persist
       * state below.  If the window didn't close completely,
       * just wait for an ACK.
       */
      len = 0;
      if (win == 0) 
      {
         tp->t_timer[TCPT_REXMT] = 0;
         tp->snd_nxt = tp->snd_una;
      }
   }
   if (len > (int)tp->t_maxseg)
   {
      len = tp->t_maxseg;
      sendalot = 1;
   }
   if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->sendq.sb_cc))
      flags &= ~TH_FIN;

   /* figure out what OUR advertised window should be */
   win = (int)(mt_defrxwin - so->rcvdq.sb_cc);

   /*
    * If our state indicates that FIN should be sent
    * and we have not yet done so, or we're retransmitting the FIN,
    * then we need to send.
    */
   if ((flags & TH_FIN) &&
       (so->sendq.sb_cc == 0) &&
       ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
   {
      goto send;
   }

   /*
    * Send if we owe peer an ACK.
    */
   if (tp->t_flags & (TF_ACKNOW | TF_SENDKEEP))
      goto send;
   if (flags & (TH_SYN|TH_RST))
      goto send;
   if (SEQ_GT(tp->snd_up, tp->snd_una))
      goto send;

   /*
    * Sender silly window avoidance.  If connection is idle
    * and can send all data, a maximum segment,
    * at least a maximum default-size segment do it,
    * or are forced, do it; otherwise don't bother.
    * If peer's buffer is tiny, then send
    * when window is at least half open.
    * If retransmitting (possibly after persist timer forced us
    * to send into a small window), then must resend.
    */
   if (len)
   {
      if (len == (int)tp->t_maxseg)
         goto send;
      if ((idle || tp->t_flags & TF_NODELAY) &&
          len + off >= (int)so->sendq.sb_cc)
      {
         goto send;
      }
      if (tp->t_force)
         goto send;
      if (len >= (int)(tp->max_sndwnd / 2))
         goto send;
      if (SEQ_LT(tp->snd_nxt, tp->snd_max))
         goto send;
   }

   /*
    * Compare available window to amount of window
    * known to peer (as advertised window less
    * next expected input).  If the difference is at least two
    * max size segments or at least 35% of the maximum possible
    * window, then want to send a window update to peer.
    */
   if (win > 0)
   {
      int   adv = (int)win - (int)(tp->rcv_adv - tp->rcv_nxt);

      if (so->sendq.sb_cc == 0 && adv >= (int)(tp->t_maxseg * 2))
         goto send;
      if ((100 * (unsigned)adv / mt_defrxwin) >= 35)
         goto send;
   }

   /*
    * TCP window updates are not reliable, rather a polling protocol
    * using ``persist'' packets is used to insure receipt of window
    * updates.  The three ``states'' for the output side are:
    *   idle         not doing retransmits or persists
    *   persisting      to move a small or zero window
    *   (re)transmitting   and thereby not persisting
    *
    * tp->t_timer[TCPT_PERSIST]
    *   is set when we are in persist state.
    * tp->t_force
    *   is set when we are called to send a persist packet.
    * tp->t_timer[TCPT_REXMT]
    *   is set when we are retransmitting
    * The output side is idle when both timers are zero.
    *
    * If send window is too small, there is data to transmit, and no
    * retransmit or persist is pending, then go to persist state.
    * If nothing happens soon, send when timer expires:
    * if window is nonzero, transmit what we can,
    * otherwise force out a byte.
    */
   if (so->sendq.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
       tp->t_timer[TCPT_PERSIST] == 0) 
   {
      tp->t_rxtshift = 0;
      tcp_setpersist(tp);
   }

   /*
    * No reason to send a segment, just return.
    */
   return (0);

send:
   ENTER_CRIT_SECTION(tp);

   /* Limit send length to the current buffer */
   if (len)
   {
      int   bufoff = off;

      sendp = (PACKET)so->sendq.p_head;

      /* find packet containing data to send (at "off") */
      while (sendp)  /* loop through socket send list */
      {
         if(bufoff <= 0)
            break;   /* off is in this buffer, break */

         bufoff -= sendp->m_len;
         sendp = sendp->m_next;
      }
      if (!sendp)
      { 
         if(bufoff == 0)  /* all data in queue is sent? */
         {
            len = 0;
            goto sentall;
         }
         else
         {
            dtrap("tcpout 1\n");  /* shouldn't happen */
            return EINVAL;
         }
      }
#ifdef NPDEBUG
      if (bufoff != 0) { dtrap("tcpout 2\n");  /* shouldn't happen */ }
#endif   /* NPDEBUG */

      /* if socket has multiple unsent pkts, set flag for send to loop */
      if ((so->sendq.p_head) && (len > (int)sendp->m_len))
      {
         flags &= ~TH_FIN; /* don't FIN on segment prior to last */
         sendalot = 1;     /* set to send more segments */
      }
      if((flags & TH_FIN) && (so->sendq.sb_cc > (unsigned)len))
      {
         /* This can happen on slow links (PPP) which retry the last 
          * segment - the one with the FIN bit attached to data.

⌨️ 快捷键说明

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