图解学习网站:xiaolincoding.com
大家好,我是小林。
最近有个同学跟我反馈,去面腾讯的时候,又被腾讯面试官拷打了 。
因为他是搞 Go 后端的,没怎么接触过 socket 编程,结果问了好几个网络编程的问题,直接懵逼了。
第一个问题
服务端调用listen之后sleep休眠,请问客户端的connect能不能成功?
这个问题实际上在考察大家对于 TCP 三次握手的底层原理,很多同学对于 TCP 三次握手的理解只停留在SYN和ACK报文的交互,而关于三次握手过程中,内核做了哪些事情?socket 编程和三次握手有什么关系?都不太明白,那这样在遇到这种问题就比较难回答了。
我们先看看客户端连接服务端时,发生了什么?
- 服务端和客户端初始化 socket
- 服务端调用 bind,将 socket 绑定在指定的 IP 地址和端口;
- 服务端调用 listen,进行监听,这个时候服务端内核就会有 TCP 半连接队列和 TCP 全连接队列了。
- 客户端调用 connect,向服务端的地址和端口发起连接请求,这时候就会发生 TCP 第一次握手,发送 syn 报文,并告诉服务端当前发送序列号 client_isn,客户端进入 SYN_SENT 状态;
- 服务端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 client_isn 1,表示对 SYN 包 client_isn 的确认,同时服务端也发送一个 SYN 包,告诉客户端当前我的发送序列号为 server_isn,服务端进入 SYN_RCVD 状态,然后会把这个连接放到 TCP 半连接队列中。
- 客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务端的 SYN 包进行应答,应答数据为 server_isn 1;
- ACK 应答包到达服务端后,服务端的 TCP 连接进入 ESTABLISHED 状态,同时服务端协议栈使得 accept 阻塞调用返回,这个时候服务端到客户端的单向连接也建立成功,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 TCP 全连接队列,等待进程调用 accept 函数时把连接取出来。至此,客户端与服务端两个方向的连接都建立成功。
从上面的描述过程,我们可以得知客户端 connect 成功返回是发送完第三次握手后,服务端 accept 成功返回是在三次握手成功之后。
所以,服务端调用listen之后sleep休眠,并不会影响 TCP 三次握手的过程,当服务端调用listen
后,操作系统内核会开始监听指定端口上的连接请求。即使服务端进入sleep
休眠状态,内核仍然会继续监听连接请求,并将其放入到TCP全连接队列中,等待服务端调用 accpet 来获取连接。
我说不影响就不影响吗?
肯定也不一定对的嘛,还是给大家做个实验验证下我的想法对不对。
写了一个简单的服务端代码,服务端调用完 listen 后,直接进入长达 100000 年的休眠(夸张手法)。
然后编译 启动程序!
ok,很顺利,服务端监听了 8080 端口之后就睡觉去了。
接下来,用 linux 的 nc 工具来模仿客户端连接这个端口。
代码语言:javascript复制nc 127.0.0.1 8888
- 如果这个命令立即返回,就当无事发生的话,说明出问题了,可能是无法通信,可能是客户端没有打开;
- 如果正在等待,就说明连接成功,可以在服务器这个窗口中输入任意内容,在客户端会显示出来
来看看结果如何?
结果是正在等待,说明连接是正常建立成功的!
我们可以通过 netstat 命令来确认连接状态。
完全没问题,状态是ESTABLISHED!
说明服务端调用listen之后sleep休眠,客户端的connect是可以成功的,能顺利完成 TCP 三次握手,我前面的理论分析是没问题的。
针对这个问题,还有另外一种问法,服务端没有执行没有 accept,能成功建立 TCP 连接吗?
答案很明显,也是可以的,因为 accept 并不参与三次握手的过程,只是负责从 TCP全连接队列中取出连接。
第二个问题
如果两边建立了连接,然后有一端sleep休眠,请问write和send能不能成功?
我直接结论,是可以成功的。
因为发送方调用 wirte 发送数据的时候,当把数据从应用层拷贝到 socket 发送缓冲区之后,函数就会返回成功了,至于什么时候发数据,发多少数据,这个后续由内核自己做决定。
来源:小白 debug
并不是等接收方调用 read 函数读到数据后,发送方的 write 函数才返回。因此,接收方的进程即使处于睡眠状态,也不会影响发送方执行 wirte。
并且即使发送方进程即使处于睡眠状态,内核依然也是可以正常接收数据,接收到的数据,会被先放到接收缓冲区,等待发送方进程调用 read 从接收缓冲区读取数据。
最后
这两个问题就分析到这里,本质上就是考察大家对 Linux 内核在 TCP 握手和数据传输阶段做了什么事情。
话说回来,感觉腾讯确实很喜欢问网络编程的底层原理啊,以前也分享过好几个同学在面腾讯遇到的网络编程的问题,去图解网站(xiaolincoding.com)翻一翻就可以看到啦。
讲完了,收工,溜了!(是不是像极了下班的样子)
历史好文:
【后端训练营】这一个月惊喜满满!!
准备很久,还是被小红书虐了。。。
不愧是字节,把我吊打了。。。
不愧是微信啊,问的范围贼广!
最累的一场面试,还得是腾讯!