1. 建立tcp连接时,server端使用的端口号和listen port的关系
tcp连接是一个4元组:[clientIP, clientPort, serverIP, serverPort],四个元素中有一个元素不同,就是一个不同的tcp连接。以ssh为例,监听在22端口,不同的client连接过来,四元组中server部分都是[server_ip, 22]
>>>
tcp 0 0 192.168.66.154:22 192.168.66.78:45638 ESTABLISHED
tcp 0 52 192.168.66.154:22 192.168.65.57:57719 ESTABLISHED
>>>

2. 防火墙只允许listen port上的数据经过,会对(1)中的情况有什么影响
如果防火墙只允许22端口通过,由于(1)中的结论,不会有影响。

3. tcp阻塞式的send()有没有超时,如何设置超时时间
"man tcp"文档里面 TCP_USER_TIMEOUT (since Linux 2.6.37) 项写道:
failure may take up to 20 minutes with the current system defaults in a normal WAN environment
这个时间为数据包被发送后未接收到ACK确认的最大时长,从这里看出,默认的send超时为20分钟。
额外的,keepalive相关的配置:
tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
The number of seconds between TCP keep-alive probes.
tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no response is obtained from the other end.
tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes.
默认时间超长。
另外,阻塞式的recv()默认没有时间限制,会一直等待。
需要注意的点:线路忙碌(丢包)和对方一直不recv消息是两回事(参考https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/)
一直不recv消息是不会触发send()超时的。

4. 如果阻塞在send()时,把网络断了,会有什么影响
应该不会有影响,因为tcp是不知道网络断了的,除非有额外的心跳协议,程序会一直阻塞在send()直到超时。

5. tcp发送缓冲区大小和tx buffer的关系
大部分网卡都支持多队列模式,每个队列在内存中会有一个对应的TX/RX环形缓冲区,而且这个TX/RX是可以被DMA操控的。发送数据时,先从用户缓冲区拷贝到内核skb缓冲区。然后经过一系列处理(可能会有拷贝发生),再通过DMA映射,把skb地址映射到TX队列中,然后由网卡发送出去。

6. tcp什么时候产生RST
RST表示复位,用来异常的关闭连接,区别于正常的四次握手关闭。
(1) 目的地为某端口的SYN到达,然而在该端口上并没有正在监听的服务器。没有listen,但是收到连接SYN请求
(2) TCP想取消一个已有连接。正常情况FIN会在缓冲区数据发送完之后再发送,通过套接字选项SO_LINGER的数值设置为0,可以直接丢弃数据,并发送RST给对方。
(3) TCP接收到一个根本不存在的连接上的分节。
引申情况:
SIGPIPE信号:连接建立,若某一端关闭连接,而另一端仍然向它写数据,第一次写数据后会收到RST响应,此后再写数据,内核将向进程发出SIGPIPE信号,通知进程此连接已经断开。而SIGPIPE信号的默认处理是终止程序

7. 测试tcp多个client端绑定同一个port,但是四元组不同
应用场景,某台机器防火墙只允许一个端口收发进出数据,需要从一个端口对外发起多个连接。
通过设置SO_REUSEADDR选项实现,以下只针对tcp,udp没测过:
(a)当local address被一个处于listen状态的socket使用时,加上该参数也不能重用这个地址(地址=[ip, port])
(b)两个进程可以绑定相同的本地地址,只要四元组不同就可以。
引申情况:
TCP的”TIME_WAIT“状态 ---- 持续大约2MSL(60秒),为了进一步提高tcp的可靠性。
存在理由有:
(1) 可靠地实现TCP全双工连接的终止。如果最后的ACK丢失,对方重传FIN,如果不维持这个状态信息,那么客户端将响应RST分节,对方将此分节解释成一个错误。
(2) 允许老的重复分节在网络中消逝。有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身(这个概率非常小,因为新连接的起始序列号是随机的)。
可以看出TIME_WAIT状态单纯是为了增加可靠性。大多数时候,在TIME_WAIT状态内,程序都是无事可做,只有少数时候会收到对方重发的FIN然后返回ACK。如果对网络和程序有信息,完全可以使用SO_REUSEADDR选项忽略TIME_WAIT,是的新的连接直接使用已关闭的端口。
时间长度选择2MSL,也是考虑到一个网络包来回的最长时间,1个网络包能存活1MSL时间,来回就是2MSL。超过这个时间,基本就能假设没有丢包。为什么不是更长呢?因为没法做到100%,只能选一个相对合理的时长。

8. 路由器限流原理以及方法
一般都是直接把包丢弃,有的是在ip层,有的是tcp层,也有直接链路层。

9. udp调用sendto函数会不会有报错的情况
如果写入过快,会有error,但是具体没测试,可能会阻塞,也可能返回EWOULDBLOCK/EAGAIN。
本质上对数据发送来说,是对协议透明的,因为缓冲区是有限的,所以只要数据写入速度大于网卡发送速度,缓冲区迟早会满。缓冲区满了之后的处理由操作系统决定。而tcp重传这些,也是内核的处理,不会阻碍缓冲区填满。

10. 网卡收到数据,但是不及时取走,会怎么样?
当 NIC 把数据包通过 DMA 复制到内核缓冲区 sk_buffer 后,NIC 立即发起一个硬件中断。CPU 接收后,首先进入上半部分,网卡中断对应的中断处理程序是网卡驱动程序的一部分,之后由它发起软中断,进入下半部分,开始消费 sk_buffer 中的数据,交给内核协议栈处理。
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC 接收到的数据包无法及时写到 sk_buffer,就会产生堆积,当 NIC 内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为 rx_fifo_errors,在 /proc/net/dev 中体现为 fifo 字段增长,在 ifconfig 中体现为 overruns 指标增长。
(内核通常需要快速的拷贝网络数据包到系统内存,因为网卡上接收网络数据包的缓存大小固定,而且相比系统内存也要小得多。所以上述拷贝动作一旦被延迟,必然造成网卡FIFO缓存溢出 - 进入的数据包占满了网卡的缓存,后续的包只能被丢弃,这也应该就是ifconfig里的overrun的来源。)

posted on 2021-07-09 16:30  SimbaStar  阅读(625)  评论(0编辑  收藏  举报