There are essentially three cases: 1) The user initiates by telling
the TCP to CLOSE the connection 2) The remote TCP initiates by sending a
FIN control signal 3) Both users CLOSE simultaneously
/* If the TF_ACK_NOW flag is set and no data will be sent (either * because the ->unsent queue is empty or because the window does * not allow it), construct an empty ACK segment and send it. * * If data is to be sent, we will just piggyback the ACK (see below). */ if (pcb->flags & TF_ACK_NOW && (seg == NULL || lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { return tcp_send_empty_ack(pcb); }
#define IP_PCB \ /* ip addresses in network byte order */ \ ip_addr_t local_ip; \ ip_addr_t remote_ip; \ /* Socket options */ \ u8_t so_options; \ /* Type Of Service */ \ u8_t tos; \ /* Time To Live */ \ u8_t ttl \ /* link layer address resolution hint */ \ IP_PCB_ADDRHINT
本地端口:
1 2 3 4 5 6 7 8
/* members common to struct tcp_pcb and struct tcp_listen_pcb */ #define TCP_PCB_COMMON(type) \ type *next; /* for the linked list */ \ void *callback_arg; \ enum tcp_state state; /* TCP state */ \ u8_t prio; \ /* ports are in host byte order */ \ u16_t local_port
/* 服务端口号 */ u16_t remote_port; ... /* the rest of the fields are in host byte order as we have to do some math with them */ /* Timers */ u8_t polltmr, pollinterval; u8_t last_timer; /* 控制块最后一次被处理的时间 */ u32_t tmr; /* receiver variables */ u32_t rcv_nxt; /* 下一次期望收到的seqno */ tcpwnd_size_t rcv_wnd; /* 接收窗口大小 */ tcpwnd_size_t rcv_ann_wnd; /* 告知对方的接收窗大小 */ u32_t rcv_ann_right_edge; /* 通知窗口右边缘 */
/* sender variables *//* 发送方变量 */ u32_t snd_nxt; /* next new seqno to be sent */ u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last window update. */ u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ tcpwnd_size_t snd_wnd; /* sender window */ tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */ ... /* 保持空闲的控制变量 */ /* idle time before KEEPALIVE is sent */ u32_t keep_idle; #if LWIP_TCP_KEEPALIVE u32_t keep_intvl; u32_t keep_cnt; #endif/* LWIP_TCP_KEEPALIVE */
/** the TCP protocol control block for listening pcbs */ structtcp_pcb_listen { /** Common members of all PCB types */ IP_PCB; /** Protocol specific PCB members */ TCP_PCB_COMMON(struct tcp_pcb_listen);
#if LWIP_CALLBACK_API /* Function to call when a listener has been connected. */ tcp_accept_fn accept; #endif/* LWIP_CALLBACK_API */
/* The TCP PCB lists. */ /** List of all TCP PCBs bound but not yet (connected || listening) */ structtcp_pcb *tcp_bound_pcbs; /** List of all TCP PCBs in LISTEN state */ uniontcp_listen_pcbs_ttcp_listen_pcbs; /** List of all TCP PCBs that are in a state in which * they accept or send data. */ structtcp_pcb *tcp_active_pcbs; /** List of all TCP PCBs in TIME-WAIT state */ structtcp_pcb *tcp_tw_pcbs;
/* 将pbuf指针从tcp header指向data区 */ tcphdr_optlen = hdrlen_bytes - TCP_HLEN; tcphdr_opt2 = NULL; if (p->len >= hdrlen_bytes) { /* all options are in the first pbuf */ tcphdr_opt1len = tcphdr_optlen; pbuf_header(p, -(s16_t)hdrlen_bytes); /* cannot fail */ } else { u16_t opt2len; /* TCP header fits into first pbuf, options don't - data is in the next pbuf */ /* there must be a next pbuf, due to hdrlen_bytes sanity check above */ LWIP_ASSERT("p->next != NULL", p->next != NULL);
/* advance over the TCP header (cannot fail) */ pbuf_header(p, -TCP_HLEN);
/* determine how long the first and second parts of the options are */ tcphdr_opt1len = p->len; opt2len = tcphdr_optlen - tcphdr_opt1len;
/* options continue in the next pbuf: set p to zero length and hide the options in the next pbuf (adjusting p->tot_len) */ pbuf_header(p, -(s16_t)tcphdr_opt1len);
/* check that the options fit in the second pbuf */ if (opt2len > p->next->len) { /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len)); TCP_STATS_INC(tcp.lenerr); goto dropped; }
/* remember the pointer to the second part of the options */ tcphdr_opt2 = (u8_t*)p->next->payload;
/* advance p->next to point after the options, and manually adjust p->tot_len to keep it consistent with the changed p->next */ pbuf_header(p->next, -(s16_t)opt2len); p->tot_len -= opt2len;
/* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ prev = NULL;
/* 遍历活跃的tcp_active_pcbs寻找对应TCP控制块 */ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) && ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); if (prev != NULL) { prev->next = pcb->next; pcb->next = tcp_active_pcbs; tcp_active_pcbs = pcb; } else { TCP_STATS_INC(tcp.cachehit); } LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; } prev = pcb; }
if (pcb == NULL) { /* 如果没找到active连接,则检查TW(Time-Wait)状态连接 */ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) && ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { /* We don't really care enough to move this PCB to the front of the list since we are not very likely to receive that many segments for connections in TIME-WAIT. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); tcp_timewait_input(pcb); /* 有匹配的ip和端口 */ pbuf_free(p); return; } }
/* 若仍未找到,则在LISTENING态的网口中查找连接. */ prev = NULL; for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if (lpcb->local_port == tcphdr->dest) { if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) { /* found an ANY TYPE (IPv4/IPv6) match */ #if SO_REUSE lpcb_any = lpcb; lpcb_prev = prev; #else/* SO_REUSE */ break; #endif/* SO_REUSE */ } elseif (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) { if (ip_addr_cmp(&lpcb->local_ip, ip_current_dest_addr())) { /* found an exact match */ break; } elseif (ip_addr_isany(&lpcb->local_ip)) { /* found an ANY-match */ #if SO_REUSE lpcb_any = lpcb; lpcb_prev = prev; #else/* SO_REUSE */ break; #endif/* SO_REUSE */ } } } prev = (struct tcp_pcb *)lpcb; } #if SO_REUSE /* first try specific local IP */ if (lpcb == NULL) { /* only pass to ANY if no specific local IP has been found */ lpcb = lpcb_any; prev = lpcb_prev; } #endif/* SO_REUSE */ if (lpcb != NULL) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ if (prev != NULL) { ((struct tcp_pcb_listen *)prev)->next = lpcb->next; /* our successor is the remainder of the listening list */ lpcb->next = tcp_listen_pcbs.listen_pcbs; /* put this listening pcb at the head of the listening list */ tcp_listen_pcbs.listen_pcbs = lpcb; } else { TCP_STATS_INC(tcp.cachehit); }
if (pcb != NULL) { /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG tcp_debug_print_state(pcb->state); #endif/* TCP_INPUT_DEBUG */
/* Set up a tcp_seg structure. */ inseg.next = NULL; inseg.len = p->tot_len; inseg.p = p; inseg.tcphdr = tcphdr;
recv_data = NULL; recv_flags = 0; recv_acked = 0;
if (flags & TCP_PSH) { p->flags |= PBUF_FLAG_PUSH; }
/* If there is data which was previously "refused" by upper layer */ if (pcb->refused_data != NULL) { if ((tcp_process_refused_data(pcb) == ERR_ABRT) || ((pcb->refused_data != NULL) && (tcplen > 0))) { /* pcb has been aborted or refused data is still refused and the new segment contains data */ if (pcb->rcv_ann_wnd == 0) { /* this is a zero-window probe, we respond to it with current RCV.NXT and drop the data segment */ tcp_send_empty_ack(pcb); } TCP_STATS_INC(tcp.drop); MIB2_STATS_INC(mib2.tcpinerrs); goto aborted; } } tcp_input_pcb = pcb; err = tcp_process(pcb); /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. If so, we don't do anything. */ if (err != ERR_ABRT) { if (recv_flags & TF_RESET) { /* TF_RESET means that the connection was reset by the other end. We then call the error callback to inform the application that the connection is dead before we deallocate the PCB. */ TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST); tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { err = ERR_OK; /* If the application has registered a "sent" function to be called when new send buffer space is available, we call it now. */ if (recv_acked > 0) { u16_t acked16; #if LWIP_WND_SCALE /* recv_acked is u32_t but the sent callback only takes a u16_t, so we might have to call it multiple times. */ u32_t acked = recv_acked; while (acked > 0) { acked16 = (u16_t)LWIP_MIN(acked, 0xffffu); acked -= acked16; #else { acked16 = recv_acked; #endif TCP_EVENT_SENT(pcb, (u16_t)acked16, err); if (err == ERR_ABRT) { goto aborted; } } recv_acked = 0; } if (recv_flags & TF_CLOSED) { /* The connection has been closed and we will deallocate the PCB. */ if (!(pcb->flags & TF_RXCLOSED)) { /* Connection closed although the application has only shut down the tx side: call the PCB's err callback and indicate the closure to ensure the application doesn't continue using the PCB. */ TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD); } tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); goto aborted; } #if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE while (recv_data != NULL) { structpbuf *rest =NULL; pbuf_split_64k(recv_data, &rest); #else/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ if (recv_data != NULL) { #endif/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); if (pcb->flags & TF_RXCLOSED) { /* received data although already closed -> abort (send RST) to notify the remote host that not all data has been processed */ pbuf_free(recv_data); #if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE if (rest != NULL) { pbuf_free(rest); } #endif/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ tcp_abort(pcb); goto aborted; }
/* Notify application that data has been received. */ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); if (err == ERR_ABRT) { #if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE if (rest != NULL) { pbuf_free(rest); } #endif/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ goto aborted; }
/* If the upper layer can't receive this data, store it */ if (err != ERR_OK) { #if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE if (rest != NULL) { pbuf_cat(recv_data, rest); } #endif/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ pcb->refused_data = recv_data; LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); #if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE break; } else { /* Upper layer received the data, go on with the rest if > 64K */ recv_data = rest; #endif/* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ } }
/* If a FIN segment was received, we call the callback function with a NULL buffer to indicate EOF. */ if (recv_flags & TF_GOT_FIN) { if (pcb->refused_data != NULL) { /* Delay this if we have refused data. */ pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; } else { /* correct rcv_wnd as the application won't call tcp_recved() for the FIN's seqno */ if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) { pcb->rcv_wnd++; } TCP_EVENT_CLOSED(pcb, err); if (err == ERR_ABRT) { goto aborted; } } }
tcp_input_pcb = NULL; /* Try to send something out. */ tcp_output(pcb); #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif/* TCP_DEBUG */ #endif/* TCP_INPUT_DEBUG */ } } /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). Below this line, 'pcb' may not be dereferenced! */ aborted: tcp_input_pcb = NULL; recv_data = NULL;
/* give up our reference to inseg.p */ if (inseg.p != NULL) { pbuf_free(inseg.p); inseg.p = NULL; } } else {
/* If no matching PCB was found, send a TCP RST (reset) to the sender. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), tcphdr->dest, tcphdr->src); } pbuf_free(p); }