FAQ

面试常见题型之TCP/UDP

TCP/UDP见解

Posted by Dandan on November 19, 2019

1、tcp和udp的区别?

(1) TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
(2) TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
(3) TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
(4) 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
(5) TCP首部开销20字节;UDP的首部开销小,只有8个字节
(6) TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

2、udp调用connect有什么作用?

(1)、因为UDP可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom() 时都必须指定目标IP和端口号。通过调用connect()建立一个端到端的连接, 就可以和TCP一样使用send()/recv()传递数据,而不需要每次都指定目标IP和端口号。 但是它和TCP不同的是它没有三次握手的过程。
(2)、还可以通过在建立连接的UDP套接字,再次调用connect()实现以下功能:

  • a.指定新的IP地址和端口号
  • b.断开连接

(3)、tcp只能调用一次connect()函数。

3、TCP的十一种状态?

(1) CLOSED: 初始状态,表示TCP连接是关闭或者未打开
(2) LISTEN: 表示服务端的某个socket处于监听状态,可以接受客户端的连接
(3) SYN_RCVD: 表示收到了SYN报文。这个状态基本上是服务器端收到SYN报文后的一个短暂的中间状态 ,使用netart很难捕捉到。当再次收到客户端的ACK报文时,进入到ESTABLISHED的状态。
(4) SYN_SENT: 这个状态与SYN_RCVD状态相呼应,客户端主动发起连接,调用connect函数时,发送SYN报文, 随即进入到SYN_SENT状态,并等待服务端的SYN报文。当接收到服务端的SYN报文后,回应ACK后,会 进入到 ESTABLISHED 的状态。
(5) ESTABLISHED: 表示TCP已经建立成功。
(6) FIN_WAIT_1 :这个状态得好好解释一下,其实FIN_WAIT_1 和FIN_WAIT_2 两种状态的真正含义都是表示等待对方的FIN报文。
而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接, 向对方发送了FIN报文,此时该SOCKET进入到FIN_WAIT_1 状态。而当对方回应ACK报文后, 则进入到FIN_WAIT_2 状态。当然在实际的正常情况下,无论对方处于任何种情况下,都应该马上回应ACK报文, 所以FIN_WAIT_1 状态一般是比较难见到的,而FIN_WAIT_2 状态有时仍可以用netstat看到
(7) FIN_WAIT_2:上面已经解释了这种状态的由来,实际上FIN_WAIT_2状态下的SOCKET表示半连接,即有一方调用close()主动要求关闭连接。
注意:FIN_WAIT_2 是没有超时的(不像TIME_WAIT 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),
那这个 FIN_WAIT_2 状态将一直保持到系统重启,越来越多的FIN_WAIT_2 状态会导致内核crash。
(8) TIME_WAIT: 表示收到了对方的FIN报文,并且已经发送了ACK。这个时候实际上已经没有了报文交互,那么TIME_WAIT状态 下的TCP连接会等待2*MSL,然后回到CLOSE状态。(Max Segment Lifetime,最大分段生存期, 指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值, RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值)。 如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态
(9) CLOSING: CLOSING 状态表示一方发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。 什么情况下会出现此种情况呢?那就是当双方几乎在同时close()一个SOCKET的话,就出现了双方同时发送FIN报文的情况, 这是就会出现CLOSING 状态,表示双方都正在关闭SOCKET连接。 这种状态在实际情况中应该很少见,属于一种比较罕见的例外状态。正常情况下,当一方发送FIN报文后, 按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。
(10) CLOSE_WAIT:表示正在等待关闭。怎么理解呢?当对方close()一个SOCKET后发送FIN报文给自己,你的系统毫无疑问地将会回应一个ACK报文给对方,
此时TCP连接则进入到CLOSE_WAIT状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话, 那你也就可以close()这个SOCKET并发送FIN报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略, 继续发送或丢弃。简单地说,当你处于CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。加入对方一直不回应ACK报文 ,则socket会一直无法关闭。
(11) LAST_ACK: 当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK 状态。当收到对方的ACK报文后,也就可以进入到CLOSED 状态了。

4、socket服务端的实现,select和epoll的区别?

(1) select缺点: 1.最大并发数限制:使用32个整数的32位,即32*32=1024来标识fd,虽然可修改,但是有以下第二点的瓶颈; 2.效率低:每次都会线性扫描整个fd_set,集合越大速度越慢; 3.内核/用户空间内存拷贝问题。

(2) epoll的提升: 1.本身没有最大并发连接的限制,仅受系统中进程能打开的最大文件数目限制; 2.效率提升:只有活跃的socket才会主动的去调用callback函数; 3.省去不必要的内存拷贝:epoll通过内核与用户空间mmap同一块内存实现。

  select poll epoll
底层数据结构 数组存储文件描述符 链表存储文件描述符 红黑树存储监控的文件描述符,双链表存储就绪的文件描述符
如何从fd数据中获取就绪的fd 遍历fd_set 遍历链表 回调
时间复杂度 获得就绪的文件描述符需要遍历fd数组,O(n) 获得就绪的文件描述符需要遍历fd链表,O(n) 当有就绪事件时,系统注册的回调函数就会被调用,将就绪的fd放入到就绪链表中。O(1)
FD数据拷贝 每次调用select,需要将fd数据从用户空间拷贝到内核空间 每次调用poll,需要将fd数据从用户空间拷贝到内核空间 使用内存映射(mmap),不需要从用户空间频繁拷贝fd数据到内核空间
最大连接数 有限制,一般为1024 无限制 无限制
  • 应用场景 a. 连接数较少并且都很活跃,用select和poll效率更高 b. 连接数较多并且都不很活跃,使用epoll效率更高

6、大规模连接上来,并发模型怎么设计?(高并发网络模型)

(1) Linux的并发模型有三种,包括:多进程并发、多线程并发以及IO复用模型。
(2)多进程并发:accept返回成功时候,就为这一个连接fork一个进程,专门处理这个连接上的数据收发,等这个连接处理结束之后就结束这个进程
所线程并发:类似多进程方式,但是针对一个连接启动一个线程。
优点: 相对多进程方式,会节约一些资源,会更加高效一些。
缺点: 相对多进程方式,增加了编程的复杂度,因为需要考虑数据同步和锁保护。另外一个进程中不能启动太多的线程。 在Linux系统下线程在系统内部其实就是进程,线程调度按照进程调度的方式去执行的
Select+多线程:有一个线程专门用于监听端口,accept返回之后就把这个描述符放入 描述符集合 fd中,一个线程用select去轮训描述符集合,
在有数据的连接上接收数据,另外一个线程专门发送数据。当然也可以接收和发送用一个线程。
epoll方式:一个线程专门进行端口监听,accept接收到连接的时候,把该连接设置成非阻塞模式,
把 epoll事件设置成边缘触发方式,加入到epoll管理。接收线程阻塞在epoll的等待事件函数。另外一个线程专门用于数据发送。

# 7、 tcp结束连接怎么握手,time_wait状态是什么,为什么会有time_wait状态?哪一方会有time_wait状态,如何避免time_wait状态占用资源? tcp通过四次挥手结束连接。

  • TIME_WAIT: 表示收到了对方的FIN报文,并且已经发送了ACK。这个时候实际上已经没有了报文交互,那么TIME_WAIT状态 下的TCP连接会等待2*MSL,然后回到CLOSE状态。(Max Segment Lifetime,最大分段生存期, 指一个TCP报文在Internet上的最长生存时间。
    每个具体的TCP协议实现都必须选择一个确定的MSL值,
    RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值)。
    如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
  • TIME_WAIT状态是发起断连的一端存在的状态。
  • TIME_WAIT状态存在的意义:
    1、实现可靠的全双工连接:因为在最后一次发送ACK报本可能丢失,则对端可能会希望本端复位重发,导致错误产生。
    2、允许老的重复的分段在网络上消失。

8、tcp头多少字节?哪些字段?

tcp头有20字节,包括选项时会增大,最大不超过60字节。包括:16位源端口号、16位目的端口号、32位的序号、32位确认号、4位头部长度

9、connect会阻塞,怎么解决?

connect调用的时候,当发起主动连接的时候,如果服务端关闭,则进程会被connect阻塞,等待较长时间返回。假如直接将connect直接定义为非阻塞的,则无法确定connect是否连接成功。
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4.使用select()检查该socket描述符是否可写
5.根据select()返回的结果判断connect()结果
6.将socket设置为阻塞模式

10、keepalive 是什么?如何使用?

TCP协议中有长连接和短连接之分。短连接在数据包发送完毕后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即通常所说的 keepalive功能。
当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1, 同时置上errno为ETIMEDOUT。此时TCP的状态是断开的。