📄 tcp.c
字号:
}
pcb->local_port = port;
return ERR_OK;
}
#if LWIP_CALLBACK_API
static err_t tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
{
(void)arg;
(void)pcb;
(void)err;
return ERR_ABRT;
}
#endif /* LWIP_CALLBACK_API */
/**
* Set the state of the connection to be LISTEN, which means that it
* is able to accept incoming connections. The protocol control block
* is reallocated in order to consume less memory. Setting the
* connection to LISTEN is an irreversible process.
*
*/
struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
{
struct tcp_pcb_listen *lpcb;
/* already listening? */
if (pcb->state == LISTEN)
{
return pcb;
}
lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN);
if (lpcb == NULL)
{
return NULL;
}
lpcb->callback_arg = pcb->callback_arg;
lpcb->local_port = pcb->local_port;
lpcb->state = LISTEN;
// lpcb->so_options = pcb->so_options;
// lpcb->so_options |= SOF_ACCEPTCONN;
// lpcb->ttl = pcb->ttl;
// lpcb->tos = pcb->tos;
lpcb->local_ip=pcb->local_ip;
memp_free(MEMP_TCP_PCB, pcb);
#if LWIP_CALLBACK_API
lpcb->accept = tcp_accept_null;
#endif /* LWIP_CALLBACK_API */
TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb);
return (struct tcp_pcb *)lpcb;
}
/**
* This function should be called by the application when it has
* processed the data. The purpose is to advertise a larger window
* when the data has been processed.
*
*/
void tcp_recved(struct tcp_pcb *pcb, u16_t len)
{
if ((u32_t)pcb->rcv_wnd + len > TCP_WND)
{
pcb->rcv_wnd = TCP_WND;
}
else
{
pcb->rcv_wnd += len;
}
if (!(pcb->flags & TF_ACK_DELAY) &&
!(pcb->flags & TF_ACK_NOW))
{
/*
* We send an ACK here (if one is not already pending, hence
* the above tests) as tcp_recved() implies that the application
* has processed some data, and so we can open the receiver's
* window to allow more to be transmitted. This could result in
* two ACKs being sent for each received packet in some limited cases
* (where the application is only receiving data, and is slow to
* process it) but it is necessary to guarantee that the sender can
* continue to transmit.
*/
tcp_ack(pcb);
}
}
/**
* A nastly hack featuring 'goto' statements that allocates a
* new TCP local port.
*/
static u16_t tcp_new_port(void)
{
struct tcp_pcb *pcb;
#ifndef TCP_LOCAL_PORT_RANGE_START
#define TCP_LOCAL_PORT_RANGE_START 4096
#define TCP_LOCAL_PORT_RANGE_END 0x7fff
#endif
static u16_t port = TCP_LOCAL_PORT_RANGE_START;
again:
if (++port > TCP_LOCAL_PORT_RANGE_END)
{
port = TCP_LOCAL_PORT_RANGE_START;
}
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)
{
if (pcb->local_port == port)
{
goto again;
}
}
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next)
{
if (pcb->local_port == port)
{
goto again;
}
}
for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next)
{
if (pcb->local_port == port)
{
goto again;
}
}
return port;
}
/**
* Connects to another host. The function given as the "connected"
* argument will be called when the connection has been established.
*
*/
err_t tcp_connect(struct tcp_pcb *pcb, Uint32 *ipaddr, u16_t port,
err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{
u32_t optdata;
err_t ret;
u32_t iss;
if (ipaddr != NULL)
{
pcb->dest_ip = *ipaddr;
}
else
{
return ERR_VAL;
}
pcb->remote_port = port;
if (pcb->local_port == 0)
{
pcb->local_port = tcp_new_port();
}
iss = tcp_next_iss();
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
pcb->snd_lbb = iss - 1;
pcb->rcv_wnd = TCP_WND;
pcb->snd_wnd = TCP_WND;
pcb->mss = TCP_MSS;
pcb->cwnd = 1;
pcb->ssthresh = pcb->mss * 10;
pcb->state = SYN_SENT;
#if LWIP_CALLBACK_API
pcb->connected = connected;
#endif /* LWIP_CALLBACK_API */
TCP_REG(&tcp_active_pcbs, pcb);
/* Build an MSS option */
optdata = htonl(((u32_t)2 << 24) |
((u32_t)4 << 16) |
(((u32_t)pcb->mss / 256) << 8) |
(pcb->mss & 255));
ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4);
if (ret == ERR_OK) {
tcp_output(pcb);
}
return ret;
}
/**
* Called every 500 ms and implements the retransmission timer and the timer that
* removes PCBs that have been in TIME-WAIT for enough time. It also increments
* various timers such as the inactivity timer in each PCB.
*/
void
tcp_slowtmr(void)
{
struct tcp_pcb *pcb, *pcb2, *prev;
u32_t eff_wnd;
u8_t pcb_remove; /* flag if a PCB should be removed */
err_t err;
err = ERR_OK;
++tcp_ticks;
/* Steps through all of the active PCBs. */
prev = NULL;
pcb = tcp_active_pcbs;
if (pcb == NULL)
{
}
while (pcb != NULL)
{
pcb_remove = 0;
if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX)
{
++pcb_remove;
}
else if (pcb->nrtx == TCP_MAXRTX)
{
++pcb_remove;
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
}
else
{
++pcb->rtime;
if (pcb->unacked != NULL && pcb->rtime >= pcb->rto)
{
// /* Time for a retransmission. */
// LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %u pcb->rto %u\n",\
// pcb->rtime, pcb->rto));
/* Double retransmission time-out unless we are trying to
* connect to somebody (i.e., we are in SYN_SENT). */
if (pcb->state != SYN_SENT)
{
pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
}
tcp_rexmit(pcb);
/* Reduce congestion window and ssthresh. */
eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
if (pcb->ssthresh < pcb->mss)
{
pcb->ssthresh = pcb->mss * 2;
}
pcb->cwnd = pcb->mss;
// LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %u ssthresh %u\n",
// pcb->cwnd, pcb->ssthresh));
}
}
/* Check if this PCB has stayed too long in FIN-WAIT-2 */
if (pcb->state == FIN_WAIT_2)
{
if ((u32_t)(tcp_ticks - pcb->tmr) >TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL)
{
++pcb_remove;
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
}
}
/* Check if KEEPALIVE should be sent */
// if((pcb->so_options & SOF_KEEPALIVE)&&((pcb->state == ESTABLISHED)||(pcb->state == CLOSE_WAIT)))
if(((pcb->state == ESTABLISHED)||(pcb->state == CLOSE_WAIT)))
{
if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
{
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %u.%u.%u.%u.\n",
// ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
// ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
tcp_abort(pcb);
}
else if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + pcb->keep_cnt * TCP_KEEPINTVL) / TCP_SLOW_INTERVAL)
{
tcp_keepalive(pcb);
pcb->keep_cnt++;
}
}
/* If this PCB has queued out of sequence data, but has been
inactive for too long, will drop the data (it will eventually
be retransmitted). */
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL && (u32_t)tcp_ticks - pcb->tmr >=pcb->rto * TCP_OOSEQ_TIMEOUT)
{
tcp_segs_free(pcb->ooseq);
pcb->ooseq = NULL;
// LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
}
#endif /* TCP_QUEUE_OOSEQ */
/* Check if this PCB has stayed too long in SYN-RCVD */
if (pcb->state == SYN_RCVD)
{
if ((u32_t)(tcp_ticks - pcb->tmr)>TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL)
{
++pcb_remove;
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
}
}
/* If the PCB should be removed, do it. */
if (pcb_remove)
{
tcp_pcb_purge(pcb);
/* Remove PCB from tcp_active_pcbs list. */
if (prev != NULL)
{
// LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
prev->next = pcb->next;
}
else
{
/* This PCB was the first. */
// LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
tcp_active_pcbs = pcb->next;
}
// TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT);
pcb2 = pcb->next;
memp_free(MEMP_TCP_PCB, pcb);
pcb = pcb2;
}
else
{
/* We check if we should poll the connection. */
++pcb->polltmr;
if (pcb->polltmr >= pcb->pollinterval)
{
pcb->polltmr = 0;
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
TCP_EVENT_POLL(pcb, err);
if (err == ERR_OK)
{
tcp_output(pcb);
}
}
prev = pcb;
pcb = pcb->next;
}
}
/* Steps through all of the TIME-WAIT PCBs. */
prev = NULL;
pcb = tcp_tw_pcbs;
while (pcb != NULL)
{
// LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
pcb_remove = 0;
/* Check if this PCB has stayed long enough in TIME-WAIT */
if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL)
{
++pcb_remove;
}
/* If the PCB should be removed, do it. */
if (pcb_remove)
{
tcp_pcb_purge(pcb);
/* Remove PCB from tcp_tw_pcbs list. */
if (prev != NULL)
{
// LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
prev->next = pcb->next;
}
else
{
/* This PCB was the first. */
// LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
tcp_tw_pcbs = pcb->next;
}
pcb2 = pcb->next;
memp_free(MEMP_TCP_PCB, pcb);
pcb = pcb2;
}
else
{
prev = pcb;
pcb = pcb->next;
}
}
}
/**
* Is called every TCP_FAST_INTERVAL (250 ms) and sends delayed ACKs.
*/
void tcp_fasttmr(void)
{
struct tcp_pcb *pcb;
/* send delayed ACKs */
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)
{
if (pcb->flags & TF_ACK_DELAY)
{
tcp_ack_now(pcb);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
}
}
/**
* Deallocates a list of TCP segments (tcp_seg structures).
*
*/
u8_t tcp_segs_free(struct tcp_seg *seg)
{
u8_t count = 0;
struct tcp_seg *next;
while (seg != NULL)
{
next = seg->next;
count += tcp_seg_free(seg);
seg = next;
}
return count;
}
/**
* Frees a TCP segment.
*
*/
u8_t tcp_seg_free(struct tcp_seg *seg)
{
u8_t count = 0;
if (seg != NULL)
{
if (seg->p != NULL)
{
count = pbuf_free(seg->p);
#if TCP_DEBUG
seg->p = NULL;
#endif /* TCP_DEBUG */
}
memp_free(MEMP_TCP_SEG, seg);
}
return count;
}
/**
* Sets the priority of a connection.
*
*/
void tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
{
pcb->prio = prio;
}
#if TCP_QUEUE_OOSEQ
/**
* Returns a copy of the given TCP segment.
*
*/
struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg)
{
struct tcp_seg *cseg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -