Go 面试复盘
先上总结:
- 面经光看没用,只有自己不断的面试,然后总结,自己理解性的进行描述才有意义,否则只是像八股文一样去背记,从而未能理解真正的含义。那么,可能下一次面试的时候,上一次面试的问题依然处于一种遗留的状态,这样就无法在技术的关键节点进行成长。
- 尤其是算法题,如果一两个月不复盘算法问题,那么算法思维存在,但是代码熟练度会下降,从而导致写算法的时间变长,以及相应的心态焦虑。至于算法的时间复杂度与空间复杂度放在下文具体说明。
- 以下为自己答的不好与没答出的问题。
1、TCP 三次握手
答:TCP的三次握手为:
1、客户端发送的报文为 SYN 报文,并选择一个初始的 Seq 序号,之后客户端进入监听状态(SYN-SENT)。
2、服务器在接受到客户端第一次发送的 ACK 报文之后,如果同意连接,即向客户端发送连接确认报文,即 SYN + ACK 报文,也附加一个自选的初始 Seq 序号,并且此序号与客户端的序号无关,之后服务器端继续维持监听状态(SYN-REVD)
3、客户端在接受到服务器发送回的报文之后,再向服务器端发送确认报文,确认号为服务器初始的 Seq 序号 + 1,序号为自己初始的 Seq 序号 + 1。
4、此后服务器与客户端正式建立连接,开始发送数据,双方状态为ESTABLISHED。
此处引用哔哩哔哩 up 主掌芝士zzs的图片
2、TCP 需要三次握手的原因
答:采用第三次握手的原因是:
1、如果第一次客户端的报文中途出现延迟,而客户端开始重发第一次的报文,并且重发报文被服务器正确接收。
2、而此时,第一次客户端发送的报文又到达服务端,服务端接受后,又返回一个报文,相当于服务器同一个客户端建立了两个连接,而客户端只认为自己建立的一个连接,造成了状态不一致,同时服务器的资源也被浪费了。
3、故为了尽可能保证连接的建立及时、有效且资源节约,故采用 TCP 三次握手。
3、TCP 四次挥手
答:TCP 的四次挥手过程为
1、首先由客户端向服务器端发送关闭连接请求报文,即 FIN 报文,此时客户端由 ESTABLISHED 状态转变为 FIN-WAIT-1 状态,此时还能继续接受服务器所发送的数据。
2、服务器在接受到客户端向服务器端发送的 FIN 包后,向客户端发送确认报文,即 ACK 报文,此时服务器的状态由 ESTABLISHED 状态转变为 CLOSED-WAIT状态,此时服务器端还能继续把未能发送完的数据继续发送。
3、客户端在接受了服务器端发回的 ACK 确认报文之后由 FIN-WAIT-1 转变为 FIN-WAIT-2 状态,并且能继续接受数据,直到服务器发送终止报文,即 FIN 包为止。
4、服务器向客户端发送 FIN 包,此时服务器端由 CLOSED-WAIT 状态转变为 LAST-ACK 状态,等待客户端返回最后一次确认报文,即 ACK 为止。
5、客户端接收到 FIN 包后,由 FIN-WAIT-2 变为 TIME-WAIT 状态,超过一定的时间后自动转变为 CLOSED 状态。
6、服务器端收到客户端的 ACK 报文后,即由 LAST-ACK 状态转变为 CLOSED 状态,不再发送与接受客户端的数据。
此处引用哔哩哔哩 up 主 掌芝士zzs 的图片
4、TCP 需要四次挥手的原因
答:采用第四次挥手的原因是:
1、如果客户端第四次发送 ACK 报文后就直接进入 CLOSED 状态,那么如果第四次发送的ACK报文在传输的过程中丢失,服务器由于一直未能接收客户端发送的 ACK 报文,再次向客户端发送相应的 FIN 报文,而此时客户端已经关闭,接受不到服务器发送的 FIN 报文。即造成了服务器的资源浪费
2、故为了保证通信尽可能的可靠,采用 TCP 四次握手,但是在考研中,有种特殊的情况,在确保第三次握手能成立的情况下,第四次握手可以被省略。若将一个往返视为 RTT 的情况下,最短的释放连接所需要的时间为 1.5 个 RTT 即可,不需要 2 个 RTT,所以这也是为了节省资源所考虑的情况,并不一定视为错误答案。
5、TCP粘包问题
答:TCP 对比 UDP,前者主要是以字节流的形式传输数据,而UDP则以报文的形式传输数据。
其中报文与字节流的区别主要是,字节流传输是以字节为单位进行数据传输,与每一个数据中的独立的内容无关,而报文传输为传输以报文为单位,保留了报文内容的边界。所以就会导致TCP传输字节时候,有可能区分不了数据的边界,导致最后解包数据所造成的解码乱码现象,即TCP的粘包的问题。
解决 TCP 粘包问题的办法有许多种,其中在之前所学的Zinx框架中,刘丹冰老师(后面称为 Aceld 老师)解释了一种 TLV 格式的封包解包办法(如下图),即使用 datalen、msgID、data作为封包解包的字段,首先读取一遍封包的头部长度,即 datalen 与 msgID 字段的大小,此处按照自己设置的来。一般设置是两个 uint32 类型,即为 8 Byte 的。
其中解析开头的 datalen为 data字段的数据长度,msgID 为相应的数据包编号。然后根据开头的 datalen 的具体数值来读取之后的 data 段的数据。至于TCP发送数据流中出现的错误,利用好 TCP 的错误重传机制就好,go 语言也有 TCP 包的实现,具体以后看底层代码来解释,本篇博客暂时不讨论此问题。
6、写代码:channel
1 |
|
7、Go 中 map 查询的时间复杂度为 O(1)
答:查看了相应的各种博客,了解到:
1、java 中 map 的底层源代码实现为数组 + 链表 + 红黑树,所以在数据量极小的情况下,相应的**查询时间复杂度为 O(1)**,而在数据量较多的情况,map 的查询时间复杂度应该是大于 O(1),小于 O(N),接近 O(logN) 的时间复杂度的!
2、所以说关于 map 的查询时间复杂度是一个很老的问题了,一般使用情况默认为 O(1) 的时间复杂度。但是这并不意味着就要否认 map 的查询时间复杂度是 O(logN) 的说法。
3、不懂不理解的问题,一定需要事后进行相关资料的查询,以及总结。
关于map 查询的时间复杂度,StackOverFlow 上给出的说法挺多
8、GMP 模型
强烈推荐 Aceld 老师的 GMP 模式详细解释,可以明白调度器的调度行为。
GMP 模型已经单独作为一篇博客存在,所以此处不再阐述。
9、Go GC 在什么情况下性能比较低
1、内存泄露。
2、小对象,结构体比指针的好
————END 如果本博客存在误解的问题,恳请大家指出!—————-