LWIP中tcp_in.c下tcp_process函数。
学习《嵌入式网络那些事lwip协议 深度剖析与实战演练》过程中
对tcp_process这个函数的理解
连接建立过程中的握手分为三个报文分别为①② ③
连接断开过程中的握手分为④ ⑤ ⑥ ⑦
此外还有双方同时关闭的情况需要考虑
函数前面部分属于对输入的报文进行判断,是否合法,是否为存在RST标志置位,并判断收到的握手包为超时重发
static err_t
tcp_process(struct tcp_pcb *pcb)
{
struct tcp_seg *rseg;
u8_t acceptable = 0;
err_t err;
err = ERR_OK;
/* Process incoming RST segments. */
if (flags & TCP_RST) {
/* First, determine if the reset is acceptable. */
if (pcb->state == SYN_SENT) {
if (ackno == pcb->snd_nxt) {
acceptable = 1;
}
} else {
if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
pcb->rcv_nxt+pcb->rcv_wnd)) {
acceptable = 1;
}
}
if (acceptable) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
recv_flags |= TF_RESET;
pcb->flags &= ~TF_ACK_DELAY;
return ERR_RST;
} else {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
seqno, pcb->rcv_nxt));
LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
seqno, pcb->rcv_nxt));
return ERR_OK;
}
}
if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
/* Cope with new connection attempt after remote end crashed */
tcp_ack_now(pcb);
return ERR_OK;
}
if ((pcb->flags & TF_RXCLOSED) == 0) {
/* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
pcb->tmr = tcp_ticks;
}
pcb->keep_cnt_sent = 0;
tcp_parseopt(pcb);
switch (pcb->state)
下面是实现TCP状态机的具体部分
case SYN_SENT:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
/* received SYN ACK with expected sequence number? */
if ((flags & TCP_ACK) && (flags & TCP_SYN)
&& ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
pcb->snd_buf++;
pcb->rcv_nxt = seqno + 1;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->lastack = ackno;
pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wnd_max = tcphdr->wnd;
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
/* Set ssthresh again after changing pcb->mss (already set in tcp_connect
* but for the default value of pcb->mss) */
pcb->ssthresh = pcb->mss * 10;
pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
--pcb->snd_queuelen;
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
rseg = pcb->unacked;
pcb->unacked = rseg->next;
tcp_seg_free(rseg);
/* If there's nothing left to acknowledge, stop the retransmit
timer, otherwise reset it to start again */
if(pcb->unacked == NULL)
pcb->rtime = -1;
else {
pcb->rtime = 0;
pcb->nrtx = 0;
}
/* Call the user specified function to call when sucessfully
* connected. */
TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
if (err == ERR_ABRT) {
return ERR_ABRT;
}
tcp_ack_now(pcb);
}
/* received ACK? possibly a half-open connection */
else if (flags & TCP_ACK) {
/* send a RST to bring the other side in a non-synchronized state. */
tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
tcphdr->dest, tcphdr->src);
}
break;
SYN_SENT (此状态是客户端发送完SYN握手请求后的状态,等待SYN+ACK)
一、如果收到②SYN+ACK,且对确认序号ackno进行校验,符合预期。设置出口参数,
控制块进入ESTABLISHED状态,对控制块中unacked进行更新。调用connected函数(回调用tcp_enqueue_flags将握手报文段放到TCP控制块的unsent队列上。调用tcp_output(会调用tcp_output_segment判断报文是否在允许的发送窗口内,并将报文段从unsent转移到unacked队列上))发送ACK,结束三次握手。
二、如果收到的是ACK,返回一个RST
case SYN_RCVD:
if (flags & TCP_ACK) {
/* expected ACK number? */
if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
u16_t old_cwnd;
pcb->state = ESTABLISHED;
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API
LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
#endif
/* Call the accept function. */
TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
if (err != ERR_OK) {
/* If the accept function returns with an error, we abort
* the connection. */
/* Already aborted? */
if (err != ERR_ABRT) {
tcp_abort(pcb);
}
return ERR_ABRT;
}
old_cwnd = pcb->cwnd;
/* If there was any data contained within this ACK,
* we'd better pass it on to the application as well. */
tcp_receive(pcb);
/* Prevent ACK for SYN to generate a sent event */
if (pcb->acked != 0) {
pcb->acked--;
}
pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
if (recv_flags & TF_GOT_FIN) {
tcp_ack_now(pcb);
pcb->state = CLOSE_WAIT;
}
} else {
/* incorrect ACK number, send RST */
tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
tcphdr->dest, tcphdr->src);
}
} else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
/* Looks like another copy of the SYN - retransmit our SYN-ACK */
tcp_rexmit(pcb);
}
break;
SYN_RCVD(此状态是服务器发送完SYN+ACK后的状态,等待ACK)
一、 如果收到③ACK,且确认序号ackno合法
控制块进入ESTABLISHED状态,开始接收数据。调用用户的accept函数(调用出错就关闭当前连接,返回出错),保持旧的阻塞窗口,调用函数tcp_receive处理报文中的数据,同时当本地的unacked被报文中的ACK确认时,重新设置阻塞窗口。
数据传输过程中,当接收到④FIN握手,响应对方的FIN握手,并回复一个ACK⑤,并且控制块进入CLOSE_WAIT状态。
二、 如果收到①SYN握手报文,说明自己发的②SYN+ACK丢失,重传②SYN+ACK
case CLOSE_WAIT:
/* FALLTHROUGH */
CLOSE_WAIT (此状态是服务器处于半关闭状态)
case ESTABLISHED:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) { /* passive close */
tcp_ack_now(pcb);
pcb->state = CLOSE_WAIT;
}
break;
ESTABLISHED (握手建立后,传输数据的过程中)
一、接收到④FIN握手,响应对方的FIN握手,并回复一个ACK⑤,并且控制块进入CLOSE_WAIT状态。
case FIN_WAIT_1:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) {
if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
LWIP_DEBUGF(TCP_DEBUG,
("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
} else {
tcp_ack_now(pcb);
pcb->state = CLOSING;
}
} else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
pcb->state = FIN_WAIT_2;
}
break;
FIN_WAIT1 (上层应用调用tcp_close函数,并发送FIN后的状态)
继续调用tcp_receive函数处理报文中的数据
一、 收到一个⑥FIN握手,且包含有效ACK。则响应此FIN握手,回复一个ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中
收到的这个FIN握手也可能是④,即双方同时执行关闭操作,则此时返回ACK,进入CLOSING状态
二、 收到一个只收到⑤ACK,则进入FIN_WAIT2状态
case FIN_WAIT_2:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
}
break;
FIN_WAIT2 (主动关闭,发送FIN握手且接受到ACK后的状态)
继续调用tcp_receive函数处理报文中的数据
一、收到一个⑥FIN握手,则响应此FIN握手,回复一个ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中
case CLOSING:
tcp_receive(pcb);
if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
}
break;
CLOSING (双方同时执行主动关闭后的状态)
继续调用tcp_receive函数处理报文中的数据
当收到有效ACK,开始消除连接中的所有数据。控制块从tcp_active_pcb链表中删除,控制块进入TIME_WAIT状态,控制块加入到tcp_tw_pcbs链表中
case LAST_ACK:
tcp_receive(pcb);
if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
/* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
recv_flags |= TF_CLOSED;
}
break;
default:
break;
LAST_ACK
继续调用tcp_receive函数处理报文中的数据
当收到有效ACK,设置recv_flags为TF_CLOSED,返回tcp_input对该控制块进行释放和清除工作。