首先解释一段截取的网络数据认识一下。(由a_la_lei解释)
1、-> syn(这一步是初始化发送端的ISN。理论上,它的数据字段没有任何值,消耗的是一个虚字节)
TCP: Sequence number = 4071231308
TCP: Acknowledgement number = 0
2、<- ack syn(初始化接收端的ISN,并对收到的包进行确认。因为确认的方式是累计确认,所以尽管第1步传输了一个虚字节,但ACK仍旧是4071231308+1=4071231309)
TCP: Sequence number = 1191340143
TCP: Acknowledgement number = 4071231309
3、-> ack(回应第2步的确认。因为第1步消耗的是一个虚字节,所以理论上Seq应该仍旧是4071231308,但由于协议的具体实现略有不同,这里又在虚字节上加1变成4071231309。仍旧是累计确认。)
TCP: Sequence number = 4071231309
TCP: Acknowledgement number = 1191340144
4、-> ack push 登录信息(载荷包。因为第3步仅是一个确认包,它没有包含任何有效数据,所以这里的Seq仍旧是4071231309。仍旧是对第2步确认)
TCP: Sequence number = 4071231309
TCP: Acknowledgement number = 1191340144
5、<- ack(仅仅是一个确认包。仍旧是根据累计确认原则对第4步确认,ACK等于4071231309+有效载荷=4071231337)
TCP: Sequence number = 1191340144
TCP: Acknowledgement number = 4071231337
6、<- ack push 回应信息(载荷包。因为第5步仅仅是一个确认包,所以这里不消耗任何序号,Seq仍旧是1191340144。ACK仍旧是对第4步的确认)
TCP: Sequence number = 1191340144
TCP: Acknowledgement number = 4071231337
7、-> ack(仅仅是一个确认包。仍旧是根据累计确认原则对第6步确认,ACK等于1191340144+有效载荷=1191340217)
TCP: Sequence number = 4071231337
TCP: Acknowledgement number = 1191340217
8、<- ack push 第二次回应信息(载荷包。因为仍旧有数据要发送,按第7步给予的ACK来设置此包的Seq。此包的Ack仍旧是4071231337)
TCP: Sequence number = 1191340217
TCP: Acknowledgement number = 4071231337
本流程需要知道的几个规则:
规则1、累计确认。接收端对收到的载荷包(数据字段有值的TCP包),回应一个确认包,确认号是所收到包的TCP数据字段最后一个字节+1。
规则2、捎带确认。载荷包必须捎带确认字段。这样可以减少网络流量。
规则3、虚字节。有些数据包(ACK)不携带任何数据,所以不消耗序列号,也就是说仍旧保持不变。
uip中,序号的操作流程
三次握手:第一次 第二次 第三次
seqno:1713796940 2957476497 1713796941
ackno:0 1713796941 2957476498
SYN = 1 ACK = 0 SYN = 1 ACK = 1 SYN = 0 ACK = 1
seqno:32位序号,表示在这个报文段中的的第一个数据字节。:我发送的数据段第一个字节的序列号是。
ackno:32位确认序号包含发送确认的一端所期望收到的下一个序号。:我希望下次接收到的数据段第一个字节的序列号。
u8_t snd_nxt[4]; // 下一次发送的起始字节的序列号
u8_t rcv_nxt[4]; // 上一次接收起始字节的序列号
u8_t iss /TCP初始化序列号
u8_t uip_acc32[4]; /临时变量无意义/
u8_t BUF->seqno[1]...[4]; //TCP里的32位序号
u8_t BUF->ackno[1]...[4]; //TCP里的32位确认序号
void uip_add_rcv_nxt(u16_t n) // 功能:(u32_t)rcv_nxt = (u32_t)rcv_nxt + n
{}
1.主动链接中的应用:
uip_connect() // 主动发起链接函数
{
conn->snd_nxt[0] = iss[0]; // 用于第一次握手的seqno
conn->snd_nxt[1] = iss[1];
conn->snd_nxt[2] = iss[2];
conn->snd_nxt[3] = iss[3];
}
found:中主动链接部分
if((BUF->flags & TCP_ACK) && uip_outstanding(uip_connr)) { // 收到了ACK帧,同时有待确认的数据;
uip_add32(uip_connr->snd_nxt, uip_connr->len); // 计算下一次发送的第一个字节的序列号
// uip_connr->len 用于记录本次发送的字节数
// 在第三次握手中uip_connr->len = 1
if(BUF->ackno[0] == uip_acc32[0] && BUF->ackno[1] == uip_acc32[1] &&
BUF->ackno[2] == uip_acc32[2] && BUF->ackno[3] == uip_acc32[3]) {
uip_connr->snd_nxt[0] = uip_acc32[0];
uip_connr->snd_nxt[1] = uip_acc32[1];
uip_connr->snd_nxt[2] = uip_acc32[2];
uip_connr->snd_nxt[3] = uip_acc32[3];
}
}
uip_connr->rcv_nxt[0] = BUF->seqno[0]; // 用于第三次握手的ackno,将收到的
uip_connr->rcv_nxt[1] = BUF->seqno[1]; // 第二次握手中的seqno+1赋值给第三
uip_connr->rcv_nxt[2] = BUF->seqno[2]; // 次握手中的ackno
uip_connr->rcv_nxt[3] = BUF->seqno[3];
uip_add_rcv_nxt(1); // uip_connr->rcv_nxt[3] = uip_connr->rcv_nxt[3]+1
2.被动连接中应用:
found_listen:
uip_connr->tcpstateflags = SYN_RCVD;
uip_connr->snd_nxt[0] = iss[0]; // 第二次握手用的seqno赋值,该值是有咱定。
uip_connr->snd_nxt[1] = iss[1];
uip_connr->snd_nxt[2] = iss[2];
uip_connr->snd_nxt[3] = iss[3];
/* rcv_nxt should be the seqno from the incoming packet + 1. */
uip_connr->rcv_nxt[3] = BUF->seqno[3]; // 将对方第一次握手发送的seqno+1赋值给
uip_connr->rcv_nxt[2] = BUF->seqno[2]; // 第二次握手用的ackno
uip_connr->rcv_nxt[1] = BUF->seqno[1];
uip_connr->rcv_nxt[0] = BUF->seqno[0];
uip_add_rcv_nxt(1);
3.平时交互信息中的应用:
计算seqno的值跟主动链接中的应用-found:中主动链接部分代码重合,不在多说。
计算ackno:
uip_add_rcv_nxt(1 + uip_len); // uip_connr->rcv_nxt+1+uip_len,其中uip_len是接收到的数据长度。
最后都要应用:
BUF->ackno[0] = uip_connr->rcv_nxt[0];
BUF->ackno[1] = uip_connr->rcv_nxt[1];
BUF->ackno[2] = uip_connr->rcv_nxt[2];
BUF->ackno[3] = uip_connr->rcv_nxt[3];
BUF->seqno[0] = uip_connr->snd_nxt[0];
BUF->seqno[1] = uip_connr->snd_nxt[1];
BUF->seqno[2] = uip_connr->snd_nxt[2];
BUF->seqno[3] = uip_connr->snd_nxt[3];
写入序号值。
====
http://blog.21ic.com/user1/4861/archives/2009/59995.html