java网络

*爱你&永不变心* 提交于 2020-03-26 09:57:26

 

 这个图很形象的展示了OSI的五层架构之间的关系。

OSI被称为开放式互联,是国际标准组织制定的网络模型,本来是七层,后来把表现层和会话层加到应用层里面了。

那么五层模型中的每一层具体都是干什么的呢?

在标准的网络模型中,每一层都有它不同的用处,而且每一层都只提供向上和向下的接口,而不会垮层去通信。

在应用层这里,主要是为特定的应用程序提供数据传输服务。这一层是和程序员关系最紧密的一层,其中的代表性协议就是http,它的数据单位是报文。

在传输层,为进程提供数据传输服务。这一层主要为应用层的各种各样的协议提供通用的传输层协议。这里主要就是两个最简单的协议:TCP协议和UDP协议。这里传输是端到端,连接的是端口号,也就是进程。

在网络层,为主机提供数据传输服务。这一层主要将传输层的数据报封装成分组。经典协议就是IP协议。

在数据链路层,为同一链路的主机提供数据传输服务。将网络层传下来的分组封装成帧,这里主要是MAC地址。

在物理层,最底层,传输的是二进制比特流。

 


 

我们从网络层说起,一般网络层下面的就不去了解了。

网络层是整个互联网的核心。网络层向上只提供简单灵活的、无连接的。尽最大努力交互的数据报服务。

IP数据报的格式:

 

 版本:众所周知,ip现在有两个版本,ipv4和ipv6,ipv4的地址已经用光了,ipv6地址非常多。

首部长度:上面每一行是4个字节,除去可变部分,最少有五行,因此就是20个字节。

总长度:首部长度+数据部分长度

生存时间:TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。

协议:携带的数据应该上交的处理协议:传输层的协议。

首部检验和:因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。

标识:为了在分片的情况下,仍然使不同分片具有相同的标识。

片偏移:在分片的时候使用。

源地址、目标地址:ip

以上就是ip数据报的格式,当然最后还要加上要传输的数据部分了。

可以看的出ip的首部规定了ip协议的很多属性内容。

 

那么ip地址是怎么编码的呢

ip地址的编码方式经历了三个历史阶段:

分类:由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的

A类地址:0+网络地址(7位) +  主机地址(24位)

B类地址:10+网络地址(14位)+主机地址(16位)

C类地址:110+网络地址(21位)+主机地址(8位)

D类地址:1110 +       多目的广播地址(28位)

E类地址:11110 +   保留用于实验室和将来使用

注意:在各类IP地址中,有一些IP地址用于表示特殊用途,不用于作主机IP地址:    

          主机号全为0表示网络本身;

      主机号全为1表示本网络的广播地址;

      127.0.0.1网络保留作为环路自测地址,此地址表示任意主机本身;

      32bit全为0,即0.0.0.0表示整个TCP/IP网络;

      32bit全为1,即255.255.255.255表示整个TCP/IP网络的广播地址。

 

子网划分:

通过在主机号字段中拿一部分作为子网号,把两级 IP 地址划分为三级 IP 地址。

IP 地址 ::= {< 网络号 >, < 子网号 >, < 主机号 >}

要使用子网,必须配置子网掩码。一个 B 类地址的默认子网掩码为 255.255.0.0,如果 B 类地址的子网占两个比特,那么子网掩码为 11111111 11111111 11000000 00000000,也就是 255.255.192.0。

注意,外部网络看不到子网的存在。

 

无分类:

无分类编址 CIDR 消除了传统 A 类、B 类和 C 类地址以及划分子网的概念,使用网络前缀和主机号来对 IP 地址进行编码,网络前缀的长度可以根据需要变化。

IP 地址 ::= {< 网络前缀号 >, < 主机号 >}

CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法,例如 128.14.35.7/20 表示前 20 位为网络前缀。

CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。

一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 构成超网

在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。

 

以上这部分不太懂。

在数据链路层实现通信的过程中,ip数据报的源地址和目标地址不会发生改变,但MAC的地址会随着链路的改变而改变,

 

 

从上图也可以看出,通信过程中,从一个路由器跳到另一个路由器,他们的MAC都不相同,那么怎么由ip地址来得到不同路由器的MAC地址呢?

使用的是地址解析协议:ARP协议。

每一个主机都会有一个ARP高速缓存,里面存放着本局域网内个主机和路由器之间的ip到MAC的映射。

如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。

 

 

ARP协议的作用就是建立ip到MAC的映射,完成ip数据报在数据链路层的传输。

那么路由器是如何分组转发的呢?

  • 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。

  • 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付;

  • 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器;

  • 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器;

  • 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器;

  • 报告转发分组出错。

 

 

 

在传输过程中,如何控制报文的状态,使用的是网络控制报文协议(ICMP)

作用是更有效的转发ip数据报,提高交付成功的机会。比如常用的ping应用,来测试两台主机之间的联通性。Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 回答报文。Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。

 


 

以上就是网络层的东西,主要就是ip协议的格式,还有与之配套的ARP协议和ICMP协议。


 

传输层:TCP和UDP

UDP协议:用户数据报协议,是无连接的,尽最大可能交付,没有拥塞控制(不会拥塞,因为无连接)、面向报文(对应用层的报文不拆分,只添加首部)、支持一对一,一对多,多对多等。

TCP协议:传输控制协议,面向连接的,提供可靠交付(交付成功)、有流量控制、有拥塞控制(因为是面向连接的)、提供全双工通信、面向字节流(把报文看成字节流,将字节流组织成大小不等的数据块)一对一。

 

UDP的首部格式

源端口、目标端口、长度、检验和

TCP的首部格式

 

 

这里有几个很重要的参数:

序号:用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。

确认号:期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。

数据偏移:首部长度

确认ACK:当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。刚开始为0

同步SYN:在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。

终止FIN:用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。

窗口:在拥塞中很重要的概念,窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。在后面详细说

 

重要!!!

TCP的三次握手:

 

 

看上面这个图,表示的很清楚,

首先,握手的概念:就是一次发送数据的操作,不管是客户端向服务端,还是服务端向客户端。

服务端一直在监听自己的端口号,查看是否有请求进来。

客户端主动开启请求,将同步SYN打开为1,然后,发送了一个序号为x的数据报。这是第一次握手。

服务端监听到了这次请求,看到了客户端发了一个序号为x的数据报,它就向客户端进行了响应,为了保证客户端能收到这次响应,那么它需要给客户端提一个要求,看客户端能不能实现。因此,它的响应就是,要求客户端发送一个x序号的下一个序号的数据报。这样就能保证服务端的响应是针对上一个序号x的请求。这是第二次握手。(只有这两次握手可以了吗,貌似可以了,因此,客户端如果接到了这个响应,就知道自己上一个请求被响应了,但事实不是如此)

客户端需要把服务端要求的数据报发给服务端:seq=x。这就是第三次握手。

 

为什么是三次握手而不是两次握手?

在上面分析了,在第二次握手之后,客户端知道自己的上一次请求已经被服务端接收到了。但!,服务端不知道自己发的响应是不是被客户端接收到,因此,要想双方都能维护自己的序列号,必须进行第三次握手,让服务端知道自己的响应被拿到了。

为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤

如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

当然还有一个原因,就是,客户端发送的请求在网络中滞留,隔很长时间才能收到服务器的连接确认,但在此之前,客户端会在超时之后,尝试重新请求连接,这样,这个重新请求的连接也会到达服务端,如果只有两次握手,服务器就会打开两个连接。但这不是我们的本意,我们希望它只有一个连接,因此。如果加了第三次握手,那么客户端会忽略哪个重新尝试请求的确认响应,而是对开始时的响应进行确认。因此,不进行第三次握手,服务端不会再次打开连接。

 

 

TCP的四次挥手

 

 要保证全双工通信的正确关闭,需要四次挥手过程。

服务端仍然保持监听状态,而客户端发起主动关闭的请求,发送FIN=1,表示要关闭了。同时发送此时的序列号u。 第一次挥手

服务端监听到了这次关闭请求,然后进入CLOSE-WAIT状态,就是等着关闭了,但还有数据没有发送完成,先将这些数据发送完。这时,客户端不能向服务器发送消息,因为是CLOSE-WAIT状态。为的是让服务端把没交付的数据都发送完成。   第二次挥手

然后服务端也向客户端发起确认关闭的请求,发送了一个FIN=1,表示我也要关了。这时,就不是CLOSE-WAIT状态了,到了LAST-ACK最后确认阶段。此时,客户端可以向服务器发送数据了。 第三次挥手

客户端收到了服务到请求关闭的请求,客户端会向服务端发出一个确认请求,表示我收到了你的关闭确认请求,当发送完了以后,客户端不会立刻关闭,而是等待两个2个最大报文存活时间。然后释放。 第四次挥手

服务器收到客户端的请求之后,立刻关闭连接。

 

可以三次挥手吗?

先来回答这个问题:第二次挥手和第三次挥手的区别:

第二次挥手有两个作用,一个是确认收到客户端的关闭请求,一个就是向客户端传送未完的数据。

第三次挥手的作用是,向客户端发起关闭请求。

因此,当服务端不需要对客户端传送数据的时候,就可以把第二次挥手和第三次挥手合并,最终三次挥手就能关闭。

 

TIME_WAIT是客户端在最后向服务端挥手后,要等待释放的时间。

TIME_WAIT的作用:

1.可靠的实现TCP全双工通信的终止

很好理解,在最后一次挥手的时候,如果没有被服务端接收到,服务端会重新发送FIN请求,因此客户端需要维持一段时间来看服务端会不会重发。

2.允许老的重复分节在网络中消逝

TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

 

大量TIME_WAIT造成的影响

在高并发短链接的TCP服务器上,服务器处理完请求后,会立即主动的正常关闭连接,这样的场景下,会出现大量的socker处于TIME_WAIT状态,如果客户端并发量持续很高,那么部分客户端就会显示连接不到。

 

 

TCP可靠传输

 1.确认和重传:接收方收到报文会确认,发送方没有收到确认,过一段时间会重传。

2.数据校验

3.数据合理分片和排序:

  UDP:IP数据报大于1500字节,大于MTU.这个时候发送方IP层就需要分片(fragmentation).把数据报分成若干片,使每 一片都小于MTU.而接收方IP层则需要进行数据报的重组.这样就会多做许多事情,而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便 无法重组数据报.将导致丢弃整个UDP数据报.(MTU为最大传输单元)

  TCP:按照MTU合理分片,接收方会缓存未按序到达的数据,重新排序后再交给应用层。

4.流量控制:当接收方来不及处理发送方的数据,能提示发送方降低发送速率。防止包丢失。

5.拥塞控制:当网络拥塞时,减少数据的发送。

 

TCP滑动窗口:

发送窗口:在没有收到接收窗口的确认下,发送方可以连续的把发送窗口的数据发送出去。

接收窗口:接收方在没有给发送方确认的情况下,允许连续接收的大小。

 

滑动的含义:发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。

发送窗口有四个组成部分:

  • 已发送并收到确认的数据(不再发送窗口和发送缓冲区之内)(这部分可以往右移,给其他数据让路)

  • 已发送但未收到确认的数据(位于发送窗口之中)(等待确认)

  • 允许发送但尚未发送的数据(等待发送)

  • 发送窗口外发送缓冲区内暂时不允许发送的数据;(等待允许)

 

 在上图中,A向B发送,26.。。。30,这些是已经确认的,已经右移出窗口了,51往后的是没有被允许发送的,还没有进入窗口,窗口中只有已发送还没接收的,和允许发送还没发送的。

B接收A的,B接收到了31.。。。。41,但B确认的时候是按序确认,31是按序到达的,因此B会确认,但34不是按序到达的,因此,B不会确认,需要A重发32,然后才会确认。

 

流量控制

 

 从上图就可以知道,返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送:

 

拥塞控制

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。

流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。

TCP主要通过四个算法来进行拥塞控制:

1.慢开始与拥塞避免

慢开始和拥塞控制算法目的在拥塞发生时循序减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。

发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...

注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。

如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。

 

 看上面这个图,很好理解,慢开始的时候,cwnd=1,这时候只允许发送方发送一个数据报,但当收到确认的时候,就可以发送2个,4个,。。。。然后设置一个拥塞避免的门限,如果超过这个门限,就开始每次只增加1个报文数量。

当确认超时以后,又重新从慢开始出发。

 

2.快重传和快恢复

快重传和快恢复则是为了减少因为拥塞导致的数据包丢失带来的重传时间,从而避免传递无用的数据到网络。

在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3

在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。

慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

 

 

 TCP粘包情况:

发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

 

 


 

以上就是计算机网络的底层原理。我们重点说了,OSI的五层协议,并且讲了ip协议的首部格式(版本、首部长度、协议、源地址。目标地址、标识。偏移量),然后着重讲了TCP协议,三次握手、四次挥手。然后讲了TCP的滑窗机制。


 

 

接下来讲讲套接字,socket。

数据在网络中,以有限大小的包形式传输的,这些包被称为数据报,每一个数据报都包含一个首部和有效载荷。首部包含了地址和端口和其他的管理信息。

 

 

socket让程序员可以将网络连接视为另一种可以读取的字节的流。掩盖了网络的底层细节。

java中的Socket类:

使用直接代码通过主机操作系统的本地TCP通信栈进行通信。

socket是应用程序与TCP/IP协议的封装

程序员通过调用API就能实现通信。

 

 从上图可以看出,套接字位于应用程序和传输层之间,使用门面模式,将复杂的TCP/IP协议隐藏在socket后面。

 

 来看上面这张图:

先从服务器说起,服务器先初始化Socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。这时,客户端初始化了一个Socket,然后连接服务器,如果连接成功,那么就建立了连接。,进行数据的读写。最后关闭连接。


在Uinx里面,socket就是一个文件, 用打开-读写-关闭模式来操作。

 

 

 

 

套接字的介绍就是这样了。

 

下面介绍IO模型

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!