1. 使用场景
在一大流量网络中心旁路抓包,不能完全排除乱序或丢包的可能
2. Review代码
2.1 窗口检验部分代码
if (
! ( !datalen && ntohl(this_tcphdr->th_seq) == rcv->ack_seq )
&& /* zero window handle */
( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) ||
before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq)
)
) {
return;
}
上面的这串代码有很大问题,如果TCP正常交互可能会没太大的问题,但一旦有异常就会造成重组后的流不完整,甚至无法重组。
2.2 四次挥手部分代码
if ((this_tcphdr->th_flags & TH_ACK)) {
handle_ack(snd, ntohl(this_tcphdr->th_ack));
if (rcv->state == FIN_SENT)
rcv->state = FIN_CONFIRMED;
if ((rcv->state == FIN_CONFIRMED && snd->state == FIN_CONFIRMED) ||
(rcv->state == FIN_CONFIRMED && snd->state == TCP_CLOSING)) {
struct lurker_node *i;
a_tcp->nids_state = NIDS_CLOSE;
for (i = a_tcp->listeners; i; i = i->next)
(i->item) (a_tcp, &i->data);
nids_free_tcp_stream(tcp, a_tcp);
return;
}
}
上面这部分代码是处理 TCP关闭的,需要四次正确挥手才能走到这个逻辑里,如果在一方出现异常,会在他处将其中的状态置为 TCP_CLOSING
所以在这还需要加入这个判断。
还有个问题,Android 系统部分APP使用的是三次挥手,此时还需在process_tcp
后面对三次挥手的进行判断,一时出现三次挥手即可关闭TCP,无需等待最后一个ack
。
2.2 内存增长
在TCP会造成内存快速增长的原因大致如下
* 如有丢包情况造成大量缓存包,如迟迟未补上所需报文将会愈演愈烈
* 上层数据要对 IP 数据包进行检验,看是否有IP分片,如果你的业务是基于TCP重组后的数据的话,请一定要做IP分片对系统影响的测试,否则一条TCP流就会被丢弃,64K的缓冲数据也会造成内存增加
* TCP 本身具有结点池,但其本身的池会按照分配的时间进行老化,建议在每个TCP流来新包的时候更新下顺序,只需要将当前结点取出放置在最前端即可(TCP结点为双向链表,效率不是问题)强烈建议
3. 总结
先写这么多,这里还有好多需要待完善,关键要看你的运用场景,对TCP重组有多大的要求,是否允许丢包等等。在现行情况下,技术是为业务服务的,所以一定想清你的业务需要怎样的TCP流。