文章目录
  1. 1. 层次结构总述
  2. 2. 应用层
  3. 3. 传输层
  4. 4. 网络层
  5. 5. 数据链路层
  6. 6. 网络编程

通常现在的计算机网络都采用一种分层模型来构建,本文也以分层的结构来梳理计算机网络常见面试问题。

层次结构总述

网络体系结构?

有两种模型,一种是7层网络模型,也就是OSI模型,一种是4层模型,也就是TCP/IP模型。
前者模型是从底向上一步一步进行抽象,分别是物理层、数据链路层、网络层、传输层、会话层、表示层,应用层。具体如下图表示,高层是底层的一种抽象。

4层模型是将7层模型进行了浓缩,将底下两层,合并成为了数据链路层,网络层和传输层单独,最上面3层合并成为了应用层,具体对应关系如下图所示

一般现在用到的都是TCP/IP 4层网络模型,和7层模型相比,要简单不少,同时7层模型分层分的太过于细腻,导致实现起来也比较麻烦,而4层模型已经可以较好的满足现实世界中的业务模型,因此 TCP/TP 4 层模型成为了事实上的标准。

OSI模型中,一个协议应该属于哪一层是以什么为标准划分的?

一个协议归属于OSI参考模型哪一层,主要根据该层可以提供什么样的服务。这个服务如果拘泥于一条链路,则为数据链路层;如果服务可以让终端的流量可以跨越路由器的不同接口在互联网穿梭,则为网络层。如果可以提供或可靠、或不可靠端对端服务的,则为传输层。可以给用户提供服务的则为应用层。

最好一个全流程理解计算机网络的例子如下:

浏览器中输入一个URL发生什么,用到那些协议?

浏览器中输入URL,首先浏览器要将URL解析为IP地址,解析域名就要用到DNS协议,首先主机会查询DNS的缓存,如果没有就给本地DNS发送查询请求。DNS查询分为两种方式,一种是递归查询,一种是迭代查询。如果是迭代查询,本地的DNS服务器,向根域名服务器发送查询请求,根域名服务器告知该域名的一级域名服务器,然后本地服务器给该一级域名服务器发送查询请求,然后依次类推直到查询到该域名的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议。
得到IP地址后,浏览器就要与服务器建立一个http连接。因此要用到http协议,http协议报文格式上面已经提到。http生成一个get请求报文,将该报文传给TCP层处理。如果采用https还会先对http数据进行加密。TCP层如果有需要先将HTTP数据包分片,分片依据路径MTU和MSS。TCP的数据包然后会发送给IP层,用到IP协议。IP层通过路由选路,一跳一跳发送到目的地址。当然在一个网段内的寻址是通过以太网协议实现(也可以是其他物理层协议,比如PPP,SLIP),以太网协议需要直到目的IP地址的物理地址,有需要ARP协议。

应用层

应用层包括两个主要的协议:HTTP 和 DNS

HTTP/HTTPS 1.0/1.1/2.0的特点和区别?

HTTPS是基于安全套接字层的超文本传输协议,HTTPS在HTTP应用层的基础上使用安全套接字层作为子层。HTTPS = HTTP + SSL。

HTTP和HTTPS的区别如下:
(1) 安全性:HTTP是超文本传输协议,信息是明文传输,容易发生流量劫持和HTTP攻击,HTTPS进行SSL加密传输、身份认证,比HTTP安全;
(2) 费用:HTTP免费,HTTPS的CA证书需要一定费用;
(3) 端口:HTTP标准端口为80,HTTPS标准端口号为443。
(4) 防劫持:HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。

HTTP1.0/1.1/2.0的特点和区别如下:
(1) HTTP1.0规定浏览器与服务器保持较短时间的链接,链接无法复用,易产生线头阻塞;
(2) HTTP1.1支持持久链接,增加请求头和响应头来扩充功能,包括断点续传、缓存处理和host头处理;
(3) HTTP2.0支持多路复用、首部压缩、流量控制和服务端推送。

HTTP中,session 和 cookie 区别?禁用cookie后怎么办?

Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session也会失效。
Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies。

那么,在客户端禁用Cookie的时候,我们要怎么做呢,可以有以下两种方法
1)设置php.ini中的session.use_trans_sid = 1或者在PHP编译时打开–enable-trans-sid选项,让PHP自动通过URL传递session id。
2)如果是虚拟主机或者租用的服务器,无法去修改PHP.ini,那么可以手动通过URL传值,或者通过隐藏表单传递session id。说简单些就是自己去操纵sessionid这个唯一标识符,去鉴别用户即可。

域名解析的过程?DNS的工作过程?

1)检查浏览器缓存中有没有
2)检查本地系统hosts文件中有没有
3)检查本地路由器中有没有
4)ISP的本地DNS服务器:ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS查询请求。
5)根服务器:ISP的DNS服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS服务器先问根域名服务器.com域名服务器的IP地址,然后再问.com域名服务器,依次类推)。

DNS欺骗的方式?

hosts文件篡改
本机DNS劫持
DNS通讯包篡改
SYN Flood:SYN-FLOOD是一种常见的DDos攻击,拒绝服务攻击。通过网络服务所在的端口发送大量伪造原地址的攻击报文,发送到服务端,造成服务端上的半开连接队列被占满,从而阻止其他用户进行访问。
它的数据报特征是大量syn包,并且缺少最后一步的ACK回复。

传输层

传输层是重头戏,其中包括两个主要协议TCP,UDP。其中TCP提供可靠的有保证的传输,涉及的知识点比较多,面试中也被问得比较多。

TCP 和 UDP 有什么区别?

最基本的是基于连接和无连接的区别,这是因为协议的目的是不一样的。TCP实现的是可靠传输,因此就需要进行拥塞控制和流量控制,超时重传,乱序分包进行重排等相关实现。而UDP是没有连接的概念,只管发送出去,也不管是否可达或者是是否可以收到回应报文。因此这就体现在TCP报文首部需要20个字节以上,而UDP报文首都需要8个字节就可以了。
TCP与UDP区别总结:
1)TCP面向连接(如打电话要先拨号建立连接); UDP是无连接的,即发送数据之前不需要建立连接
2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流; UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5)TCP首部开销20字节(32bit x 5); UDP的首部开销小,只有8个字节(16位源端口,16位目的端口,16位长度,16位校验和)
6)TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

TCP 和 UDP 的应用场景?

从特点上,TCP 是可靠的但传输速度慢 ,UDP 是不可靠的但传输速度快。因此在选用具体协议通信时,应该根据通信数据的要求而决定。
  若通信数据完整性需让位与通信实时性,则应该选用 TCP 协议(如文件传输、重要状态的更新等);反之,则使用 UDP 协议(如视频传输、实时通信等)。
  
如何实现可靠的UDP?

自定义通讯协议,在应用层定义一些可靠的协议,比如检测包的顺序,重复包等问题,如果没有收到对方的ACK,重新发包。
UDP没有Delievery Garuantee,也没有顺序保证,所以如果你要求你的数据发送与接受既要高效,又要保证有序,收包确认等,你就需要在UDP协议上构建自己的协议。比如RTCP,RTP协议就是在UPD协议之上专门为H.323协议簇上的IP电话设计的一种介于传输层和应用层之间的协议。

简单来讲,要使用UDP来构建可靠的面向连接的数据传输,就要实现类似于TCP协议的超时重传,有序接受,应答确认,滑动窗口流量控制等机制,等于说要在传输层的上一层(或者直接在应用层)实现TCP协议的可靠数据传输机制,比如使用UDP数据包+序列号,UDP数据包+时间戳等方法,在服务器端进行应答确认机制,这样就会保证不可靠的UDP协议进行可靠的数据传输,不过这好像也是一个难题!

UDP中一个包的大小最大能多大?

相对于不同的系统,不同的要求,其得到的答案是不一样的。
1)局域网环境下,建议将UDP数据控制在1472字节以下。
以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的,这个1500字节被称为链路层的MTU(最大传输单元)。但这并不是指链路层的长度被限制在1500字节,其实这个MTU指的是链路层的数据区,并不包括链路层的首部和尾部的18个字节。所以,事实上这个1500字节就是网络层IP数据报的长度限制。因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节。而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的。又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节。这个1472字节就是我们可以使用的字节数。
当我们发送的UDP数据大于1472的时候会怎样呢? 这也就是说IP数据报大于1500字节,大于MTU,这个时候发送方IP层就需要分片(fragmentation)。把数据报分成若干片,使每一片都小于MTU,而接收方IP层则需要进行数据报的重组。这样就会多做许多事情,而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方无法重组数据报,将导致丢弃整个UDP数据报。因此,在普通的局域网环境下,建议将UDP的数据控制在1472字节以下为好。

2)Internet编程时,建议将UDP数据控制在548字节以下
进行Internet编程时则不同,因为Internet上的路由器可能会将MTU设为不同的值。如果我们假定MTU为1500来发送数据,而途经的某个网络的MTU值小于1500字节,那么系统将会使用一系列的机制来调整MTU值,使数据报能够顺利到达目的地,这样就会做许多不必要的操作。鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时,最好将UDP的数据长度控件在548字节以内。

下面说重头戏,TCP:

详细说明TCP状态迁移过程?

客户端:CLOSED初始态,发送SYN,进入SYN_SENT,收到SYN+ACK,发送ACK,进入ESTABLISHED状态,然后需要结束通信,发送FIN,进入FIN_WAIT1,收到服务器的ACK,进入FIN_WAIT2,收到服务器的FIN,发送ACK,进入TIME_WAIT,结束进入(回到)CLOSED状态。
服务端:CLOSED初始态,进入LISTEN状态,收到SYN,发送SYN+ACK,进入SYN_RCVD状态,收到ACK(第三次握手),进入ESTABLISHED状态,收到FIN,发送ACK,进入CLOSED_WAIT状态,等自己数据发完,发送FIN,进入LAST_ACK状态,如果成功,进入(返回)CLOSED状态。

TCP三次握手和四次挥手,以及各个状态的作用?

三次握手用于进行连接
第一次握手:建立连接时,客户端发送SYN包,例如seq = 200 ,SYN = 1 (200是随机产生的一个值,TCP规定SYN = 1的时候不能携带数据)给服务器,然后Socket进入SYN_SENT状态
第二次握手:服务器收到SYN报文段进行确认,发一个将SYN=1,ACK = 1,seq = 300,ack = 201(ack确认号为收到的序列号+1,ACK=1表示确认号有效,建立成功全程都是1,300是随机生成的)
第三次握手:客户端再进行一次确认,发一个ACK = 1,seq = 201,SYN = 0,ack = 301,ACK = 1(seq为第一次发送的seq+1,SYN从此都是0,ack为服务器确认的seq+1,ACK从此都是1)
建立成功 之后就可以相互发送数据报文了。
四次挥手用于断开连接
tcp四次挥手,由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
第一次挥手: 机1向主机2发送FIN报文段,表示关闭数据传送,并主机1进入FIN_WAIT_1状态,表示没有数据要传输了
第二次挥手:机2接受到FIN报文后进入CLOSE_WAIT状态(被动关闭),然后发送ACK确认,表示同意了主机1的关闭请求,这个时候主机1进入到FIN_WAIT_2状态,这个状态是相对来讲稍微持久一点的。
第三次挥手:机2等待主机1发送完数据,发送FIN到主机1请求关闭,主机2进入LAST_ACK状态,
第四次挥手:机1收到主机2发送的FIN后,回复ACK确认到主机2,主机1进入TIME_WAIT状态,主机2收到主机1的ACK后就关闭连接了,状态为CLOSED,主机1等待2MSL,仍然没有收到主机2的回复,说明主机2已经正常关闭了,主机1关闭连接
MSL(Maximum Segment Lifetime):报文最大生存时间,是任何报文段被丢弃前在网络内的最长时间。当主机1回复主机2的FIN后,等待(2-4分钟),即使两端的应用程序结束。

三次握手为什么不是两次或者四次?

两次太少,因为最后的客户端发往服务器的ACK至为重要,因为两次的话,B无法确定B的信息A是否能收到。
四次太多,因为服务器发送的SYN和对客户端的ACK可以合并为一个报文段发送,因为中间没有任何其他数据传输。如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。

2MSL是什么状态?作用是什么?

是TIME_WAIT,主要有两个作用:
1)保留端口不被复用,以接受服务器可能重传的FIN。如果不这样做,那么客户端收到重发的FIN会返回一个RST报文段,而服务器期待的是正常的ACK,所以会报错。
2)保证双向的包都在网络中消失。如果不这样做,在此端口立马启动另一个相同/相似的连接,则他们可能会收到之前连接的报文段,显然不好,2MSL就能保证之前的连接的所有报文段都在网络中消失。

time_wait,close_wait状态产生的原因,keepalive?

TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得注意的状态有两个:CLOSE_WAIT和TIME_WAIT。

CLOSE_WAIT 是被动关闭连接时形成的。根据TCP状态机,服务器端收到客户端发送的FIN,则按照TCP实现发送ACK,因此进入CLOSE_WAIT状态。但如果服务器端不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接。此时,可能是系统忙于处理读、写操作,而未将已收到FIN的连接,进行close。此时,recv/read已收到FIN的连接socket,会返回0。

解决CLOSE_WAIT的方法:
1 一般原因都是TCP连接没有调用关闭方法。需要应用来处理网络链接关闭。
2 对于Web请求出现这个原因,经常是因为Response的BodyStream没有调用Close。
3 TCP的KeepLive功能,可以让操作系统替我们自动清理掉CLOSE_WAIT的连接。但是KeepLive在Windows操作系统下默认是7200秒,也就是2个小时才清理一次。往往满足不了要求。可以调小该数值。

close_Wait引发的问题: close_Wait会占用一个连接,网络可用连接小。数量过多,可能会引起网络性能下降,并占用系统非换页内存。尤其是在有连接池的情况下(比如HttpRequest)会耗尽连接池的网络连接数,导致无法建立新的网络连接。

大量TIME_WAIT存在什么问题,如何解决?

ip_local_port_range范围不到3w,大量的TIME_WAIT状态使得local port在TIME_WAIT持续期间不能被再次分配,即没有可用的local port,这将是导致新建连接失败的最大原因。

解决方案:
1)修改系统配置
具体来说,需要修改本文前面介绍的tcp_max_tw_buckets、tcp_tw_recycle、tcp_tw_reuse这三个配置项。
2)修改应用程序
具体来说,可以细分为两种方式:
1/ 将TCP短连接改造为长连接。通常情况下,如果发起连接的目标也是自己可控制的服务器时,它们自己的TCP通信最好采用长连接,避免大量TCP短连接每次建立/释放产生的各种开销;如果建立连接的目标是不受自己控制的机器时,能否使用长连接就需要考虑对方机器是否支持长连接方式了。
2/ 通过getsockopt/setsockoptapi设置socket的SO_LINGER选项
3/ 服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。

TCP重传机制?

TCP是一种可靠的协议,在网络交互的过程中,由于TCP报文是封装在IP协议中的,IP协议的无连接特性导致其可能在交互的过程中丢失,在这种情况下,TCP协议如何保障其传输的可靠性呢?依靠确认/重传机制。
重传又分为超时重传和快速重传。
1)超时重传
说白了就是在请求包发出去的时候,开启一个计时器,当计时器达到时间之后,没有收到ACK,则就进行重发请求的操作,一直重发直到达到重发上限次数或者收到ACK。发送端的等待的时间叫RTO(Retransmission TimeOut).
快速重传
2)还有一种机制就是快速重传,Fast Retransmit是由接收端主动要求重发的,当接收方收到的数据包是不正常的序列号,那么接收方会重复把应该收到的那一条ACK重复发送,这个时候,如果发送方收到连续3条的同一个序列号的ACK,那么就会启动快速重传机制,把这个ACK对应的发送包重新发送一次。

TCP拥塞控制?

TCP 的拥塞控制方法主要有四种:慢开始、拥塞避免、快重传和快恢复。
为了简化问题,在下面的讨论中,我们作出如下两个假设:
a 数据单向传输;
b 接收方的缓存是无限大的,窗口大小仅由网络的拥塞程度决定;
(1) 慢开始和拥塞避免
这里存在这样一个原则:只要网络没有出现拥塞,拥塞窗口 cwnd(可以理解为发送窗口)就可以无限增大,以便于单次发送更多的数据,但是只要网络发生了拥塞,就必须把拥塞窗口减小,以减少涌入网络的数据量,从而减轻网络的拥塞程度。
但是发送方如何知道网络发生了拥塞呢?当网络发生拥塞时,路由器的缓存便处于爆满状态,此时它不得不丢弃一部分分组。这样发送方就不可能收到确认报文,最终导致等待超时。现代网络线路的传输质量都很好,因为硬件层面的问题导致丢弃分组的情况是很少发生的。也就是说,出现超时便代表着网络出现了拥塞。
本来是通过控制拥塞窗口的大小来实现 TCP 的拥塞控制,但是为了更好地解释慢开始的原理,我们将报文段的个数作为拥塞窗口的单位,即从原来的增加窗口大小变为了增加报文段的个数。这样可以用较小的数字来说明拥塞控制的原理。
慢开始算法的“慢”不是指增长慢,而是指最开始的时候,从一个较低的起点开始增长。
值得注意的是,上面的结论并不具有实际意义,因为拥塞窗口只要收到一个新的确认报文就会增大(假如这个确认报文段使得窗口增大),并且立即发送新窗口中的数据,而不必等到该轮次中所有的报文段都成功确认。
同时为了避免拥塞窗口无限增大造成网络拥塞,需要设置一个慢开始门限 ssthresh。拥塞窗口 cwnd 的大小和慢开始门限 ssthresh 之间存在如下的关系:
a 当 cwnd < ssthresh 时,使用慢开始算法;
b 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法;
在慢开始阶段,拥塞窗口的大小呈指数增加;
到达慢开始门限后,采用拥塞避免算法,窗口大小依次加 1,呈线性增长趋势;
当窗口 cwnd = 24 时,出现了超时,此时发送方判断网络发生了阻塞,于是调整 ssthresh = cwnd/2 = 12,同时重新设置窗口 cwnd = 1;
接下来继续慢开始算法,达到新的门限 ssthresh = 12 时改用拥塞避免算法,窗口值呈线性增大;
当窗口值 cwnd = 16 时,出现了新的情况:发送方连续收到了 3 个对于同一报文段的重复确认(图中的 3-ACK),这个问题将在快重传和快恢复中继续讨论;

(3) 快重传和快恢复
我们先考虑这样一种情况:有时候,确认报文段只是在网络传输中丢失了,但是其实网络并没有发生拥塞。由于发送方接收不到确认报文段而导致超时的发生,此时便会开启慢开始算法,然后将窗口置为 1,导致传输效率降低。
在下面的图中,发送方先发送了 M1 和 M2,并且成功收到了它们的确认报文段,但是接下来发送的 M3 却丢失了。由于接受方此时并不知道发送方向其发送了 M3,所以此时它做出任何响应。当接受方接收到发送方继续发送的 M4 的时候,经过的简单对比,它知道 M3 肯定是丢失了,于是立刻向发送方发出对 M2 的重复确认,对于后面的 M5、M6 也是如此。于是接收方一共收到了 4 个对 M2 的确认,除去第一个之外,后面的三个都是重复确认,也即图中的 3-ACK。此时,快重传算法规定,当发送方连续收到 3 个重复确认的时候,就必须立即对丢失的报文段进行重传(即快重传),这样便不会导致超时的发生,从而避免了之前的情况。
接下来对于图中的 3-ACK 点来说,发送方知道只是丢失了部分报文段,于是执行快恢复算法,调整 ssthresh = cwnd/2 = 8,同时设置 cwnd = ssthresh = 8,然后接着开始执行拥塞避免算法。

另外,我们在刚开始的时候,假定发送窗口的大小只会受到网络的影响。但是接收方的缓存肯定是有限的,也就是说:接收窗口 rwnd 和拥塞窗口 cwnd 二者共同制约着发送窗口的大小。显然,三者之间存在下图中的关系:

发送窗口上限 = Min[rwnd, cwnd]

上述公式说明,每次对发送窗口进行调整时,需要根据当前的网络状况和接收窗口的大小而定。

TCP的滑动窗口?

滑动窗口是发送方通过接受方的确认报文段中描述的缓冲区的大小来计算自己最多能发送多长的数据。如果发送方收到接受方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接受方发送窗口大小不为0的数据报的到来。
缓存与滑动窗口的关系
我们在上面的例子中所提到的字节流实际上是位于发、接双方的缓存中的,窗口在字节流上滑动,对发送和接收的字节流进行控制。
在发送方这边,发送窗口后沿的字节会被删去,应用进程写入的新的字节又会添加到字节流的最前头。因此必须对应用程序写入字节的速度进行控制,否则会爆缓存。
在接收方这边,接收窗口后沿的数据只是在确认报文段中已经确认的数据,等待被应用进程读取,成功读取后才能删除。如果应用进程来不及读取,接收缓存就会被填满,最终导致接收窗口为 0,然后又会反馈到发送窗口那边,使发送停止。
值得说明的是,在上面的例子中,我们为了解释的方便,只考虑了一方发送、一方接收的情况。但是由于 TCP 是全双工通信,实际中任意一方都是发送窗口与接收窗口并存的。

TCP精髓问题:停止等待协议、连续ARQ协议

停止等待协议
停止等待协议是tcp保证传输可靠的重要途径,”停止等待”就是指发送完一个分组就停止发送,等待对方的确认,只有对方确认过,才发送下一个分组.
停止等待协议的优点是简单,但是缺点是信道的利用率太低。

连续ARQ协议
连续重发请求ARQ方案是指发送方可以连续发送一系列信息帧,即不用等前一帧被确认便可继续发送下一帧,效率大大提高。但在这种重发请求方案中,需要在发送方设置一个较大的缓冲存储空间(称作重发表),用以存放若干待确认的以及待发送信息帧。当发送方收到对某信息帧的确认帧后,便可从重发表中将该信息帧删除。所以,连续重发请求ARQ方案的链路传输效率大大提高,但相应地需要更大的缓冲存储空间。
在这一协议中,当发送站点发送完一个数据帧后,不是停下来等待应答帧,而是可以连续再发送若干个数据帧。如果在此过程中又收到了接收端发来的应答帧,那么还可以接着发送数据帧。由于减少了等待时间,整个通信的吞吐量就提高了。
ARQ代表的是自动重传请求(Auto Repeat reQuest,ARQ),而GBN与选择重传都属于其中。其中GBN的发送窗口>1,接收窗口=1,选择重传协议:发送窗口大小>1,接收窗口大于1。GBN协议中接收方可以发送累计确认帧ACK,而选择重传没有累计确认的特点。

说说Nagle算法?

背景:如果互联网上传递的都是小包,那绝对是个灾难,每个网络请求都耗费比较大的资源,如果一份数据分为零零散散很多份小包,每个网络传输都只传输一个小包,那么是典型的浪费资源,增加拥堵。
Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
Nagle算法规定了,发送方网络链路上一个连接只能有一个未获得ACK的请求包。这个就意味着,发送方只有等待上一个请求的ACK回来之后才能发送下一个请求,这样两个请求过程中间,发送方的缓存区就存储了足够滑动窗口大小的包进行传递,这样就有效避免了大量的小包产生。

Keepalive是什么东西,如何使用?

keepalive,是在TCP中一个可以检测死连接的机制。
keepalive 原理很简单,TCP会在空闲了一定时间后发送数据给对方:
1.如果主机可达,对方就会响应ACK应答,就认为是存活的。
2.如果可达,但应用程序退出,对方就发FIN应答,发送TCP撤消连接。
3.如果可达,但应用程序崩溃,对方就发RST消息。
4.如果对方主机不响应ack, rst,继续发送直到超时,就撤消连接。这个时间就是默认的二个小时。

Keepalive 和 应用层的 HeartBeat 的联系和区别?

Keepalive适用于清除死亡时间比较长的连接。
1)默认不开启,采用keepalive,它会先要求此连接一定时间没有活动(一般是几个小时),然后发出数据段,经过多次尝试后(每次尝试之间也有时间间隔),如果仍没有响应,则判断连接中断。可想而知,整个周期需要很长的时间(默认时长是2小时)。
2)keepalive只能检测连接是否存活,不能检测连接是否可用。比如服务器因为负载过高导致无法响应请求但是双方的连接仍然存在,此时keepalive无法判断该连接是否可用。
3)如果TCP连接中的另一方因为停电突然断网等非正常断开的现象,由于服务器端(被动连接/断开的一方)并不知道客户端已断开连接,此时若服务器正在发送数据,那么会导致数据发送失败并进行数据重传,由于重传包的优先级要高于keepalive的数据包,因此keepalive的数据包无法及时发送出去。

所以,需要一种方法能够清除和回收那些在系统不知情的情况下死去了很久的连接,keepalive是非常好的选择。

在大部分情况下,特别是分布式环境中,我们需要的是一个能够快速或者实时监控连接状态的机制,这里,heart-beat才是更加合适的方案。

它们的不同点在于,keepalive是tcp实现中内建的机制,是在创建tcp连接时通过设置参数启动keepalive机制;而heart-beat则需要在tcp之上的应用层实现。一个简单的heart-beat实现一般测试连接是否中断采用的时间间隔都比较短,可以很快的决定连接是否中断。并且,由于是在应用层实现,因为可以自行决定当判断连接中断后应该采取的行为,而keepalive在判断连接失败后只会将连接丢弃。

TCP中已有SO_KEEPALIVE选项,为什么还要在应用层加入心跳包机制,或者说,光用keepalive够么?

主要是因为TCP协议中的SO_KEEPALIVE有几个致命的缺陷:
1、keepalive只能检测连接是否存活,不能检测连接是否可用。比如服务器因为负载过高导致无法响应请求但是双方的连接仍然存在,此时keepalive无法判断该连接是否可用。
2、如果TCP连接中的另一方因为停电突然断网等非正常断开的现象,由于服务器端(被动连接/断开的一方)并不知道客户端已断开连接,此时若服务器正在发送数据,那么会导致数据发送失败并进行数据重传,由于重传包的优先级要高于keepalive的数据包,因此keepalive的数据包无法及时发送出去。
3、当重传超过一定次数,TCP协议会发送keepalive探测包到客户端,一旦探测包没有返回,服务器端会以keepaliveinterval的频率继续发送探测包,经过若干次重试,若服务器一直没有收到应答就会认为该TCP连接已经断开(默认时长是2小时),而2小时以内这个连接一直不会断开,浪费系统资源。

什么是TCP的自连接,如何解决?

TCP自连接,就是出现源ip和源端口通目的ip和目的端口完全相同的情况。
解决之道:
1)可以做一个检测。
2)Server端程序的端口号最好不要选择ip_local_port_range区间内的端口,这样Client如果使用随机端口是在ip_local_port_range区间内,这样也就不会发生本机上的自连接。
如果Server端的端口号已经固定,并在 ip_local_port_range区间内,那么可以设置 ip_local_reserved_ports 为该Server端的端口号,那么Client就不会使用ip_local_reserved_ports中的值作为随机端口。

TCP带外数据?

带外数据即就是优先数据,linux系统的套接字机制支持低层协议发送和接受带外数据。但是TCP协议没有真正意义上的带外数据。为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机制。TCP在报文头中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理。

Linux 中每个 TCP 连接最少占用多少内存?

维持一个tcp连接需要占用哪些资源,下面就总结一下最近学习的内容,不足之处,请读者多多指正。
一个tcp连接需要:1,socket文件描述符;2,IP地址;3,端口;4,内存
TCP连接的四元组:源IP 源端口 目标IP 目标端口,这四元组构成了一个唯一的tcp连接。

对于一台服务器,我们假设只有一个网卡,那么就对应一个唯一的IP地址,而监听端口,我们可以在1024-65535之间任选一个。通过这个监听端口,我们接收来自客户端的连接请求。那么,它的IP、端口已经确定了,下面就是讨论socket文件描述符合内存了。

对于文件描述符fd,每个tcp连接占用一个,那么一个文件描述符下的文件大约占1K字节,而内核对这块也有说明,文件描述符建议最多占用10%的内存,如果是8G内存,那么就相当于800M即80万个文件描述符,当然,这个数据也可以通过linux参数调优进行调节,我在之前的一篇章节中也有讨论到,请大家参考:http://blog.csdn.net/fox_hacker/article/details/41148115

而对于内存,tcp连接归根结底需要双方接收和发送数据,那么就需要一个读缓冲区和写缓冲区,这两个buffer在linux下最小为4096字节,可通过cat /proc/sys/net/ipv4/tcp_rmem和cat /proc/sys/net/ipv4/tcp_wmem来查看。所以,一个tcp连接最小占用内存为4096+4096 = 8k,那么对于一个8G内存的机器,在不考虑其他限制下,最多支持的并发量为:810241024/8 约等于100万。此数字为纯理论上限数值,在实际中,由于linux kernel对一些资源的限制,加上程序的业务处理,所以,8G内存是很难达到100万连接的,当然,我们也可以通过增加内存的方式增加并发量。

网上也有人做过相关试验,程序接收1024000个连接,共消耗7,5G内存,即每个连接消耗在8K左右。

TCP 能否发送0字节的数据包?

1)tcp和udp都能发送0字节的数据包,windows下通过GetLastError查看返回值为0
2)tcp发送0字节的数据包后,接收方调用recv不会接收到该数据包
3)udp发送0字节的数据包后,接收方调用recvfrom能够接收到该数据包

TCP 协议中为什么syn会消耗一个序号?

总的来说,tcp是用“确认”这个手段来保证可靠的,在tcp整个过程中,SYN,FIN这两个命令,以及数据传输都是需要确认的,这样才能保证可靠。

HTTP协议和TCP的区别?

TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
Http协议底层就是使用TCP协议来进行实现的。
Http协议帮我们实现了消息边界的划分,因为TCP协议是流式协议,消息是没有边界的,如果希望消息有边界,就需要自己进行消息边界的划分,常用的有两种方式,一种是以\r\n作为消息结束的标志,FTP协议就是使用这种方式,另外一种是包长+包体的形式。HTTP协议采用的混合模式,头部信息是通过\n来进行消息边界的划分,数据信息采用的是包长+包体的格式来进行消息边界的划分。

网络层

ARP的机制?

ARP:地址解析协议,是将IP地址解析为物理地址(MAC地址)的协议,用来解决同一个局域网上的主机或路由器的IP地址和硬件地址的映射问题。ARP高速缓存用来存放本局域网中的各主机和路由器中的IP地址到MAC地址的映射表。ARP缓存表采用了老化机制,在一段时间内如果表中的某一行没有使用,就会被删除,这样可以大大减少ARP缓存表的长度,加快查询速度。

ARP的工作原理:
(1) 当源主机要发送数据时,首先检查ARP高速缓存列表中是否有对应IP地址的目的主机的MAC地址,若有,则直接发送数据;若没有,就在本局域网上广播发送ARP请求分组,包括源主机的IP地址、源主机的MAC地址和目的主机的IP地址。
(2) 当本局域网中的所有主机收到该ARP请求分组时,首先检查数据包中的目的主机IP地址和自己的IP地址是否一致,若不同,则忽略该数据包;若相同,则将数据包中的源主机的IP地址和MAC地址写入到ARP高速缓存列表中,然后向源主机发送ARP响应分组,同时在此ARP响应包中写入自己的MAC地址。
(3) 源主机收到ARP响应分组后,将目的主机的IP地址和MAC地址写入ARP高速缓存列表中,并利用此映射发送数据。
广播发送ARP请求分组,单播发送ARP响应分组。

ARP欺骗/攻击
ARP攻击就是通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞,攻击者只要持续不断的发出伪造的ARP响应包就能更改目标主机ARP缓存中的IP-MAC条目,造成网络中断或中间人攻击。ARP攻击主要是存在于局域网网络中,局域网中若有一个人感染ARP木马,则感染该ARP木马的系统将会试图通过“ARP欺骗”手段截获所在网络内其它计算机的通信信息,并因此造成网内其它计算机的通信故障。

RARP的实现?

RARP:逆地址解析协议,作用与ARP相反,是将MAC地址解析为IP地址的协议。主要用于无盘工作站引导时获取IP地址。
(1) 源端发送一个本地的RARP广播包,在此广播包中声明自己的MAC地址,并且请求任何收到此请求的RARP服务器分配一个IP地址。
(2)本地网段上的RARP服务器收到此请求后,检查其RARP列表,查找该MAC地址对应的IP地址。如果存在,RARP服务器就给源端发送一个响应数据包,并将此IP地址提供给对方主机使用;如果不存在,RARP服务器对此不做任何响应。
(3)源端在收到从RARP服务器来的响应信息后,利用得到的IP地址进行通信;如果一直没有收到RARP服务器的响应信息,则表示初始化失败。
广播发送RARP请求分组,单播发送RARP响应分组。

IP地址的分类,子网划分?

A类地址:0开头,1.0.0.0 - 126.255.255.255,其中0和127作为特殊地址。
B类地址:10开头,128.0.0.0 - 191.255.255.255。
C类地址:110开头,192.0.0.0 - 223.255.255.255。默认子网掩码为255.255.255.0,也可写作/24。最大主机数量256-2=254。
D类地址范围:224.0.0.0 - 239.255.255.255。组播、多播
E类地址范围:240.0.0.0 - 255.255.255.254。保留地址,255.255.255.255,广播地址

有哪些私有保留地址?

私有地址(内部局域网可以使用的)
A级:10.0.0.0 - 10.255.255.255
B级:172.16.0.0 - 172.31.255.255
C级:192.168.0.0 - 192.168.255.255
保留地址(特殊用途的)
A类:127.X.X.X
B类:169.254.X.X

单播、多播(组播)和广播的区别?

1)单播:主机之间“一对一”的通讯模式,网络中的交换机和路由器对数据只进行转发不进行复制。
2)广播:主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。
3)组播:主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。

IP组播有什么好处?

组播是指主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要(未加入组)的主机的其他通讯。

1 需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。具备广播所具备的优点。
2.由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP协议允许有2亿6千多万个(268435456)组播,所以其提供的服务可以非常丰富。
3.此协议和单播协议一样允许在Internet宽带网上传输。

组播的缺点:
1 与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补。
2 现行网络虽然都支持组播的传输,但在客户认证、QOS等方面还需要完善,这些缺点在理论上都有成熟的解决方案,只是需要逐步推广应用到现存网络当中。

为什么每台电脑都要设置子网掩码?

在正规网络环境中,设备,如电脑等,都是连接在交换机上的,交换机再往上接到路由器上。子网掩码用来判断是否在一个网段。
如果掩码位相同,则判定为同一局域网,则直接将数据包发给交换机,通过MAC地址表转发就可以通信了。如果掩码位不同,则判定不在同一局域网,需要路由出去,所以就需要把数据包发给网关,由 路由器根据路由表转发出去,进行外网路由通信。
所以每台设备上都要配子网掩码,这样设备通信的时候才知道这个数据包要发给路由器还是直接交换机就能处理。

为什么以太网无法接收大于1500字节的数据包?

综合考虑信道利用率和信道状态的折中方法,因为帧长太小信道利用率低,而帧长太大,出错重传效率会降低,具体地:
1、数据包越大,出错的可能性越大;
2、数据包一旦出错就需要重传,大的数据包重传花费的时间多,从而导致网速的下降;
3、时延增大,数据包只有在被完整收到的时候才会开始做校验检查,确认收到的每个bit都没有出错,如果frame包太大,将花费较长时间等待数据包进行校验,从而不能将包及时交给上层;
4、所有用户共享一个公共节点的资源,每个用户的每个资源切片一定要有所限制,不然其他人就只能干等着。这个切片规定多大要考虑效率问题 。
5、最初以太网是在共享介质上面而且是半双工的所有机器共享10Mbps的带宽,如果发送的帧太长,就会占用太久的网络,不仅会影响其他机器的通信,而且会让更多的机器等待到这一帧发送结束之后一齐发送,造成更多的冲突。现在其实已经没有这个限制了,许多交换机都支持更大的MTU,不过单纯提高MTU也不能提高性能。
6、交换机的成本和造价。单个数据块越大,交换机就需要越大的缓存。为什么这么说呢?交换机的设计,并不是像应用程序一样,数据包多大就申请多大的缓存。他一般是硬件加速的方法,为每个数据块分配固定长度的缓存。不论你发送的数据块多大,100或1500,交换机都按1500去为每个数据包分配相同大小的缓存。这样的实现有3个好处:一个是快,相同的操作执行速度高;二是内存无碎片,不规则的内存申请释放,最后会留下很多内存碎片,不需要复杂的内存管理;三是防止错误和攻击。如果根据数据内容的“长度”去申请内存,如果长度域出错怎么办,如果有人故意把长度域设置非常大攻击怎么办,如果有人故意把长度域设置小但是数据块却故意设置大怎么办。

为什么网关与主机可以不在同一个网段?

要深入理解这个问题,首先要搞清楚网络通讯的原理,网络上通讯工作在物理层和数据链路层,源地址和目标地址是通过源和目的的mac地址进行通讯的。
当源主机访问目标主机时,首先看两者的IP在不在同一网段,结果是:
1、两者在同一网段,就会直接把包发向目标IP,这时要做:
1.1 查本地arp缓存,看看是否有IP和Mac的对应表.
1.1.1 有,直接向网络上发包,包中包括原mac及目标mac。
1.1.2 没有,则向网络发arp广播,用来查找与目标IP对应的mac地址(ARP发送的是广播数据,电缆上的每个以太网接口都要接收广播的数据帧)。
1.1.2.1 如果查到了,则向网络发包。
1.1.2.2 没查到,则不通讯。

2、两者不在同一网段,则把目标地址转为网关地址(也就是平时说的向网关发包),然后查找本地arp缓存,继续1.1 。

由此可以看出,源主机和网关的通讯过程中,并不会检查两者是不是同一网段,而是直接去查arp缓存或者发送ARP广播。所以是可能通讯的。

NAT 和 DHCP 的区别是什么?

DHCP 用于为网络内的主机分配 IP 地址和一些其他与网络配置有关的信息;NAT 用于在网络间翻译地址,以达到同一个网络内的多个主机(对应多个地址)通过同一个网关(一个地址)来访问外部网络
简单来说:
DHCP是维护私网(局域网)秩序,而NAT是两个网之间(私网到私网、私网到公网)联系的桥梁,这是区别;
DHCP维护的IP可以通过NAT访问不在本网的服务器或其他客户端,这是联系。

Syn Flood 攻击以及如何防范?

1 什么是SYN Flood攻击
在TCP三次握手时,服务器接收客户端的SYN请求,操作系统将为该请求分配一个TCP(Transmission Control Block),服务器返回一个SYN/ACK请求,并将处于SYN_RCVD状态(半开连接状态)。
从以上过程可以看到,如果恶意的向某个服务器端口发送大量的SYN包,则可以使服务器打开大量的半开连接,分配TCB,从而消耗大量的服务器资源,同时也使得正常的连接请求无法被相应。而攻击发起方的资源消耗相比较可忽略不计。
SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一。
2 怎样发现自己处于被攻击状态
(1)服务端无法提供正常的TCP服务。连接请求被拒绝或超时;
(2)通过 netstat -an 命令检查系统,发现有大量的SYN_RECV连接状态。  
3 防御措施
(1)使用TCP Wrapper,服务端只处理有限来源IP的TCP连接请求,其它未指定来源的连接请求一概拒绝。
(2)缩短SYN Timeout时间,由于SYN Flood攻击的效果取决于服务器上保持的SYN半连接数,这个值=SYN攻击的频度 x SYN Timeout,所以通过缩短从接收到SYN报文到确定这个报文无效并丢弃改连接的时间,例如设置为20秒以下(过低的SYN Timeout设置可能会影响客户的正常访问),可以成倍的降低服务器的负荷。
(3)设置SYN Cookie,就是给每一个请求连接的IP地址分配一个Cookie,如果短时间内连续受到某个IP的重复SYN报文,就认定是受到了攻击,以后从这个IP地址来的包会被一概丢弃。
(4)使用SYN Proxy防火墙
Syn Cache技术和Syn Cookie技术总的来说是一种主机保护技术,需要系统的TCP/IP协议栈的支持,而目前并非所有的操作系统支持这些技术。因此很多防火墙中都提供一种 SYN代理的功能,其主要原理是对试图穿越的SYN请求进行验证后才放行,

静态路由和动态路由各自的优缺点?

1)静态路由是指由网络管理员手工配置的路由信息。当网络的拓扑结构或链路的状态发生变化时,网络管理员需要手工去修改路由表中相关的静态路由信息。
2)动态路由是指路由器能够自动地建立自己的路由表,并且能够根据实际实际情况的变化适时地进行调整。

动态路由的优点:
增加或删除网络时,管理员维护路由配置的工作量较少。
网络拓扑结构发生变化时,协议可以自动做出调整。
配置不容易出错。
扩展性好,网络增长时不会出现问题。
动态路由的缺点:
需要占用路由器资源(CPU 时间、内存和链路带宽)。
管理员需要掌握更多的网络知识才能进行配置、验证和故障排除工作。

路由器和交换机的不同之处有哪些?

1)范围的差别。交换机是用来连接局域网的,路由器是可以跨越局域网的。
2)寻址方式。路由器在网络层,路由器根据IP地址寻址。交换机在中继层,交换机根据MAC地址寻址。
3)防火墙。路由器提供防火墙的服务,交换机不能提供该功能。

防火墙的端口防护?

指通过对防火墙的端口开关的设置,关闭一些非必需端口,达到一定安全防护目的行为.
对某一个端口(例如telnet的端口为23)进行基于IP地址,方向,关键字等的检查过滤,判断是否准入或准出,以保护防火墙内的系统。

Ping命令使用的哪种报文

Ping利用的就是ICMP ECHO和ICMP ECHO REPLY包来探测主机是否存在,这两个分别使用:ICMP ECHO(Type 8) 和ECHO Reply (Type 0)。

两台笔记本电脑连起来后ping不通,你觉得可能是哪些问题造成的?

1 IP是否在同一网段(ip地址配置有问题)
2 防火墙是否禁用了PING回复
3 网卡是否启用(tcp/ip安装的不完整)
4 还有就是网线具体接触是否正常
为什么有时ping服务器第一包丢失?

RFC 826号文档中有如下描述,如果没有ARP映射,则地址解析模块将通知调用方(在这里也就是上层的IP)它将会丢弃报文(当然在这里地址解析模块假设的是这个被丢弃的分组将会被高层重传,然而IP并没有数据恢复这个功能)。
有时发送ping命令,并没有网关的ARP地址,所以会产生丢包现象。

ICMP是属于什么协议,处于那一层?

该协议是TCP/IP协议集中的一个子协议,属于网络层协议。
主要用于在主机与路由器之间传递控制信息,包括报告错误、交换受限控制和状态信息等。
当遇到IP数据无法访问目标、IP路由器无法按当前的传输速率转发数据包等情况时,会自动发送ICMP消息。
我们可以通过Ping命令发送ICMP回应请求消息并记录收到ICMP回应回复消息,通过这些消息来对网络或主机的故障提供参考依据。

ICMP包头主要包括:TYPE类型,CODE代码和校验和。

数据链路层

数据链路层的CSMA/CD协议?

本质原因:在传统的共享以太网中,所有的节点共享传输介质。
CSMA/CD(Carrier Sense Multiple Access with Collision Detection)即带冲突检测的载波监听多路访问技术(载波监听多点接入/碰撞检测)。
如何保证传输介质有序、高效地为许多节点提供传输服务,就是以太网的介质访问控制协议要解决的问题。

过程:先听后发,边发边听,冲突停发,随机延迟后重发。
它的工作原理是:发送数据前先侦听信道是否空闲,若空闲,则立即发送数据。若信道忙碌,则等待一段时间至信道中的信息传输结束后再发送数据;若在上一段信息发送结束后,同时有两个或两个以上的节点都提出发送请求,则判定为冲突。若侦听到冲突,则立即停止发送数据,等待一段随机时间,再重新尝试。

网桥的作用?

网桥是一个局域网与另一个局域网之间建立连接的桥梁.
网桥工作在数据链路层,将两个LAN(局域网)连起来,根据MAC地址来转发帧,可以看作一个“低层的路由器”(路由器工作在网络层,根据网络地址如IP地址进行转发)。

网桥的功能在延长网络跨度上类似于中继器,然而它能提供智能化连接服务,即根据帧的终点地址处于哪一网段来进行转发和滤除。网桥对站点所处网段的了解是靠“自学习”实现的。
使用网桥进行互连克服了物理限制,这意味着构成LAN的数据站总数和网段数很容易扩充。

网桥的中继功能仅仅依赖于MAC帧的地址,因而对高层协议完全透明。
网桥将一个较大的LAN分成段,有利于改善可靠性、可用性和安全性。

网络编程

编写socket套接字的步骤?

基于TCP的socket通信模型
服务器:
1.创建套接字描述符(socket)
2.设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
3.将套接字描述符绑定到服务器地址(bind)
4.将套接字描述符设置为监听套接字描述符(listen),等待来自客户端的连接请求,监听套接字维护未完成连接队列和已完成连接队列
5.从已完成连接队列中取得队首项,返回新的已连接套接字描述符(accept),如果已完成连接队列为空,则会阻塞
6.从已连接套接字描述符读取来自客户端的请求(read)
7.向已连接套接字描述符写入应答(write)
8.关闭已连接套接字描述符(close),回到第5步等待下一个客户端的连接请求
客户端:
1.创建套接字描述符(socket)
2.设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
3.请求建立到服务器的TCP连接并阻塞,直到连接成功建立(connect)
4.向套接字描述符写入请求(write)
5.从套接字描述符读取来自服务器的应答(read)
6.关闭套接字描述符(close)

基于UDP套接字编程
服务器:
1.创建套接字描述符(socket)
2.设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
3.将套接字描述符绑定到服务器地址(bind)
4.从套接字描述符读取来自客户端的请求并取得客户端的地址(recvfrom)
5.向套接字描述符写入应答并发送给客户端(sendto)
6.回到第4步等待读取下一个来自客户端的请求
客户端:
1.创建套接字描述符(socket)
2.设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
3.向套接字描述符写入请求并发送给服务器(sendto)
4.从套接字描述符读取来自服务器的应答(recvfrom)
5.关闭套接字描述符(close)

什么是网络套接字(Socket)?流套接字(SOCK_STREAM)基于什么协议?

网络套接字是一个5元组,<源IP, 源端口,目的IP, 目的端口,协议>。只要这个5元组中有任何一个元素不相同个就认为是不同的部分,其中协议常见的是TCP或者是UDP协议。
流套接字是基于TCP协议的,数据报套接字是基于UDP协议的。还有一种是raw socket,是可以自己进行协议包装的,比如说syn flood中ip地址伪造就是用的raw socket进行的。

Linux/Unix Socket编程并发时什么时候用进程(fork),什么时候用线程(池)?

答案一:
1)进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
2)线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
两者都可以提高程序的并发度,提高程序运行效率和响应时间。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP(Symmetric Multi-Processing,对称多处理结构的简称,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。)机器上运行,而进程则可以跨机器迁移。
答案二:
根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:
1)速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。
2)资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
3)同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

对一个已经关闭了的 socket 的 Server 调用 write 操作?

当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把SIGPIPE设为SIG_IGN 如: signal(SIGPIPE,SIG_IGN); 这时SIGPIPE交给了系统处理。

怎样实时判断socket链接状态?

对端正常close socket,或者进程退出(正常退出或崩溃),对端系统正常关闭这种情况下,协议栈会走正常的关闭状态转移,使用epoll的话,一般要判断如下几个情况:
1、处理可读事件时,在循环read后,返回结果为0。
2、处理可写事件时,write返回-1,errno为EPIPE。
3、EPOLLERR或EPOLLHUP或事件。
对端非正常断开,比如服务器断电,网线被拔掉这种情况下,协议栈无法感知,SO_KEEPALIVE这个选项的超时事件太长并不实用,一般还是以应用层的heartbeat来及时发现。

socket套接字在多线程发送数据时要加锁吗?

socket是全双工的, 多线程同时 send 和 recv 没问题。
在操作系统层面, send 和 recv 均是原子操作, 多线程对同一个socket 发送数据, 无需加锁。

同步IO和异步IO的区别?

同步IO:如果有数据就返回,没有数据就等待,直至数据到来这种模式。网络编程中的read函数就是同步IO。
异步IO:你只管调用,调用完走就可以了,操作系统会帮你把数据处理好,然后在数据处理好之后会通知你,这就是异步IO。Linux系统中的aio_read函数就是异步IO函数。

同步,异步,阻塞,非阻塞辨析?

阻塞:等待
非阻塞:不等待,立即返回,但是多次轮询
这二者是针对I/O模型而言的。

而同步、异步是针对应用程序与内核的交互而言的。
同步:进程触发IO操作并等待或者轮询的去查看IO操作是否完成。
异步:进程触发IO操作以后,直接返回,做自己的事情,IO交给内核来处理,完成后内核通知进程IO完成。

什么是IOCP?

IOCP全称I/O Completion Port, 是window上的一种高效的异步通信模型,是真正的异步通信模型。
提到IOCP就要提到epoll了。
以epoll为核心的reactor模型和以IOCP为核心的proactor模型,是两种不同的通讯模型。
1)Reactor核心就是监听事件的发生,如果事件发生会通知你进行后续的处理.
2)而Proactor模型的核心是,在你监听事件的同时,告诉它应该怎么处理,比如说读数据应该读取到哪里,这就需要你提前开辟好内存空间,然后当事件结束完成以后,会通知你,这个时候你就可以直接操作数据了,因为内核已经将数据从内核空间拷贝到了用户态空间。
ASIO网络框架就是Proactor模型。

什么是SO_LINGER选项?

异常关闭的实现是通过setsockopt()函数中的SO_LINGER选项实现的。前面我们提到在正常情况下主动关闭连接会把发送缓冲区中的数据都发送出去,但是并不知道对端的TCP是否确认了数据。

而使用SO_LINGER选项,并将l_linger的值设置为大于0,那么就会在调用close()之后,close()不立即返回,而是等待l_linger秒,直到对端发送了对数据和FIN的确认之后,close()再成功返回,

还有一种close()返回的情况,那就是过了l_linger秒之后,对端还没有发送对数据和FIN的确认,这时close()会返回-1并设置errno为EWOULDBLOCK。这里有很重要的一点就是,即使close成功返回了,但是并不代表对端应用程序是否已读取数据,只能说明对端确认了数据和FIN。
如果l_linger的值设置为0,那么就会造成上面我们进行实验的情况,立即关闭连接,跳过了TIME_WAIT状态。

SO_LINGER还有一个作用就是用来减少TIME_WAIT套接字的数量。在设置SO_LINGER选项时,指定等待时间为0,此时调用主动关闭时不会发送FIN来结束连接,而是直接将连接设置为CLOSE状态,清除套接字中的发送和接收缓冲区,直接对对端发送RST包。

什么是SO_REUSEADDR选项?

(1)SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作他们的本地端口的连接仍存在。
(2)允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。
(3)SO_REUSEADDR 允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。
(4)SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口号已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说本特性仅支持UDP套接字。

五种IO模型?

select,poll,epoll的区别?

select,poll,epoll的区别
1/ select:是最初解决IO阻塞问题的方法。用结构体fd_set来告诉内核监听多个文件描述符,该结构体被称为描述符集。由数组来维持哪些描述符被置位了。对结构体的操作封装在三个宏定义中。通过轮寻来查找是否有描述符要被处理,如果没有返回
存在的问题:

  1. 内置数组的形式使得select的最大文件数受限与FD_SIZE;
  2. 每次调用select前都要重新初始化描述符集,将fd从用户态拷贝到内核态,每次调用select后,都需要将fd从内核态拷贝到用户态;
  3. 轮寻排查当文件描述符个数很多时,效率很低;
    2/ poll:通过一个可变长度的数组解决了select文件描述符受限的问题。数组中元素是结构体,该结构体保存描述符的信息,每增加一个文件描述符就向数组中加入一个结构体,结构体只需要拷贝一次到内核态。poll解决了select重复初始化的问题。轮寻排查的问题未解决。
    3/ epoll:轮寻排查所有文件描述符的效率不高,使服务器并发能力受限。因此,epoll采用只返回状态发生变化的文件描述符,便解决了轮寻的瓶颈。

在消息传递方式上,select,poll,epoll 具有以下区别:
1)select, poll: 内核需要将消息传递到用户空间,都需要内核拷贝动作
2)通过内核和用户空间共享一块内存(事件表)来实现。

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点:
(1)表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
(2)select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

更通俗的说法:
从内核源码来分析吧,诶不用介绍select吗…算了,其实select跟poll是差不多的,复用了很多代码,只是记录监听events的数据结构不一样…(先介绍了select,然后讲了一下与poll的区别)。epoll的话,在类unix系统中好像只有linux有,epoll把epoll实例创建、events增删改还有events轮询都分开了,这样的话epoll实例就可以被同一个进程中的所有线程共享。epoll跟poll一样,使用链表节点记录监听events,但是呢它有三个链表型结构(就绪链表、辅助链表、红黑树),首先想要监听的events的节点被放到红黑树里,这样可以加快events节点的访问。events就绪之后会被挂载到就绪链表里去,当epoll_wait从内核空间向用户空间写出就绪events的时候,会遍历就绪链表,同时这个时候可能还会发生新的就绪events,这个时候已就绪的events不再添加到就绪链表里去,而是使用辅助链表…

好文章:[linux下非阻塞io库 epoll](https://www.jianshu.com/p/b5bc204da984

tcp 阻塞时,socket的 send recv需要注意的操作?

在阻塞条件下,read/recv/msgrcv的行为::
1、如果没有发现数据在网络缓冲中会一直等待,
2、当发现有数据的时候会把数据读到用户指定的缓冲区,但是如果这个时候读到的数据量比较少,比参数中指定的长度要小,read 并不会一直等待下去,而是立刻返回。
read 的原则::是数据在不超过指定的长度的时候有多少读多少,没有数据就会一直等待。
所以一般情况下::我们读取数据都需要采用循环读的方式读取数据,因为一次read 完毕不能保证读到我们需要长度的数据,read 完一次需要判断读到的数据长度再决定是否还需要再次读取。

阻塞情况下: 阻塞情况下,write会将数据发送完。(不过可能被中断)
在阻塞的情况下,是会一直等待,直到write 完,全部的数据再返回.这点行为上与读操作有所不同。
原因: 读,究其原因主要是读数据的时候我们并不知道对端到底有没有数据,数据是在什么时候结束发送的,如果一直等待就可能会造成死循环,所以并没有去进行这方面的处理;写,而对于write, 由于需要写的长度是已知的,所以可以一直再写,直到写完.不过问题是write 是可能被打断吗,造成write 一次只write 一部分数据, 所以write 的过程还是需要考虑循环write, 只不过多数情况下一次write 调用就可能成功.

怎样理解阻塞非阻塞与同步异步的区别?

1)同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
2)阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

IO 模式 阻塞 非阻塞
同步 BIO:等待,阻塞,MPIO:多路复用 NBIO: 轮询
异步 AIO:异步,回调

epoll:EPOLLLT和EPOLLET的区别?

Level-Triggered :水平触发,缺省模式。
Edge-Triggered :边缘触发。
通知模式:
LT模式时,事件就绪时,假设对事件没做处理,内核会反复通知事件就绪
ET模式时,事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪(边沿只会出现一次)

适用场景:
LT模式比较慢,但是比较安全,也就是如果真的是就绪的话它会再次通知你;
ET模式比较快,但是有可能造成事件的丢失,这就可能让程序永远阻塞。LT为了担责,降低了效率,而ET为了效率将责任推给了用户

Linux-socket的close和shutdown区别及应用场景?

shutdown可以只关闭读或者只关闭写,close关闭读写。
多进程
1.如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。
2.在多进程中如果一个进程中shutdown(sfd, SHUT_RDWR)后其它的进程将无法进行通信。如果一个进程close(sfd)将不会影响到其它进程,得自己理解引用计数的用法了。

使用场景:
有些时候,你会想在socket上实现单向的socket,即数据往一个方向传输。单向的socket便称为半开放Socket。要实现半开放式,需要用到shutdown()函数。
一般来说,半开放socket适用于以下场合:
1.当你想要确保所有写好的数据已经发送成功时。如果在发送数据的过程中,网络意外断开或者出现异常,系统不一定会返回异常,这是你可能以为对端已经接收到数据了。这时需要用shutdown()来确定数据是否发送成功,因为调用shutdown()时只有在缓存中的数据全部发送成功后才会返回。
2.想用一种方法来捕获程序潜在的错误,这错误可能是因为往一个不能写的socket上写数据,也有可能是在一个不该读操作的socket上读数据。当程序尝试这样做时,将会捕获到一个异常,捕获异常对于程序排错来说是相对简单和省劲的。

reactor和proactor的区别?

一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。
1)在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。相当于具体I/O操作还是工作线程来干。
2)而在Proactor模式中,处理器或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。相当于所有I/O操作都交给主线程和内核,工作线程只负责业务逻辑,落得轻松。

如何测量网络发送速度?

1)ping命令可以通过向目标网站发送数据包,从数据包的平均达到时间和丢包率来判断,从本地到目标网站的网络情况。
2)tracert命令是使用从本地到目标网站所在网络服务器的一系列网络节点的访问速度,网络节点最多支持显示30个。

如果将同一个listening socket加入多个epoll, 是不是一种合理的设计?

并不是很合理的设计,首先,一般来说,一个epoll单属于一个线程,多个线程中每个线程拥有一个epoll。考虑一种情况,多个epoll中都存在同一个监听套接字,那么当监听套接字连接队列不为空,原本多个线程阻塞在epoll_wait,现在都被唤醒,但是实际上,仅仅一个线程才能从accept返回,其他的线程则会继续被阻塞。所有线程的唤醒和阻塞,导致了无用的开销。针对这种惊群现象,可以考虑nginx的做法,具体见链接。Nginx中利用锁解决惊群现象的做法。

IP首部、TCP首部、UDP首部、以太网首部?

下面是IP首部报文格式图:20字节

1、第一个4字节(也就是第一行):
(1)版本号(Version),4位;用于标识IP协议版本,IPv4是0100,IPv6是0110,也就是二进制的4和6。
(2)首部长度(Internet Header Length),4位;用于标识首部的长度,单位为4字节,所以首部长度最大值为:(2^4 - 1) * 4 = 60字节,但一般只推荐使用20字节的固定长度。
(3)服务类型(Type Of Service),8位;用于标识IP包的优先级,但现在并未使用。
(4)总长度(Total Length),16位;标识IP数据报的总长度,最大为:2^16 -1 = 65535字节。
2、第二个四字节:
(1)标识(Identification),16位;用于标识IP数据报,如果因为数据链路层帧数据段长度限制(也就是MTU,支持的最大传输单元),IP数据报需要进行分片发送,则每个分片的IP数据报标识都是一致的。
(2)标志(Flag),3位,但目前只有2位有意义;最低位为MF,MF=1代表后面还有分片的数据报,MF=0代表当前数据报已是最后的数据报。次低位为DF,DF=1代表不能分片,DF=0代表可以分片。
(3)片偏移(Fragment Offset),13位;代表某个分片在原始数据中的相对位置。
3、第三个四字节:
(1)生存时间(TTL),8位;以前代表IP数据报最大的生存时间,现在标识IP数据报可以经过的路由器数。
(2)协议(Protocol),8位;代表上层传输层协议的类型,1代表ICMP,2代表IGMP,6代表TCP,17代表UDP。
(3)校验和(Header Checksum),16位;用于验证数据完整性,计算方法为,首先将校验和位置零,然后将每16位二进制反码求和即为校验和,最后写入校验和位置。
4、第四个四字节:源IP地址
5、第五个四字节:目的IP地址

下面是TCP首部报文格式图:20个字节

1、第一个4字节:
(1)源端口,16位;发送数据的源进程端口
(2)目的端口,16位;接收数据的进程端口
2、第二个4字节与第三个4字节
(1)序号,32位;代表当前TCP数据段第一个字节占整个字节流的相对位置;
(2)确认号,32位;代表接收端希望接收的数据序号,为上次接收到数据报的序号+1,当ACK标志位为1时才生效。
3、第四个4字节:
(1)数据偏移,4位;实际代表TCP首部长度,最大为60字节。
(2)6个标志位,每个标志位1位;
SYN,为同步标志,用于数据同步;
ACK,为确认序号,ACK=1时确认号才有效;
FIN,为结束序号,用于发送端提出断开连接;
URG,为紧急序号,URG=1是紧急指针有效;
PSH,指示接收方立即将数据提交给应用层,而不是等待缓冲区满;
RST,重置连接。
(3)窗口值,16位;标识接收方可接受的数据字节数。详解可参看:
4、第五个4字节
(1)校验和,16位;用于检验数据完整性。
(2)紧急指针,16位;只有当URG标识位为1时,紧急指针才有效。紧急指针的值与序号的相加值为紧急数据的最后一个字节位置。用于发送紧急数据。

下面是UDP首部报文格式图:8字节

  1. 源端口:源端口号。在需要对方回信时选用,不需要时可用全0.
  2. 目的端口:目的端口号。这在终点交付报文时必须要使用到。
  3. 长度: UDP用户数据报的长度,其最小值是8(仅有首部)。
  4. 检验和:检测UDP用户数据报在传输中是否有错。有错就丢弃

如有误,欢迎批评指正!

计算机相关 | CS.Related