深信服面试
1. 代码覆盖率统计方法
-
在gcc编译代码时增加参数gcov
-
使用gtest写ut用例,构造不同的参数,争取跑到函数中每个分支
-
使用gcov还原代码执行过程,生成代码执行分析文件.html
2. 对于函数中难以测试的分支,如何单元测试
- 使用gdb在运行时改变参数值,使各个分支都能执行到
3. 自旋锁、互斥锁、读写锁实现原理
4. 如何使用互斥锁api实现读写锁
5. c++一些实现原理
6. 访问百度过程中使用到哪些协议
应用层协议
-
tcp三次握手建立连接
-
tls 4次握手确定加密算法、随机数、hash算法
-
接下来就用加密算法对数据加密后,再在客户端、服务端通信
传输层
- tcp协议
网络层
- ip协议
链路层
- 以太网协议
7. 以太网协议头组成、长度、mtu
-
协议头: 目的mac,源mac,帧类型
-
长度:14bytes
-
mtu: 1500bytes
8. tcp滑动窗口原理
9. send函数返回值在阻塞、非阻塞情况下区别
tcp协议本身可靠,并不等于应用程序用tcp发送数据就一定可靠;send发送的大小,并不代表对端recv到多少数据
- 使用
sysctl -a | grep net.ipv4.tcp_wmem
可以查看系统默认的发送缓存大小,net.ipv4_tcp_wmem = 4096(最少字节数) 16384(默认值,会被net.core.wmwm_default覆盖) 81920(最大字节数,会被net.core.wmem_max覆盖)
阻塞模式
send函数是将应用程序请求发送的数据拷贝到发送缓存中
-
如果发送缓存大小比请求发送大小要大,那么send函数立即返回,同时向网络中发送数据
-
否则会等待接收端对之前发送数据的确认,以便腾出缓存空间容纳新的待发送数据再返回;否则阻塞
非阻塞模式
-
send函数仅仅是将数据拷贝到协议栈的缓存区中,返回成功拷贝的大小;
-
如果缓存区可用空间为0,则返回-1,同时置errno为EAGAIN;
example
如果在某些条件下,发送端看似发送了10k数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中;此时,接收端收到2k数据;假如接收端调用了recv函数获取了1k的数据在处理,这时发生了以下几种情况
发送端认为send完了10k数据,关闭了socket
-
发送端进入fin_wait1,并发送fin报文给对端,缓存中8k数据并不清除,依然会发给对端
-
接收端依然在recv,则会收到余下的8k数据;然后得到一个fin报文,走4次挥手
发送端再次调用send发送8k数据
-
如果发送端缓存空间为20k,则足够装的下;send函数将数据做拷贝后,并立即返回8192;
-
如果发送缓存空间为10k,则装不下新的8k数据(10-8=2<8);则send函数会返回2048,这时应用层必须阻塞(通过select等待下一次可写信号),否则此时再调用send会得到-1,errno=EAGAIN
接收端在处理完1k后,关闭socket
-
接收端作为主动关闭者,连接将处于fin_wait1的半关闭;
-
发送端会收到socket可读信号,但是在recv时,数据长度为0,这时应该调用close函数关闭socket
-
发送端如果仅检测该socket是否可写,会得到可写信号,但继续send将返回-1,同时errno=ECONNRESET
-
发送端没有使用select机制,如果send发生在RST标志收到之前,则send工作正常
-
发送端没有使用select机制,如果send发生在RST标志收到之后,应用程序会收到SIGPIPE信号,如果忽略该信号,send=-1,errno=EPIPE
-
交换机或路由器网络断开
-
接收端处理完1k后,继续从接收缓存区读余下1k数据,然后表现为无数据可读;这时最好设定一个select等待的最大时间,如果超时无数据则认为socket不可用
-
发送端会不断将余下数据发送到网络上,但始终得不到确认,发送缓存可用空间持续为0,需要应用层程序处理;当然也可以利用tcp本身超时机制来处理
-
net.ipv4.tcp_keepalive_intvl
-
net.ipv4.tcp_keepalive_probes
-
net.ipv4.tcp_keepalive_time
-
10. listen第二个参数的含义
11. 3次握手4次挥手过程以及为什么是3次,4次
为什么3次握手
为防止已失效的连接请求报文段突然又传送到服务端,因而产生错误
-
client发出的第一个连接请求报文段并没有丢失,只是滞留在网络上,已致延误到连接释放以后的某个时间才到server
-
但server收到此失效连接请求报文后,误认为是一个新的连接请求,于是向client发出确认报文
-
如果没有第三个ack,则这个连接就建立起来了,浪费了server端资源
为什么4次挥手
client,server都需要和对方说,我已经没有数据要发送了;再加上tcp的全双工机制
12. 什么情况下会出现timewait过多,如何解决
timewait存在理由
可靠的实现tcp全双工连接的终止
-
在进行关闭连接四次挥手协议时,最后的ack由主动关闭端发出,如果这个最终的ack丢失,服务器将重发最终的fin,客户端必须维护状态信息允许它重发最终的ack;
-
如果不维持timewait状态信息,那么客户端将响应rst报文
允许老的重复分节在网络中消逝
-
lost duplicate: tcp分节可能由于路由器异常而迷途,在迷途期间,tcp发送端可能因ack超时重发这个分节,迷途的分节在路由器修复后也会被发送到目的地
-
incarnation(连接的化身): 关闭一个tcp连接后,马上重新建立起一个相同IP:port的连接,前一个连接的迷途分节在连接终止后出现,从而被误解为属于新的化身
-
为避免这种情况,tcp不允许处于time_wait状态的连接启动一个新的化身,time_wait状态持续2msl可以保证当成功建立一个tcp连接时,来自连接先前化身的重复分组已经在网络中消逝
大量time_wait造成的影响
在高并短连接tcp服务器上,当服务器处理玩请求后立刻主动正常关闭连接,这是会出现大量socket处于time_wait状态
-
高并发可以让服务器在段时间范围内同时占用大量端口,从而影响新建连接的能力
-
短连接表示”业务处理+传输数据的时间 « timewait超时的时间”的连接
如何尽量处理timewait过多(打开系统timewait重用、快速回收)
编辑内核文件/etc/sysctl.conf
,加入以下内容
-
net.ipv4.tcp_syncookies = 1
: 开启syn cookies, 当出现syn等待队列溢出时,启用cookies处理,可防止少量syn攻击,默认为0,表示关闭 -
net.ipv4.tcp_tw_reuse = 1
: 开启重用,允许timewait sockets重新用于新的tcp连接,默认关闭 -
net.ipv4.tcp_tw_recycle = 1
: 开启tcp连接中timewait sockets的快速回收,默认关闭 -
net.ipv4.tcp_fin_timeout = 1
: 修改系统默认的timeout时间