📄 tcp.c
字号:
DEBUGF(TCP_DEBUG, ("tcp_connect to port %d\n", port));
if(ipaddr != NULL) {
pcb->remote_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;
pcb->connected = connected;
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;
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_slowtmr():
*
* 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)
{
static struct tcp_pcb *pcb, *pcb2, *prev;
static struct tcp_seg *seg, *useg;
static u32_t eff_wnd;
static u8_t pcb_remove; /* flag if a PCB should be removed */
++tcp_ticks;
/* Steps through all of the active PCBs. */
prev = NULL;
pcb = tcp_active_pcbs;
while(pcb != NULL) {
ASSERT("tcp_timer_coarse: active pcb->state != CLOSED", pcb->state != CLOSED);
ASSERT("tcp_timer_coarse: active pcb->state != LISTEN", pcb->state != LISTEN);
ASSERT("tcp_timer_coarse: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
pcb_remove = 0;
if(pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
++pcb_remove;
} else if(pcb->nrtx == TCP_MAXRTX) {
++pcb_remove;
} else {
++pcb->rtime;
seg = pcb->unacked;
if(seg != NULL && pcb->rtime >= pcb->rto) {
DEBUGF(TCP_RTO_DEBUG, ("tcp_timer_coarse: rtime %ld pcb->rto %d\n",
tcp_ticks - 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];
}
/* Move all other unacked segments to the unsent queue. */
if(seg->next != NULL) {
for(useg = seg->next; useg->next != NULL; useg = useg->next);
/* useg now points to the last segment on the unacked queue. */
useg->next = pcb->unsent;
pcb->unsent = seg->next;
seg->next = NULL;
pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno);
}
/* Do the actual retransmission. */
tcp_rexmit_seg(pcb, seg);
/* Reduce congestion window and ssthresh. */
eff_wnd = 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;
DEBUGF(TCP_CWND_DEBUG, ("tcp_rexmit_seg: 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;
}
}
/* 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;
}
#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;
}
}
/* 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) {
ASSERT("tcp_timer_coarse: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
prev->next = pcb->next;
} else {
/* This PCB was the first. */
ASSERT("tcp_timer_coarse: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
tcp_active_pcbs = pcb->next;
}
if(pcb->errf != NULL) {
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->poll != NULL) {
pcb->polltmr = 0;
pcb->poll(pcb->callback_arg, pcb);
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) {
ASSERT("tcp_timer_coarse: 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) {
ASSERT("tcp_timer_coarse: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
prev->next = pcb->next;
} else {
/* This PCB was the first. */
ASSERT("tcp_timer_coarse: 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;
}
}
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_fasttmr():
*
* Is called every TCP_FINE_TIMEOUT (100 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) {
DEBUGF(TCP_DEBUG, ("tcp_timer_fine: delayed ACK\n"));
tcp_ack_now(pcb);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
}
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_segs_free():
*
* 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;
again:
if(seg != NULL) {
next = seg->next;
count += tcp_seg_free(seg);
seg = next;
goto again;
}
return count;
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_seg_free():
*
* Frees a TCP segment.
*
*/
/*-----------------------------------------------------------------------------------*/
u8_t
tcp_seg_free(struct tcp_seg *seg)
{
u8_t count = 0;
if(seg != NULL) {
if(seg->p == NULL) {
memp_free(MEMP_TCP_SEG, seg);
} else {
count = pbuf_free(seg->p);
#if TCP_DEBUG
seg->p = NULL;
#endif /* TCP_DEBUG */
memp_free(MEMP_TCP_SEG, seg);
}
}
return count;
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_seg_copy():
*
* Returns a copy of the given TCP segment.
*
*/
/*-----------------------------------------------------------------------------------*/
struct tcp_seg *
tcp_seg_copy(struct tcp_seg *seg)
{
struct tcp_seg *cseg;
cseg = memp_malloc(MEMP_TCP_SEG);
if(cseg == NULL) {
return NULL;
}
bcopy(seg, cseg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
return cseg;
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_new():
*
* Creates a new TCP protocol control block but doesn't place it on
* any of the TCP PCB lists.
*
*/
/*-----------------------------------------------------------------------------------*/
struct tcp_pcb *
tcp_new(void)
{
struct tcp_pcb *pcb;
u32_t iss;
pcb = memp_malloc2(MEMP_TCP_PCB);
if(pcb != NULL) {
bzero(pcb, sizeof(struct tcp_pcb));
pcb->snd_buf = TCP_SND_BUF;
pcb->snd_queuelen = 0;
pcb->rcv_wnd = TCP_WND;
pcb->mss = TCP_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
pcb->sa = 0;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = 0;
pcb->cwnd = 1;
iss = tcp_next_iss();
pcb->snd_wl2 = iss;
pcb->snd_nxt = iss;
pcb->snd_max = iss;
pcb->lastack = iss;
pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->polltmr = 0;
return pcb;
}
return NULL;
}
/*-----------------------------------------------------------------------------------*/
/*
* tcp_mem_reclaim():
*
* Tries to free up TCP memory. This function is called from the memory manager
* when memory is scarce.
*
*/
/*-----------------------------------------------------------------------------------*/
#if MEM_RECLAIM
static mem_size_t
tcp_mem_reclaim(void *arg, mem_size_t size)
{
static struct tcp_pcb *pcb, *inactive;
static u32_t inactivity;
static mem_size_t reclaimed;
reclaimed = 0;
/* Kill the oldest active connection, hoping that there may be
memory associated with it. */
inactivity = 0;
inactive = NULL;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if((u32_t)(tcp_ticks - pcb->tmr) > inactivity) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
}
}
if(inactive != NULL) {
DEBUGF(TCP_DEBUG, ("tcp_mem_reclaim: killing oldest PCB 0x%p (%ld)\n",
inactive, inactivity));
tcp_abort(inactive);
}
return reclaimed;
}
#endif /* MEM_RECLAIM */
/*-----------------------------------------------------------------------------------*/
/*
* tcp_memp_reclaim():
*
* Tries to free up TCP memory. This function is called from the
* memory pool manager when memory is scarce.
*
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -