📄 tcp.c
字号:
* argument will be called when the connection has been established.
*
* @param pcb the tcp_pcb used to establish the connection
* @param ipaddr the remote ip address to connect to
* @param port the remote tcp port to connect to
* @param connected callback function to call when connected (or on error)
* @return ERR_VAL if invalid arguments are given
* ERR_OK if connect request has been sent
* other err_t values if connect request couldn't be sent
*/
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{
err_t ret;
u32_t iss;
LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\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->rcv_ann_wnd = TCP_WND;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->snd_wnd = TCP_WND;
/* As initial send MSS, we use TCP_MSS but limit it to 536.
The send MSS is updated when an MSS option is received. */
pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_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_RMV(&tcp_bound_pcbs, pcb);
TCP_REG(&tcp_active_pcbs, pcb);
snmp_inc_tcpactiveopens();
ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
#if LWIP_TCP_TIMESTAMPS
| TF_SEG_OPTS_TS
#endif
);
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.
*
* Automatically called from tcp_tmr().
*/
void
tcp_slowtmr(void)
{
struct tcp_pcb *pcb, *pcb2, *prev;
u16_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) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
}
while (pcb != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
pcb_remove = 0;
if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
}
else if (pcb->nrtx == TCP_MAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
} else {
if (pcb->persist_backoff > 0) {
/* If snd_wnd is zero, use persist timer to send 1 byte probes
* instead of using the standard retransmission mechanism. */
pcb->persist_cnt++;
if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++;
}
tcp_zero_window_probe(pcb);
}
} else {
/* Increase the retransmission timer if it is running */
if(pcb->rtime >= 0)
++pcb->rtime;
if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
/* Time for a retransmission. */
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
" pcb->rto %"S16_F"\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];
}
/* Reset the retransmission timer. */
pcb->rtime = 0;
/* 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 %"U16_F
" ssthresh %"U16_F"\n",
pcb->cwnd, pcb->ssthresh));
/* The following needs to be called AFTER cwnd is set to one
mss - STJ */
tcp_rexmit_rto(pcb);
}
}
}
/* 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 LWIP_TCP_KEEPALIVE
if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
/ TCP_SLOW_INTERVAL)
#else
if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
#endif /* LWIP_TCP_KEEPALIVE */
{
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
tcp_abort(pcb);
}
#if LWIP_TCP_KEEPALIVE
else if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl)
/ TCP_SLOW_INTERVAL)
#else
else if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT)
/ TCP_SLOW_INTERVAL)
#endif /* LWIP_TCP_KEEPALIVE */
{
tcp_keepalive(pcb);
pcb->keep_cnt_sent++;
}
}
/* 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"));
}
}
/* Check if this PCB has stayed too long in LAST-ACK */
if (pcb->state == LAST_ACK) {
if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\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 process data previously
* "refused" by upper layer (application) and sends delayed ACKs.
*
* Automatically called from tcp_tmr().
*/
void
tcp_fasttmr(void)
{
struct tcp_pcb *pcb;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
/* If there is data which was previously "refused" by upper layer */
if (pcb->refused_data != NULL) {
/* Notify again application with data previously received. */
err_t err;
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
if (err == ERR_OK) {
pcb->refused_data = NULL;
}
}
/* send delayed ACKs */
if (pcb->flags & TF_ACK_DELAY) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
tcp_ack_now(pcb);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
}
}
/**
* Deallocates a list of TCP segments (tcp_seg structures).
*
* @param seg tcp_seg list of TCP segments to free
* @return the number of pbufs that were deallocated
*/
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 (tcp_seg structure).
*
* @param seg single tcp_seg to free
* @return the number of pbufs that were deallocated
*/
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.
*
* @param pcb the tcp_pcb to manipulate
* @param prio new priority
*/
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.
* The pbuf and data are not copied, only the pointers
*
* @param seg the old tcp_seg
* @return a copy of seg
*/
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;
}
SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
return cseg;
}
#endif
#if LWIP_CALLBACK_API
/**
* Default receive callback that is called if the user didn't register
* a recv callback for the pcb.
*/
static err_t
tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
arg = arg;
if (p != NULL) {
pbuf_free(p);
} else if (err == ERR_OK) {
return tcp_close(pcb);
}
return ERR_OK;
}
#endif /* LWIP_CALLBACK_API */
/**
* Kills the oldest active connection that has lower priority than prio.
*
* @param prio minimum priority
*/
static void
tcp_kill_prio(u8_t prio)
{
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
u8_t mprio;
mprio = TCP_PRIO_MAX;
/* We kill the oldest active connection that has lower priority than prio. */
inactivity = 0;
inactive = NULL;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->prio <= prio &&
pcb->prio <= mprio &&
(u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
mprio = pcb->prio;
}
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
}
}
/**
* Kills the oldest connection that is in TIME_WAIT state.
* Called from tcp_alloc() if no more connections are available.
*/
static void
tcp_kill_timewait(void)
{
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
inactivity = 0;
inactive = NULL;
/* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
}
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -