计算机网络
- OSI七层模型和TCP/IP五层模型 | 名称 | 作用 | 协议 | | —- | —- | —- | | 应用层 | 文件传输、电子邮件、虚拟终端等 | HTTP,SNMP,FTP,SMTP,DNS,Telnet | | 表示层 | 数据格式化,代码转换,数据加密 | 没有协议 | | 会话层 | 解除或建立与别的接点的联系 | 没有协议 | | 传输层 | 为运行在不同主机上的进程之间提供逻辑通信 | TCP,UDP,SPX | | 网络层 | 提供了主机之间的逻辑通信,为数据包选择路由 | IP,ICMP,RIP,OSPF,BGP,IGMP | | 数据链路层 | 传输有地址的帧以及错误检测功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU | | 物理层 | 以二进制数据形式在物理媒体上传输数据 | ISO2110,IEEE802,IEEE802.2 |
| TCP/IP层 | 网络设备 |
|---|---|
| 应用层 | 软件 |
| 传输层 | 四层交换机、也有工作在四层的路由器 |
| 网络层 | 路由器、三层交换机 |
| 数据链路层 | 网桥、交换机、网卡(一半数据链路一半物理) |
| 物理层 | 中继器、集线器、双绞线 |
网络层、数据链路层、传输层使用的寻址地址分别是什么
- ip地址,mac地址,ip:端口号
常见的应用层协议和相对端口号
- FTP:20,21;SSH:22;Telent:23;HTTP:80;HTTPS:443;DNS:53;DHCP:67;mysql:3306;tomcat:8080;oracle:1521、1526
IP地址的分类
- 现在的IP网络使用32位地址,以点分十进制表示,如172.16.0.0。地址格式为:IP地址=网络地址+主机地址 或 IP地址=主机地址+子网地址+主机地址。
IP地址根据网络ID的不同分为5种类型,A类地址、B类地址、C类地址、D类地址和E类地址。
- A类:一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”, 地址范围从1.0.0.0 到126.0.0.0。可用的A类网络有126个,每个网络能容纳1亿多个主机。默认掩码为255.0.0.0
- B类:一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,地址范围从128.0.0.0到191.255.255.255。可用的B类网络有16382个,每个网络能容纳6万多个主机。默认掩码为255.255.0.0
- C类:一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”。范围从192.0.0.0到223.255.255.255。C类网络可达209万余个,每个网络能容纳254个主机。默认掩码为255.255.255.0
- D类:D类地址称为广播地址,供特殊协议向选定的节点发送信息时用,网络地址的最高位必须是“1110”。范围从224.0.0.0~239.255.255.255
- E类:E类地址说是留给将来用,但其实现在应该用完了吧,网络地址的最高位必须是“11110”。范围从240.0.0.0~255.255.255.254
- 全零(0.0.0.0)代表当前主机,全1(255.255.255.255)代表当前子网的广播地址
在IP地址3种主要类型里,各保留了3个区域作为私有地址,其地址范围如下
- A类地址:10.0.0.0~10.255.255.255 (10全部)
- B类地址:172.16.0.0~172.31.255.255
- C类地址:192.168.0.0~192.168.255.255 (192.168全部)
- 注意,0和127开头不属于A类地址,0代表任何地址,127为回环测试地址。回环测试地址保留给内部回送函数,数字0表示该地址为本地宿主机,不能传送
子网掩码的作用
- 子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分。
- 子网掩码是一个32位地址,对于A类地址来说,默认的子网掩码是255.0.0.0;对于B类地址来说默认的子网掩码是255.255.0.0;对于C类地址来说默认的子网掩码是255.255.255.0。
如何确定子网掩码
- 如欲将B类IP地址168.195.0.0划分成若干子网,每个子网内有主机700台
- 700=1010111100,该二进制为十位数,N = 10
- 将子网掩码255.255.255.255从后向前的10位全部置0,即为11111111.11111111.11111100.00000000,也即255.255.252.0。255.255.252.0就是168.195.0.0的700台主机的子网掩码了。
如何将一个ip地址由点分制和int类型间进行相互转化
- 点分制的ip地址相比正常的二进制转成十进制,只不过是每八位都少乘了一个
而已(n从0开始)
- 10.10.1.1的十进制就是
- 点分制的ip地址相比正常的二进制转成十进制,只不过是每八位都少乘了一个
TCP三次握手、四次挥手。


需要注意的是, 上图中出现的 ACK = x +1 的写法很容易让人误以为数据包中的 ACK 域的数据值被填成了 x+1 。 ACK = x+1 的实际含义是:
- TCP 包的 ACK 标志位(1 bit) 被置成了 1
- TCP 包的确认号(acknowledgement number ) 的值为 x+1
为什么三次握手最后一次握手中, 在上面的示意图中回复的 seq = x+1 而不是 x+2
为什么tcp连接要握手,为什么一定要三次,两次会有什么后果?
这里就引出了 TCP 与 UDP 的一个基本区别, TCP 是可靠通信协议, 而 UDP 是不可靠通信协议。
- TCP 的可靠性含义: 接收方收到的数据是完整, 有序, 无差错的。
- UDP 不可靠性含义: 接收方接收到的数据可能存在部分丢失, 顺序也不一定能保证。
- TCP 协议为了实现可靠传输, 通信双方需要判断自己已经发送的数据包是否都被接收方收到, 如果没收到, 就需要重发。 为了实现这个需求, 很自然地就会引出序号(sequence number) 和确认号(acknowledgement number)的使用。
- 为了实现可靠传输,发送方和接收方始终需要同步( SYNchronize )序号。 需要注意的是, 序号并不是从 0 开始的, 而是由发送方随机选择的初始序列号 ( Initial Sequence Number, ISN )开始 。 由于 TCP 是一个双向通信协议, 通信双方都有能力发送信息, 并接收响应。 因此, 通信双方都需要随机产生一个初始的序列号, 并且把这个起始值告诉对方,并确认对方的同步序号。
- 链接
tcp协议格式,问tcp包里面有ip地址吗
- TCP封装在IP报文中的时候,如下图所示,TCP头紧接着IP头(IPV6有扩展头的时候,则TCP头在扩展头后面),不携带选项(option)的TCP头长为20bytes,携带选项的TCP头最长可到60bytes。

- 其中header length字段由4比特构成,最大值为15,单位是32比特,即头长的最大值为15*32 bits = 60bytes,因此上面说携带选项的TCP头长最长为60bytes。
- tcp包的长度由MTU决定,一般是1500,最大传输大小(MSS)为1500-IP数据包包头(20bytes)-tcp数据包包头(20bytes)==1460bytes

tcp何如保证数据可靠传输
主要靠5点:
- 确认和超时重传
- 数据合理分片和排序
- 流量控制
- 拥塞控制
- 数据校验
tcp超时重传
- 对于一些出错,超时丢包等问题TCP设计的超时与重传机制,其基本原理:在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到发送数据的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。
- 这里比较重要的是重传超时时间,怎样设置这个定时器的时间(RTO),从而保证对网络资源最小的浪费。因为若RTO太小,可能有些报文只是遇到拥堵或网络不好延迟较大而已,这样就会造成不必要的重传。太大的话,使发送端需要等待过长的时间才能发现数据丢失,影响网络传输效率。
- 由于不同的网络情况不一样,不可能设置一样的RTO,实际中RTO是根据网络中的RTT(传输往返时间)来自适应调整的。具体关系参考相关算法。
tcp慢启动
- 慢启动是TCP的一个拥塞控制机制,慢启动算法的基本思想是当TCP开始在一个网络中传输数据或发现数据丢失并开始重发时,首先慢慢的对网路实际容量进行试探,避免由于发送了过量的数据而导致阻塞。
- 慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为 1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。发送方开始时发送一个报文段,然后等待 ACK。当收到该ACK时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的 ACK时,拥塞窗口就增加为4。这是一种指数增加的关系。
tcp拥塞避免
网络中拥塞的发生会导致数据分组丢失,需要尽量避免。在实际中,拥塞算法与慢启动通常在一起实现,其基本过程:
对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。
TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估 计,而后者则与接收方在该连接上的可用缓存大小有关。
当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半(cwnd 和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则 cwnd被设置为1个报文段(这就是慢启动)。
当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到回到当拥塞发生时所处位置的半时候才停止(因为记录了在步骤2 中制造麻烦的窗口大小的一半),然后转为执行拥塞避免。

客户端当被告知服务端接收窗口大小为0后的行为,如果服务端的接收窗口又变大了呢?
- 如果窗口尺寸是 0,发送端就将发出一个探测信号以搞清这个窗口什么时间再次打开。如果发送方从来没有收到ACK信息,它就一直不断地重试,直到定时器过期。又变大就继续发。
tcp滑动窗口(整个流程、接收窗口和发送窗口是如何移动的、序列和确认号)、拥塞控制掌握三个阶段:慢启动、拥塞避免、超时重传/快速恢复)

先来看从概念上数据分为哪些类
- Sent and Acknowledged:这些数据表示已经发送成功并已经被确认的数据,比如图中的前31个bytes,这些数据其实的位置是在窗口之外了,因为窗口内顺序最低的被确认之后,要移除窗口,实际上是窗口进行合拢,同时打开接收新的带发送的数据
- Send But Not Yet Acknowledged:这部分数据称为发送但没有被确认,数据被发送出去,没有收到接收端的ACK,认为并没有完成发送,这个属于窗口内的数据。
- Not Sent,Recipient Ready to Receive:这部分是尽快发送的数据,这部分数据已经被加载到缓存中,也就是窗口中了,等待发送,其实这个窗口是完全有接收方告知的,接收方告知还是能够接受这些包,所以发送方需要尽快的发送这些包
- Not Sent,Recipient Not Ready to Receive: 这些数据属于未发送,同时接收端也不允许发送的,因为这些数据已经超出了发送端所接收的范围
- 对于发送方来讲,窗口内的包括两部分,发送窗口(已经发送了,但是没有收到ACK),接收端允许发送但是没有发送的那部分称为可用窗口。
对于接收端也是有一个接收窗口,类似发送端,接收端的数据有3个分类,因为接收端并不需要等待ACK所以它没有类似的接收并确认了的分类,情况如下
- Received and ACK Not Send to Process:这部分数据属于接收了数据但是还没有被上层的应用程序接收,也是被缓存在窗口内
- Received Not ACK: 已经接收并,但是还没有回复ACK,这些包可能输属于Delay ACK的范畴了
- Not Received:有空位,还没有被接收的数据。
- TCP并不是每一个报文段都会回复ACK的,可能会对两个报文段发送一个ACK,也可能会对多个报文段发送1个ACK(累计ACK)
- 链接
在四次挥手的时候会有一个wait的过程,目的是什么,time_wait连接过多如何解决
- 简单来说,time_wait状态是四次挥手中server向client发送FIN终止连接后client进入的状态。time_wait状态存在于client收到serverFin并返回ack包时的状态。当处于time_wait状态时,由于port被占用,我们无法创建新的连接。有时产生在server端,由于server主动断开连接或者发生异常。
存在原因
- 可靠地终止TCP连接,若处于time_wait的client发送给server确认报文段丢失的话,server将在此又一次发送FIN报文段,那么client必须处于一个可接收的状态就是time_wait而不是close状态。
- 保证迟来的TCP报文段有足够的时间被识别并丢弃,linux 中一个TCPport不能打开两次或两次以上。当client处于time_wait状态时我们将无法使用此port建立新连接,假设不存在time_wait状态,新连接可能会收到旧连接的数据。
- time_wait持续的时间是2MSL,保证旧的数据能够丢弃。
- 在高并发短连接的TCP服务器上,当服务器处理完请求后立刻按照主动正常关闭连接。这个场景下,会出现大量socket处于TIMEWAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
- 如何处理?sysctl改两个内核参数就行了,如下:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
简单来说,就是打开系统的TIMEWAIT重用和快速回收
udp的特征
- 无连接
- 尽最大努力交付
- 面向报文
- 没有拥塞控制
- 支持一对一、一对多、多对一、多对多的交互通信
- 首部开销小
知道udp是不可靠的传输,如果你来设计一个基于udp差不多可靠的算法,怎么设计?
- 设计思路
数据完整性 –> 加上一个16或者32位的CRC验证字段
乱序 –> 加上一个数据包序列号SEQ
丢包 –> 需要确认和重传机制,就是和Tcp类似的Ack机制
协议字段 –> protol 字段,标识当前使用协议
- 设计思路
udp的使用场景
- 面向数据报时
- 网络数据大多为短消息
- 拥有大量client
- 对数据安全性没有特殊要求
- 网络负担重,但要求高响应
tcp和udp的区别
- TCP 面向连接,UDP 是无连接的;
- TCP 提供可靠的服务,也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付
- TCP 的逻辑通信信道是全双工的可靠信道;UDP 则是不可靠信道
- 每一条 TCP 连接只能是点到点的;UDP 支持一对一,一对多,多对一和多对多的交互通信
- TCP 面向字节流(可能出现黏包问题),实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的(不会出现黏包问题)
- UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等)
- TCP 首部开销20字节;UDP 的首部开销小,只有 8 个字节
tcp对半开链接的处理
- 当客户端与服务器建立起正常的TCP连接后,如果客户主机网线断开、电源掉电、或系统崩溃,服务器进程将永远不会知道(通过我们常用的select,epoll监测不到断开或错误事件),如果不主动处理或重启系统的话对于服务端来说会一直维持着这个连接,任凭服务端进程如何望穿秋水,也永远再等不到客户端的任何回应。这种情况就是半开连接,浪费了服务器端可用的文件描述符。
- 说明网线断开对端是不能做任何感知的,除非我们配置操作系统的SO_KEEPALIVE选项,或者进行应用层心跳检测
- 如果网线断开的时间短暂,在SO_KEEPALIVE设定的探测时间间隔内,并且两端在此期间没有任何针对此长连接的网络操作。当连上网线后此TCP连接可以自动恢复,继续进行正常的网络操作。
- 如果网线断开的时间很长,超出了SO_KEEPALIVE设定的探测时间间隔,或者两端期间在此有了任何针对此长连接的网络操作。当连上网线时就会出现ETIMEDOUT或者ECONNRESET的错误。你必须重新建立一个新的长连接进行网络操作。
tcp与udp编程步骤:
tcp编程的服务器端步骤如下:
- 创建一个socket并设置socket属性
- 绑定IP地址、端口等信息到socket上
- 开启监听并接收客户端上来的连接
- 收发数据
- 关闭网络连接并关闭监听
tcp编程的客户端步骤如下:
- 创建一个socket并设置socket属性
- 绑定IP地址、端口等信息到socket上
- 设置要连接的对方的IP地址和端口等属性
- 连接服务器
- 收发数据
- 关闭网络连接
udp编程的服务器端步骤如下:
- 创建一个socket并设置socket属性
- 绑定IP地址、端口等信息到socket上
- 循环接收数据
- 关闭网络连接
udp编程的客户端步骤如下:
- 创建一个socket并设置socket属性
- 绑定IP地址、端口等信息到socket上
- 设置对方的IP地址和端口等属性
- 发送数据
- 关闭网络连接
如何完全消除time await? 当初回答的时候设置属性和reuse都是判错的,要求从最后挥手的阶段开始分析
粘包与处理
- TCP协议允许发送端将几次发送的数据包缓存起来合成一个数据包发送到网络上去,因为这样可以获得更高的效率,这一行为通常是在操作系统提供的SOCKET中实现,所以在应用层对此毫无所觉。所以我们在程序中调用SOCKET的send发送了数据后操作系统有可能缓存了起来,等待后续的数据一起发送,而不是立即发送出去。send的文档中对此也有说明。
- 网络传输的概念中有MTU的概念,也即是网络中一个数据包最大的长度。如果要发送超过这个长度的数据包,就需要分包发送。当调用SOCKET的send发送超过MTU的数据包时,操作系统提供的SOCKET实现会自动将这个数据包分割成几个不超过MTU的数据包发送。
- 当出现这些上面这些情况的时候,接收端就会发现接收到的数据和发送的数据的次数不一致。这个就是粘包现象。当我们传输如文件这种数据时,流式的传输非常适合,但是当我们传输指令之类的数据结构时,流式模型就有一个问题:无法知道指令的结束。所以粘包必须问题是必须解决的。
- 最简单的方法就是短连接,也就是需要发送数据的时候建立TCP连接,发送完一个数据包后就断开TCP连接,这样接收端自然就知道数据结束了。但是这样的方法因为会多次建立TCP连接,性能低下。随便用用还可以,只要稍微对性能有一点追求的人就不会使用这种方法。
使用长连接能够获得更好的性能,但不可避免的会遇到如何判断数据结构的开始与结束的问题。而此时的处理方式根据数据结构的类型分两种方式。
- 定长结构:因为粘包问题的存在,接收端不能想当然的以为发送端一次发送了多少数据就能一次收到多少数据。如果发送端发送了一个固定长度的数据结构,接收端必须每次都严格判断接收到数据的长度,当收到的数据长度不足时,需要再次接收数据,直到满足长度,当收到的数据多于固定长度时,需要截断数据,并将多余的数据缓存起来,视为长度不足需要再次接收处理。
不定长结构:定长的数据结构是一种理想的情况,真正的应用中通常使用的都是不定长的数据结构。对于发送不定长的数据结构,简单的做法就是选一个固定的字符作为数据包结束标志,接收到这个字符就代表一个数据包传输完成了。但是这只能应用于字符数据,因为二进制数据中很难确定结束字符到底是结束还是原本要传输的数据内容。目前最通用的做法是在每次发送的数据的固定偏移位置写入数据包的长度。接收端只要一开始读取固定偏移的数据就可以知道这个数据包的长度,接下来的流程就和固定长度数据结构的处理流程类似。所以对于处理粘包的关键在于提前获取到数据包的长度,无论这个长度是提前商定好的还是写在在数据包的开头。因为在每次发送的数据的固定偏移位置写入数据包的长度的方法是最通用的一种方法,所以对这种方法实现中的一些容易出错误的地方在此特别说明。
- 通常我们使用2~4个字节来存放数据长度,多字节数据的网络传输需要注意字节序,所以要注意接受者和发送者要使用相同的字节序来解析数据长度。
- 每次新开始接收一段数据时不要急着直接去解析数据长度,先确保目前收到的数据已经足够解析出数据长度,例如数据开头的2个字节存储了数据长度,那么一定确保接收了2个字节以上的数据后才去解析数据长度。如果没做到这一点的服务器代码,收到了一个字节就去解析数据长度的,结果得到的长度是内存中的随机值,结果必然是崩溃的
- 有些非法客户端或者有bug的客户端可能会发出错误的数据,导致解析出的数据长度异常的大,一定要对解析出的数据长度做检查,事先规定一个合适的长度,一旦超过果断关闭SOCKET,避免服务器无休止的等待下去浪费资源。
- 处理完一个完整的数据包后一定检查是否还有未处理的数据,如果有的话要对这段多余的数据再次开始解析数据长度的过程。不要忙着去继续接受数据。
如何处理syn flood攻击
- SYN Flood (SYN洪水) 是种典型的DoS (Denial of Service,拒绝服务) 攻击。效果就是服务器TCP连接资源耗尽,停止响应正常的TCP连接请求。攻击方A可以控制肉鸡向B发送大量SYN消息但不响应ACK消息,或者干脆伪造SYN消息中的Source IP,使B反馈的SYN-ACK消息石沉大海,导致B被大量注定不能完成的半开连接占据,直到资源耗尽,停止响应正常的连接请求。
- 最简单粗暴的办法就是提高TCP端口连接容量的同时减少半开连接的资源占用时间,也可以减少半开状态下等待ACK消息的时间或者重试发送SYN-ACK消息的次数,抑或启用某种半开连接回收机制,使得当半开连接队列满了以后做“除旧迎新”操作,当然并不是所有系统都支持这种机制。但攻击者只要稍稍改变策略就可以提高攻击效果,比如当使用半开连接回收机制时,攻击者只需提高攻击频率就可使大部分正常的等待的半开连接,在ACK消息到来前就被踢出队列。
- 以上两个思路都没有直击症结所在—任何一个SYN消息无论来源是谁,都会消耗B的一些资源保存半开状态,并逐渐达到“鸠占鹊巢”的效果。SYN Cache和SYN Cookies就是基于这个观察提出的两个方案。
- SYN Cache的出发点主要是针对“鸠占鹊巢”问题,基本原理如下:构造一个全局的哈希表,用来缓存系统当前所有的半开连接信息,连接成功则从Cache中清除相关信息;Hash Table中每个桶(bucket)的容量大小也有限制,当桶“满”时做除旧迎新操作。当B收到一个SYN消息后,会将半开连接信息加入到Hash Table中,其中key的生成很关键,既要用到SYN消息中包含的信息(如:Source IP,Port等)又要做到很难被攻击者猜到,一般会通过一个秘密的函数生成,这样所有的半开连接无论好坏,都看似随机地被平均分配到了不同的“桶”中,使攻击难度大增,因为为达到DoS效果,攻击者需要使每个桶都达到填满状态,并且还要有足够快的“填桶”速度,使得正常的半开连接在还未完成建立前就被踢出桶,这样的攻击行为估计在达到目的前早就暴露了。
- SYN Cookies着眼点主要是设法消除半开连接的资源消耗,原理与HTTP Cookies技术类似,B通过特定的算法把半开连接信息编码成“Cookie”,用作B给A的消息编号(SequenceNum),随SYN-ACK消息一同返回给连接发起方A,这样在连接完全建立前B不保存任何信息。如果A是正常用户,则会向B发送最后一次握手消息(ACK),B收到后验证“Cookie”的内容并建立连接;如果A是攻击者,则不会向B反馈ACK消息,B也没任何损失,也就说是单纯的SYN攻击不会造成B的连接资源消耗。当然这种方案也有一定缺点,最明显的就是B不保存连接的半开状态,就丧失了重发SYN-ACK消息的能力,这一方面会降低正常用户的连接成功率,另一方面会导致某些情况下正常通信的双方会对连接是否成功打开产生误解,如A发给B的第三次握手消息(ACK)半路遗失,A认为连接成功了,B认为没收到ACK,连接没成功,这种情况就需要上层应用采取策略特别处理了。
- 当然,所有这些方案都不完美各有利弊,最终的策略可能是几种方案的结合使用,形成防御体系,将攻击提前化解在局部,不至于影响整个系统。
DDos攻击的原理介绍一下
- listen有一个队列,处理连接请求。在收到匿名IP发过来的SYN之后,会在listen队列中存放一个记录,但是队列容量是有限的,当这样的恶意请求过多的时候,listen队列里就塞满了这些无效的连接请求,然后装不下更多的连接记录了,所以就拒绝其他请求了
长连接、短连接、长轮询、短轮询
- 所谓短连接,及连接只保持在数据传输过程,请求发起,连接建立,数据返回,连接关闭。它适用于一些实时数据请求,配合轮询来进行新旧数据的更替。短连接的流程就是:建立连接->发送数据->断开连接。一次发送之后立即断开连接。所以短连接不用一直占用服务器资源,可以让服务器空出资源来解决其他的连接。
- 长连接便是在连接发起后,在请求关闭连接前客户端与服务端都保持连接,实质是保持这个通信管道,之后便可以对其进行复用。长连接需要注意的是长连接并不是永久连接的。如果一段时间内,这个连接没有HTTP请求发出的话,那么这个长连接就会被断掉。它适用于涉及消息推送,请求频繁的场景(直播,流媒体)。连接建立后,在该连接下的所有请求都可以重用这个长连接管道,避免了频繁了连接请求,提升了效率。长连接最重要的是可以复用TCP连接,这样减少了每次建立连接和断开连接的开销。所以现在大部分都是长连接,例如一个页面有很多文件,JS文件,CSS文件等等,不能每次都要建立一次连接,所以长连接是比较好的解决办法。
- 所谓轮询,即是在一个循环周期内不断发起请求来得到数据的机制。只要有请求的地方,都可以实现轮询,譬如各种事件驱动模型。它的长短是在于请求的返回周期。
短轮询指的是在循环周期内,不断发起请求,每一次请求都立即返回结果,根据新旧数据对比决定是否使用这个结果。缺点如下
- 页面会出现假死:在等到每次 EventLoop 时,都要判断是否到指定时间,直到时间到再执行函数,一旦遇到页面有大量任务或者返回时间特别耗时,页面就会出现假死
- 无用的网络传输:当客户端按固定频率向服务器发起请求,数据可能并没有更新,浪费服务器资源。
长轮询即是在请求的过程中,若是服务器端数据并没有更新,那么则将这个连接挂起,直到服务器推送新的数据,再返回,然后进入循环周期。客户端像传统轮询一样从服务端请求数据,服务端会阻塞请求不会立刻返回,直到有数据或超时才返回给客户端,然后关闭连接,客户端处理完响应信息后再向服务器发送新的请求。长轮询解决了频繁的网络请求浪费服务器的问题,资源可以及时返回给浏览器。缺点如下
- 保持连接会消耗资源
- 服务器没有返回有效数据,程序超时
- 对于客户端来说,不管是长轮询还是短轮询,客户端的动作都是一样的,就是不停的去请求,不同的是服务端,短轮询情况下服务端每次请求不管有没有变化都会立即返回结果,而长轮询情况下,如果有变化才会立即返回结果,而没有变化的话,则不会再立即给客户端返回结果,直到超时为止。这样一来,客户端的请求次数将会大量减少。但是长轮询也是有坏处的,因为把请求挂起同样会导致资源的浪费,假设还是1000个人停留在某个商品详情页面,那就很有可能服务器这边挂着1000个线程,在不停检测库存量,这依然是有问题的。从这里可以看出,不管是长轮询还是短轮询,都不太适用于客户端数量太多的情况,因为每个服务器所能承载的TCP连接数是有上限的,这种轮询很容易把连接数顶满。
了解TCP连接的11种状态变迁
- 连接
全部11种状态
- 客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT 。
- 服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK 。
- 共有的:(1)CLOSED (2)ESTABLISHED 。
状态变迁
- 一开始,建立连接之前服务器和客户端的状态都为CLOSED。服务器创建socket后开始监听,变为LISTEN状态。客户端请求建立连接,向服务器发送SYN报文,客户端的状态变为SYN_SENT。服务器收到客户端的报文后向客户端发送ACK和SYN报文,此时服务器的状态变为SYN_RCVD。然后,客户端收到ACK、SYN,就向服务器发送ACK,客户端状态变为ESTABLISHED,服务器收到客户端的ACK后也变为ESTABLISHED。此时,3次握手完成,连接建立
http
一个完整的http过程是怎样的?或浏览器输入 www.qq.com 发生了什么,第二次访问与第一次有什么区别吗
- 对网址进行DNS域名解析,得到对应的IP地址
- 根据这个IP,找到对应的服务器,发起TCP的三次握手
- 建立TCP连接后发起HTTP请求
- 服务器响应HTTP请求,浏览器得到html代码
- 浏览器解析html代码,并请求html代码中的资源(如js、css图片等)(先得到html代码,才能去找这些资源)
- 浏览器对页面进行渲染呈现给用户
http有什么缺点、https的过程,https的过程中是否会有安全隐患。说下https解决了什么问题,怎么解决的?说下https的握手过程。
http的缺点
- 容易被监听:http通信都是明文,数据在客户端与服务器通信过程中,任何一点都可能被劫持。比如,发送了银行卡号和密码,hacker劫取到数据,就能看到卡号和密码,这是很危险的
- 被伪装:http通信时,无法保证通行双方是合法的,通信方可能是伪装的。比如你请求你怎么知道返回的数据就是来自淘宝,中间人可能返回数据伪装成淘宝。
- 被篡改:hacker中间篡改数据后,接收方并不知道数据已经被更改
https很好的解决了http的三个缺点(被监听、被篡改、被伪装),https不是一种新的协议,它是http+SSL(TLS)的结合体,SSL是一种独立协议,所以其它协议比如smtp等也可以跟ssl结合。https改变了通信方式,它由以前的http—–>tcp,改为http——>SSL—–>tcp;https采用了共享密钥加密+公开密钥加密的方式
- 防监听:数据是加密的,所以监听得到的数据是密文
- 防伪装:伪装分为客户端伪装和服务器伪装,通信双方携带证书,证书相当于身份证,有证书就认为合法,没有证书就认为非法,证书由第三方颁布,很难伪造
- 防篡改:https对数据做了摘要,篡改数据会被感知到。hacker即使从中改了数据也白搭。
https过程
- 客户端发送请求到服务器端
- 服务器端返回证书和公开密钥,公开密钥作为证书的一部分而存在
- 客户端验证证书和公开密钥的有效性,如果有效,则生成共享密钥并使用公开密钥加密发送到服务器端
- 服务器端使用私有密钥解密数据,并使用收到的共享密钥加密数据,发送到客户端
- 客户端使用共享密钥解密数据
- SSL加密连接建立
HTTP请求报文和响应报文的格式和内容(HTTP报文的4要素、常见的请求首部和响应首部),说出http有哪些请求方法:PUT,DELETE,POST,GET,HEAD,TRACE的区别(前4个对应增删改查,重点掌握GET和POST的区别)
一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据。
- 请求行:请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1
- 请求头部:HTTP客户程序,向服务器发送请求的时候必须指明请求类型(一般是GET或者 POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说 Content-Length必须出现。
- 空行:它的作用是通过一个空行,告诉服务器请求头部到此为止。
- 请求数据:若方法字段是POST,则通常来说此处放置的就是要提交的数据
http 100 206 是什么。。304是什么(文件未变化,客户端缓冲的文件可以继续使用)?取缓存?好说一下过程,然后讲一下304中xxxxx的header有什么作用。http2.0了解吗?做了什么优化处理
WebSocket是怎么建立连接的
cookie的作用
Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。就像我们去超市买东西,没有积分卡的情况下,我们买完东西之后,超市没有我们的任何消费信息,但我们办了积分卡之后,超市就有了我们的消费信息。cookie就像是积分卡,可以保存积分,商品就是我们的信息,超市的系统就像服务器后台,http协议就是交易的过程。
session机制采用的是在服务器端保持状态的方案,而cookie机制则是在客户端保持状态的方案,cookie又叫会话跟踪机制。打开一次浏览器到关闭浏览器算是一次会话。说到这里,讲下HTTP协议,前面提到,HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。此时,服务器无法从链接上跟踪会话。cookie可以跟踪会话,弥补HTTP无状态协议的不足。
cookie分为会话cookie和持久cookie,会话cookie是指在不设定它的生命周期expires时的状态,前面说了,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话cookie就会跟随浏览器而销毁。当关闭一个页面时,不影响会话cookie的销毁。会话cookie就像我们没有办理积分卡时,单一的买卖过程,离开之后,信息则销毁。
持久cookie则是设定了它的生命周期expires,此时,cookie像商品一样,有个保质期,关闭浏览器之后,它不会销毁,直到设定的过期时间。对于持久cookie,可以在同一个浏览器中传递数据,比如,你在打开一个淘宝页面登陆后,你在点开一个商品页面,依然是登录状态,即便你关闭了浏览器,再次开启浏览器,依然会是登录状态。这就是因为cookie自动将数据传送到服务器端,在反馈回来的结果。持久cookie就像是我们办理了一张积分卡,即便离开,信息一直保留,直到时间到期,信息销毁。
nginx
网站多用户访问时会出现什么问题?如何优化?
网络编程服务器端的接口调用顺序
NIO和BIO之类的了解吗?
五种I/O模型:
1)阻塞I/O
2)非阻塞I/O
3)I/O复用(select和poll)
4)信号驱动I/O(SIGIO)
5)异步I/O怎么理解阻塞I/O
- 假设有一个管道,线程A从管道中写数据,线程B向管道中写数据。
首先讨论缓冲区的概念,缓冲区的引入是为了减少频繁I/O操作而引起频繁的系统调用(它很慢的),当你操作一个流时,更多的是以缓冲区为单位进行操作,这是相对于用户空间而言。对于内核来说,也需要缓冲区。
缓冲区满之后,产生I/O事件,告诉线程B去读缓冲区,同时线程A阻塞。
缓冲区空之后,产生I/O事件,告诉线程A去写缓冲区,同时线程B阻塞。
在阻塞模式下,一个线程只能处理一个I/O事件。
- 假设有一个管道,线程A从管道中写数据,线程B向管道中写数据。
服务器网络编程的代码大致流程
对于socket编程,accept方法是干什么的,在三次握手中属于第几次,可以猜一下,为什么这么觉得。
在 socket 编程中,服务端调用了 bind listen 但是没有调用 accept ,而是调用了 sleep(1000) ,然后客户端 调用 connect 向已经 sleep 的服务端发起请求,问这个 connect 会不会返回。我回答会醒过来,面试官继续问,如果醒过来后,服务端没有写 accept ,那么 connect 会不会返回。
怎么样设计一个比较好的函数完全读出一个socket的所有数据
connect 在 TCP 握手的哪个阶段会返回,是三次握手完成后还是在中间某个阶段返回
backlog的作用,编程中应该设置为多大
创建一个 socket 并且加入到 epoll 中,现在把这个 socket 给 close 掉,那么这个被 close 的 socket 还在不在 epoll 中(没有主动调用 epoll_ctl 去删除),如果这时用 epoll_ctl 去删除它,还能不能删除掉。
TIME_WAIT的话那你来解释一下它的作用吧(两个),如何避免(socket选项和线程池)
假设现在系统中有很多处于TIME_WAIT的连接,这个时候你会怎么做(有一个socket选项,可以进行地址重用,我们可以可重用这些处于TIME_WAIT状态的连接的地址,没有问题的)
假设现在网络延迟很高,但是又没有发生丢包,这个时候用tcp进行传输你觉得速率怎么样?如何解决?(如果网络延迟很高,但是实际上网络没有发生丢包的话,tcp的拥塞控制可能会进行快重传使发送速率下降。从应用层封装udp协议或者使用raw socket直接封装ip数据报)
FTP服务器怎么实现(因为自己有写过),多线程磁盘读写效率(由于磁盘寻道随机性增加而导致I/O效率呈线性下降),是否考虑到断点续传。为什么要用epoll,优缺点是什么,epoll的两种方式,各有什么不同。

