15、 tcp_output 与重传
15.1 发送 segment 有关的两个队列:
z pcb->unsent,待发送 segment, tcp_enqueue()将待发送数据分割乘 segment 并放入该队列
z pcb->unacked,已发送待确认 segment, tcp_output()从该队列中取 segment,如果满足发送条件则调用
tcp_output_segment()发送该 segment。
15.2 这两个队列的其他用户
z tcp_rexmit_rto,超时重传时将 pcb->unacked 中所有的 segment 移动到到 pcb->unsent 的前面,最后调
用 tcp_output。
z tcp_rexmit,快速重传时将 pcb->unacked 第一个 segment 移动到到 pcb->unsent 的前面,最后调用
tcp_output。
15.3 tcp_output 的流程
err_t tcp_output(struct tcp_pcb *pcb)
{
struct pbuf *p;
struct tcp_hdr *tcphdr;
struct tcp_seg *seg, *useg;
u32_t wnd;
#if TCP_CWND_DEBUG
s16_t i = 0;
#endif /* TCP_CWND_DEBUG */
/* First, check if we are invoked by the TCP input processing
code. If so, we do not output anything. Instead, we rely on the
input processing code to call us when input processing is done
with. */
if (tcp_input_pcb == pcb) {
return ERR_OK;
}
wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); /* 当前窗口取拥塞窗口与对方的通告窗口的最小值 */
seg = pcb->unsent;
/* useg should point to last segment on unacked queue */
useg = pcb->unacked;
if (useg != NULL) {
for (; useg->next != NULL; useg = useg->next);
}
......
if (pcb->flags & TF_ACK_NOW &&
(seg == NULL ||
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
/* 前面已经叙述过:立刻发送 ACK */
}
......
/* 在窗口允许的前提下,尽可能地发送 segment */
while (seg != NULL &&
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
pcb->unsent = seg->next;
if (pcb->state != SYN_SENT) { /* 前面已经叙述过: ACK 与数据一起发送 */
TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
tcp_output_segment(seg, pcb); /* 发送 segment */
pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); /* next seqno to be sent */
if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
pcb->snd_max = pcb->snd_nxt; /* Highest seqno sent. */
}
/* put segment on unacknowledged list if length > 0 */
if (TCP_TCPLEN(seg) > 0) {
seg->next = NULL;
/* unacked list is empty? */
if (pcb->unacked == NULL) {
pcb->unacked = seg;
useg = seg;
/* unacked list is not empty? */
} else {
/* In the case of fast retransmit, the packet should not go to the tail
* of the unacked queue, but rather at the head. We need to check for
* this case. -STJ Jul 27, 2004 */
if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){
/* add segment to head of unacked list */
seg->next = pcb->unacked;
pcb->unacked = seg;
} else {
/* add segment to tail of unacked list */
useg->next = seg;
useg = useg->next;
}
}
/* do not queue empty segments on the unacked list */
} else {
tcp_seg_free(seg);
}
seg = pcb->unsent;
}
return ERR_OK;
}
16、内核函数 do_connect
static void do_connect(struct api_msg_msg *msg)
{
if (msg->conn->pcb.tcp == NULL) {
switch (msg->conn->type) {
......
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_new();
if (msg->conn->pcb.tcp == NULL) {
msg->conn->err = ERR_MEM;
sys_mbox_post(msg->conn->mbox, NULL);
return;
}
default:
break;
}
}
switch (msg->conn->type) {
........
case NETCONN_TCP:
setup_tcp(msg->conn);
tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port,
do_connected);
default:
break;
}
} |