面试准备之TCP/IP

算是总结一下知识点吧,感觉自己手写总结会影响非常深刻:

三次握手

1)发起端sync 发送 seq 为x

2)回应端发送sync seq为y, ack x+1

3)  发起端发送ack y+1

四次结束

为啥要4次?

每一端都要分别发FIN来表明自己发送结束了,对方也需要来个回应表明收到了FIN, 如果出现如下情况,例如发起端发了FIN,回应端发ACK同时FIN(正好回应端也没有数据可以发送了),那么我想结束也可以是3次,不过这种情况一般不会出现吧。

结束时主动关闭和被动关闭啥会有不同的状态机?

主动发送FIN的那一方会进入FIN_wait1/2,还有个Timewait, 这是因为主动发送端最后一个ACK不确定对方是否能收到,万一对端没有收到ACK,那么对端会再次发一个FIN,这样主动端还能够有机会再来一个ACK

其实更复杂的事情会在后面,网络机器突然挂了,错误怎样处理?

http://blog.csdn.net/uestczshen/article/details/53995983


面试准备之TCP/IP


服务端socket程序如果主动被干掉,那么进入主动关闭阶段,那么就会产生time_wait,产生的结果就会使无法短时间内再次用此端口。

下面这个博客很详细的写了出错时的一些情况:

http://www.cnblogs.com/wanpengcoder/category/803651.html

客户connect 主动连接sync超时

三次连接成功后,客户端紧接着发送了rst,导致服务端正准备accep(从三次握手成功队列里面取出一个来处理)出错,如果accept是柱塞模式就惨了

服务端主动kill自己,服务端进入主动关闭模式,导致timewait发生,一段时间内无法再次用此端口 SO_REUSEADDR
这个选项是否可以让端口马上可以使用需要确认,socket选项

服务端shutdown,客户端重发,超时后关闭,如果服务端重启成功后发现客户端还在发,发送rst

客户端和服务端中间网络出现问题,超时重传后关闭


原始套接字拥有更多*度:读写icmp/igmp,可以处理协议栈不支持的协议

Nagle算法:发一个报文必须给回一个

ACK延时:定时器启动延时发送ACK,捎带下次数据一起发送

上面两个方法冲突:出现write write read时第二个write由于Nagel算法会不发送,而对端却又延迟对第一个write的ack,直到ack定时器失效

糊涂窗口综合症

connect 和 accept 阻塞和非阻塞,connect调用能开启sync,非塞模式提高效率,不用等三次握手,调用accept从listen队列里面提取已经三次握手成功的,如果此时client端又发送了rst,那么阻塞模式会被阻塞,也不知道真的假的

创建socket 后,调用connect 开始发送sync,调用listen 直接导致socket进入listen

int listen(int sockfd, int backlog);

面试准备之TCP/IP

connect: 调用后进入sync-send状态,

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

不一定要bind源地址,内核可以指定

connect 失败要重新建立新的

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

mss 值是在sync报文中携带的,表示最大支持大小,一般设置为mtu-40=1460在以太网里面

tcp套接字选项:

(1) TCP_MAXSEG:

本选项允许我们获取或者设置TCP连接的最大分节大小(MSS)。已建立连接返回的是对端使用syn通知的MSS值,否则是未从对端收到MSS情况下使用的默认值;

(2) TCP_NODELAY:
开启本选项将禁止TCP的Nagle算法



三、TCP 数据包的编号(SEQ)

一个包1400字节,那么一次性发送大量数据,就必须分成多个包。比如,一个 10MB 的文件,需要发送7100多个包。

发送的时候,TCP 协议为每个包编号(sequence number,简称 SEQ),以便接收的一方按照顺序还原。万一发生丢包,也可以知道丢失的是哪一个包。

第一个包的编号是一个随机数。为了便于理解,这里就把它称为1号包。假定这个包的负载长度是100字节,那么可以推算出下一个包的编号应该是101。这就是说,每个数据包都可以得到两个编号:自身的编号,以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。

面试准备之TCP/IP

(图片说明:当前包的编号是45943,下一个数据包的编号是46183,由此可知,这个包的负载是240字节。)

四、TCP 数据包的组装

收到 TCP 数据包以后,组装还原是操作系统完成的。应用程序不会直接处理 TCP 数据包。

对于应用程序来说,不用关心数据通信的细节。除非线路异常,收到的总是完整的数据。应用程序需要的数据放在 TCP 数据包里面,有自己的格式(比如 HTTP 协议)。

TCP 并没有提供任何机制,表示原始文件的大小,这由应用层的协议来规定。比如,HTTP 协议就有一个头信息Content-Length,表示信息体的大小。对于操作系统来说,就是持续地接收 TCP 数据包,将它们按照顺序组装好,一个包都不少。

操作系统不会去处理 TCP 数据包里面的数据。一旦组装好 TCP 数据包,就把它们转交给应用程序。TCP 数据包里面有一个端口(port)参数,就是用来指定转交给监听该端口的应用程序。

面试准备之TCP/IP

(图片说明:系统根据 TCP 数据包里面的端口,将组装好的数据转交给相应的应用程序。上图中,21端口是 FTP 服务器,25端口是 SMTP 服务,80端口是 Web 服务器。)

应用程序收到组装好的原始数据,以浏览器为例,就会根据 HTTP 协议的Content-Length字段正确读出一段段的数据。这也意味着,一次 TCP 通信可以包括多个 HTTP 通信。

五、慢启动和 ACK

服务器发送数据包,当然越快越好,最好一次性全发出去。但是,发得太快,就有可能丢包。带宽小、路由器过热、缓存溢出等许多因素都会导致丢包。线路不好的话,发得越快,丢得越多。

最理想的状态是,在线路允许的情况下,达到最高速率。但是我们怎么知道,对方线路的理想速率是多少呢?答案就是慢慢试。

TCP 协议为了做到效率与可靠性的统一,设计了一个慢启动(slow start)机制。开始的时候,发送得较慢,然后根据丢包的情况,调整速率:如果不丢包,就加快发送速度;如果丢包,就降低发送速度。

Linux 内核里面设定了(常量TCP_INIT_CWND),刚开始通信的时候,发送方一次性发送10个数据包,即"发送窗口"的大小为10。然后停下来,等待接收方的确认,再继续发送。

默认情况下,接收方每收到两个 TCP 数据包,就要发送一个确认消息。"确认"的英语是 acknowledgement,所以这个确认消息就简称 ACK。

ACK 携带两个信息。

  • 期待要收到下一个数据包的编号
  • 接收方的接收窗口的剩余容量

发送方有了这两个信息,再加上自己已经发出的数据包的最新编号,就会推测出接收方大概的接收速度,从而降低或增加发送速率。这被称为"发送窗口",这个窗口的大小是可变的。

面试准备之TCP/IP

(图片说明:每个 ACK 都带有下一个数据包的编号,以及接收窗口的剩余容量。双方都会发送 ACK。)

注意,由于 TCP 通信是双向的,所以双方都需要发送 ACK。两方的窗口大小,很可能是不一样的。而且 ACK 只是很简单的几个字段,通常与数据合并在一个数据包里面发送。

面试准备之TCP/IP

(图片说明:上图一共4次通信。第一次通信,A 主机发给B 主机的数据包编号是1,长度是100字节,因此第二次通信 B 主机的 ACK 编号是 1 + 100 = 101,第三次通信 A 主机的数据包编号也是 101。同理,第二次通信 B 主机发给 A 主机的数据包编号是1,长度是200字节,因此第三次通信 A 主机的 ACK 是201,第四次通信 B 主机的数据包编号也是201。)

即使对于带宽很大、线路很好的连接,TCP 也总是从10个数据包开始慢慢试,过了一段时间以后,才达到最高的传输速率。这就是 TCP 的慢启动。

六、数据包的遗失处理

TCP 协议可以保证数据通信的完整性,这是怎么做到的?

前面说过,每一个数据包都带有下一个数据包的编号。如果下一个数据包没有收到,那么 ACK 的编号就不会发生变化。

举例来说,现在收到了4号包,但是没有收到5号包。ACK 就会记录,期待收到5号包。过了一段时间,5号包收到了,那么下一轮 ACK 会更新编号。如果5号包还是没收到,但是收到了6号包或7号包,那么 ACK 里面的编号不会变化,总是显示5号包。这会导致大量重复内容的 ACK。

如果发送方发现收到三个连续的重复 ACK,或者超时了还没有收到任何 ACK,就会确认丢包,即5号包遗失了,从而再次发送这个包。通过这种机制,TCP 保证了不会有数据包丢失。

面试准备之TCP/IP

(图片说明:Host B 没有收到100号数据包,会连续发出相同的 ACK,触发 Host A 重发100号数据包。)