android 筑基 - 网络编程基础

1,423 阅读26分钟

前言

说网络基础就离不开:网络7层协结构,基本上就是直接开说这7层结构,很多人看不懂,反正我就是没看懂,我所有的文章基本都是本着小白学习的思路,尽量一步一步扒清楚技术之间的关系

本文尝试从网络发展脉络的角度,带大家领略一番网络的知识点,结合实际帮大家理解清楚其中每个角色的位置和作用,最后再结合网络7层协结构把这些分层理顺

建议大家都看看这个国外的经典教程:计算机科学速成课,没有比这个更好的学习网络7层协结构的资料了,绝对没有......

我没打算写的多复杂,就是小白文,让大家都看懂,把基本的知识点都搞懂就是我的初衷了~

以太网、MAC 地址 组成基本网络单元

大家都知道,最早时计算机老贵了,也就大学、研究结构里有这东西,后来随着硬件发展,价格才慢慢下来,70年时计算机开发在大学,研究机构中大量出现,这些机构中的计算机产生了最高的连接需求,于是以太网诞生了

以太网就是咱们说的局域网,一组通过电线连接起来的计算机阵列,我们现在在公司和家里组件的WIFI网络就是个以太网

通过电线,可以实现把数据转换为电子信号的方式传递到其他计算机,在这个小小的网络中,计算机的数量使复数的,计算机之间要相互加以区别才能知道谁对谁,所以MAC地址又诞生了

MAC:媒体访问控制地址,这个就是计算机的身份证,每台计算机都不同,MAC地址由网卡硬件提供,由同一的管理协会,这样才能保证每个MAC地址都是唯一的,当然有的机器上由多个网卡

以太网结构的网络中,电线是所有计算机共享的,大家都可见的,大家都能监听的。大家在发数据时,会把目标的MAC地址放在数据的开头,每台计算机都监听电线的信号,发现MAC地址是自己就接收数据,以此实现2台计算机之间的联通

网络中的电线资源同一时刻只能支持1个人使用,当2台计算机要是同时发送数据的话会产生冲突,这样没有拿到电线资源的计算机会检测到冲突信号,然后定一个时间,假设是1秒之后,再次重新尝试发送数据,要是此时还是产生冲突了,那就在1秒这个基准时间之上加上一个随机时间,在此之后再重新尝试发送数据,这种随机数时间叫做计算机退避机制。要是加上随机数了还是冲突,那么就延长这个随机数时间,然后再重试,这个叫做:指数退避机制

随机数时间的目的使避免大家都再同一时间使用电线,时间叉开的话网络拥堵就会较少,这种机制以太网和WIFI都在用,很多其他传输协议也用

交换机、路由器 组成更大型的网络结构-互联网

  • 交换机: 可以实现2个网络的沟通访问
  • 路由器: 可以实现多个网络的沟通访问

这张图中的 SWITCH 就是交换机了,交换机记录有每一个MAC地址属于哪个网络,在跨网络时,交换机就会发挥作用了,把数据交给另一个网络,在跨网络时,交换机就不用工作了。跨网络工作时,数据会让2边的网络同时阻塞

这张图说的就是通过更高级的交互机:路由器实现更多网络之间的相互访问,路由器会记录与之相关的路由器的地址

若一个网络中有上千万个路由器节点,不可能让每个路由器记住其他所有路由器,这是不现实的。若是路由器有增减,也没法通知别的路由器,所以进一步使用分层思想来构建巨型网络,互联网就是使用分层思路构建出来的,每个层之中的路由器只需要记住关键的路由器节点就行了

  • 计算机首先连到局域网LAN,局域网中连接着每台电脑
  • 局域网再连到广域网WAN,广域网的路由器属于互联网服务提供商ISP
  • 广域网再连到一个区域性路由器,该路由器覆盖一个街区
  • 区域性路由器再连到一个更大的广域网里,该广域网覆盖一个城市
  • 最后会再连到互联网主干,互联网主干由一群超大型,带宽超高的路由器组组成

互联网是一个巨型分布式网络,数据会沿着互联网主干传输到制定位置的计算机

数据在网络中的传递

数据是以0、1二进制的形式在电线间传递的,不管物理距离有多远,数据在构建网络的路由器节点之间一级级接力传递,一直到目的路由器再去分发。这个过程有个参数:跳数,达标这个数据在传输过程中经历了对少个路由器

就好比我们发快递,总要按贴一张快递单,快递单也必须按照制定规矩来写一样。数据在网络之间传递,必须遵循互联网的规定,这种用于传输数据的规定加做:协议

  • IP协议,这是最早的协议,负责把数据包送到正确的计算机
  • UDP协议,只管发送的协议,负责把数据包送到正确的程序
  • TCP协议,带重发,可以知道数据是否到达的协议,负责把数据包送到正确的程序

IP协议

-> 早先,大家都是把数据直接打包成二进制文件,然后在网络线路中传递,但是后来发现,有的数据包体积太大,会把整条线路堵塞,这样会影响整体的网络线路的通畅,所以后来人们不得不把数据包切割成指定大小的块,叫做数据包,数据包都有一个 Heap 信息,记录有该数据包的目标地点

这个协议就是IP协议了,IP协议的数据包张这样,主要带一个Heap头信息

UDP协议

-> IP协议很不错,但是使用IP协议只能把数据包发送到自定的电脑上,但是该给哪个程序呢,IP解决不了,于是在IP协议之上,人们有创建出了UDP协议。UDP协议在数据包中添加了2个内容:

  • 端口号: 每个联网进程在操作系统中都会有一个专门的端口号,通过这个端口号就可以找到制定的程序了
  • 校验码: 用来校验数据包是否完整,网络总有不稳定的时候,丢失部分数据是很平台的事,这样就需要一个校验码告诉程序。UDP的校验算法是求数据和,程序接到数据包后也计算数据和,然后跟这个校验码对比

UDP 数据包:

TCP协议

-> UDP协议很不错,快速简单,但是无法保证数据的完整性,也无法感知数据是否到达,随着业务模式的发展,TCP协议出现了

  1. TCP协议会给数据包编号,这样接收端程序就可以根据编号一一组合得到完整无误的数据了,比如邮件业务就是必须保证整个信件的完整正确,因为使用UDP协议的话,数据包的到达顺序是不保证的,要是乱序到达的怎么显示数据,有时候这是不能接收的 但是TCP就不一样了,有编号了,乱序抵达的也没事,有编号就可以按照顺序组装数据了

  2. TCP协议会在发出一个数据包后等待,接收端收到数据正确后会给发送端发送一个确认码ACK,发送端收到确认码后会发送下一个数据包 TCP不仅仅可以发一个数据包,而是可以同时发送多个数据包

  3. TCP协议可以根据网络拥堵情况动态调整数据包发送量,这个机制是通过统计发包量和ACK到达时间计算出来的

UDP、TCP 使用场景

不是说TCP协议有这么多优点,UDP就没人用了,相反TCP的缺点也不小,就是实时性不行,你想TCP协议里淂等接收方发确认码才能发下一个数据包,这样能快得起来嘛。所以那些对响应时间敏感的在线游戏,视频都还是使用 UDP 的

域名、DNS

192.168.1.3这是IP地址,但是我们在平时使用中谁直接写IP地址的,写的都是域名:www.google.com,之所以写域名,是因为网络供应商考虑我们记不住,给我们添加了一个付费服务:DNS,域名解析

我们上网时,写完域名之后,浏览器先去连接DNS服务器,DNS服务器查找这个域名对应的IP地址返回给我们,浏览器再根据这个IP地址访问网站

因为域名是在太多,为了便于保存域名,所以给域名9那个认为分级,以www.images.google.com举例

  • 顶级域名: .com/.cn/.gov 这些就是
  • 二级域名 google 这些就是
  • 子域名 images 就是

DNS服务器中域名以树型结构存储,一级一级的查找,因为域名实在太多,光二级域名就有3000多万个。这样光一个 DNS 服务器都不够,这些域名存放在多个DNS服务器上,不同服务器负责树的不同部分

网络7层结构

我想大家把上面搞清楚,至少这里划分层级大家应该是能明白了

  • 物理层: 就是那些物理线路,以及线路中的电信号传输协议
  • 数据链路层: 负责操控物理层,以太网、MAC地址、碰撞检测、指数退避这些就是
  • 网络层: 构建起整个网络的这些路由器组合以及基本的数据包传输协议IP协议
  • 传输层: UDP、TCP 这些实现从一个程序到另一个程序数据传输的网络协议
  • 会话层: 使用传输层 TCP、UDP协议,创建连接、传递数据,Socket 套接字就是这层的
  • 表示层: 数据压缩、加密以及数据描述。处理流经结点的数据编码的表示方式问题,以保证一个系统应用层发出的信息可被另一系统的应用层读出
  • 应用层: 针对特定应用的协议,http,ssh这些就是,用户与网络的接口

其实还有5层的说法,看图 区别就是应用层这里了,7层分的细,5层把软件方面算成一个了

马老师这边课程图画的挺到位

图解链路层,还是马老师这边的图:

左右2边是2台机器、是端点,中间的是各种级别的路由器是网络节点,有你自己家的路由,有网络供应商的路由,还有互联网骨干路由,每个路由都有自己的MAC地址,我们叫网关,局域网的请求一定是先交给离自己最近的网关,然后一步步传给下一级网关,到达最终的网关,然后网关一广播发给目标机器

在链路层这一级别来说,IP数据包,没经过一层网关,该网关都会添加下一家网关的地址,这样数据包才能在整个互联网链路上接力传递,大家仔细理解下图,MAC 地址都是指向下一个的~

各种协议

各种协议很多,上面已经说了IP、UDP、TCP协议,下面把常见的都说说:

  • 应用层协议,将数据按协议原则包装,发给传输层
    • HTTP:超文本传输协议
    • FTP:文件传输协议
    • SMTP:简单邮件传送协议
    • SNMP:简单网络管理协议
  • 传输层协议,将应用层传过来的数据进行分组,确保数据的顺序和完整性,对每个分组进行标记,交给网络层
    • TCP:传输控制协议
    • UDP:用户数据协议
  • 网络层协议,将传输层发来的数据分组发送到目标终端
    • ICMP:Internet互联网控制报文协议
    • IGMP:Internet组管理协议
    • IP:网际协议
  • 链路层协议,为网络层发送和接收数据单元
    • ARP:地址解析协议
    • RARP:逆地址解析协议

C/S 模式

  • C: 客户端,就是发送端,一般都是程序 app
  • S: 服务端,就是服务器

C/S 说法听流行的,大家淂知道是啥,其实就是分2个端,1个发送,1个接收处理数据再返回

客户端发送HTTP报文时,报文会以二进制数据流的形式通过一条已经打开的TCP连接按序传输,TCP收到数据流后会将其分割成小的数据块,每个小块被添加的TCP首部与数据块共同组成了TCP分组,分组经由网络层发送,网络层遵循IP协议,当收到分组发送请求后,会将分组其放入IP数据报,填充报头,将数据报发经由链路层发送出去,这样经过每一层时都会添加一些头信息进去,这个大家理解下就行

TCP协议

面向连接,可靠的传输协议

TCP 协议被认为是稳定的协议,HTTP 协议就是基于 TCP 协议实现的,有以下特点:

  • 面向连接,三次握手,四次挥手
  • 双向通信,可以同时发送,接收数据
  • 保证数据可以按序发送,按序到达
  • 超时重传

TCP 报文

  • SYN 同步序号用来发起一个连接
  • ACK 确认序号
  • SEQ 发送序号
  • FIN 完成信号

TCP 传输中,每一个字节都是有序号的,从0开始,通过序号的方式保存数据的顺序,接收端按照序号重新排列数据,其中 SEQ/ACK 需要了解

  • SEQ: 发送端,发送数据包中第一个字节序号
  • ACK: 接收端,期望下一次接收数据包的第一个字节序号

3次握手

这个是最爱问的面试提了,一定要会啊

3次握手是为了建立稳定的 TCP 连接的机制,简单来说:A 和你打招呼,B 回复我听见了,A 再回复我也听见了,这样就能建立一个稳定的,相互确定的连接通道了

为啥2次握手不行呢,这也是必问的面试题

因为·网络通道毕竟不稳定,很容易丢包,只有接收方和发送方都接到对方的确认通知,这样才能真正确认相互可以开始通信了

好比A和B打招呼,A第一次招呼B没听见,A的重发机制会让A再打一次招呼,这次B听见了,和A完成一次通信。这时A第一次的招呼慢悠悠的到B了,B在发送确认消息后会一直等着A的数据包过来,这时其实A是没有和B建立通信的,那B就淂一直傻等。有了3次握手,至少B能确认超时没响应后是A没有通信的意思,A要是想通信会再次过来打招呼的

3次握手过程:

  1. 客户端发送一个报文,同步位SYN=1,数据序列号SEQ=x,进入SYN_SEND状态,等待服务端确认
  2. 服务端接收到报文,发送确认报文,确认位ACK=x+1,同步位SYN=1,数据序列号SEQ=y,服务端进入 SYN_RCVD 状态
  3. 客户端返回确认报文,确认位ACK=y+1,此时连接成功

不去管+几理解的话,就是 app先发一个 syn 同步包,服务器返回一个 syn+ack 同步+确认包,然后 app 再发一个 ack 再次确认的包

什么是连接

面试官会问:什么是连接? 很多人讲完3次握手就完了,但是后面还有,这里才是完成连接的后续工作,都完成之后才算是获得一个 TCP 连接

app 能操作网卡发起访问吗?显然不行,只有 kernel 可以,所以 app 要先切换到内核态,kernal 发起系统调用,网卡会启动一个和目标服务器的 TCP 连接,网卡使用传输层、网络层协议玩完成了3次握手。这里我们已经熟悉了,但是整个连接的过程还没有完,这里要注意,有的面试管问的包括了后续内容

TCP 3次握手成功后,kernel 会给本次 TCP 连接开辟资源,在内核空间中维护2个队列,一个接收数据,一个发送数据,因为 TCP 连接是双向的嘛~。该资源只给连接使用,连接是无形的,必须有一个能看的见的东西代表连接

app 应用程序会拿到一个文件操作符,只有这样上层用户才能向内核读写网络数据。通过3次数据包的交互,双方开辟资源代表对方。new socket 的过程会等一会,等的时间就是在做这个,连接成功了才会返回 socket 对象

4次挥手

这个是最爱问的面试提了,一定要会啊

4次挥手是为了关闭 TCP 连接的机制,简单来说:A 说我要关了,B 说我知道了,B 再说我也要关了,A 再说我知道了。根据网上的解释,TCP 是双向连接,既能发消息,也能收消息,关闭连接时必须双向都通知到位,这样才能2边都关闭,防止出现一边关上了,一边没关上

  1. Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态
  2. Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态,Client进入FIN_WAIT_2状态
  3. Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态
  4. Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server接到信号进入CLOSED状态,可以关闭连接了。然后Client等待一定时间没有回复后,证明Server已正常关闭,那么Client也可以关闭连接了

和上面说的3次握手配套,4次握手完事了要释放资源、释放端口号的~

3次握手是创建连接资源的过程,那4次挥手就是释放资源的过程吗,淂双方都得能释放资源才行,所以需要4次确保双方资源释放完成

UDP 协议

UDP 协议没有 TCP 协议稳定,因为它不建立连接,也不按顺序发送,可能会出现丢包现象,使传输的数据出错。

但是有得就有失,UDP 的效率更高,因为 UDP 头包含很少的字节,比 TCP 负载消耗少,同时也可以实现双向通信,不管消息送达的准确率,只负责无脑发送。

UDP 服务于很多知名应用层协议,比如 NFS(网络文件系统)、SNMP(简单网络管理协议)

UDP 一般多用于 IP 电话、网络视频等容错率强的场景

HTTP协议

HTTP 协议是我们使用的最多,学习这个是最有实际意义的部分了

HTTP 属于应用层,用户端所有数据都会被封装成HTTP报文,再交由下层协议进行处理。报文的作用是客户端与服务端沟通的载体,双方都要遵循统一规则对信息进行处理,这一规则称为HTTP

HTTP 即超文本传送协议,是 Web 的基础,HTTP 协议是建立在 TCP 协议之上的一种应用

HTTP 连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接,从建立连接到关闭连接的过程称为“一次连接”

  • HTTP 1.0,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接
  • 在HTTP 1.1,可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常 的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道 客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

报文结构

HTTP报文的结构分为请求和响应两种:

  • 请求报文封装用户操作产生的动作,告知服务器应采取什么行为
  • 响应报文来告知客户端请求的结果
请求报文格式:
<method> <request-url> <version> // 起始行格式
<headers> // 首部
<body> // 实体
响应报文格式:
<method> <status> <reason-phrase> // 起始行格式
<headers> // 首部
<body> // 实体

起始行

起始行表明了报文的开始,请求和响应各自的起始行不同:

  • 请求报文起始行结构为:方法 + 请求URL + 协议版本,中间用空格做分隔
GET /api/nht/blog/example HTTP/1.1
  • 响应报头起始行结构为:协议版本 + 状态码 + 描述文本,中间用空格做分隔
HTTP/1.1 200 OK

请求方法

方法 Method 是告诉服务端请求报文要干什么事情,常见的HTTP方法如下:

方法含义对应动作
GET从服务端获取资源查询
POST向服务端发送数据新增
PUT将客户端发送的数据存到服务端修改
DELETE从服务端删除资源删除
OPTIONS对服务端进行预检,如服务端支持哪些方法---
HEAD只获取资源头部---
TRACE请求服务器回送收到的请求信息,用于测试或诊断---
CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器---

PUT、DELETE、POST、GET = 增删改查

状态码

  • 100~101 信息提示
  • 200~206 成功
  • 300~305 重定向
  • 400~415 客户端错误
  • 500~505 服务端错误

详细请看:HTTP 状态码

Head 头信息

HTTP 协议中的 Head 头信息,是用键值对的形式存储的一些数据,描述了请求或者响应报文的属性,如Content-Type 表明了请求主体的数据类型,Date 说明了请求的创建时间,客户端与服务端通过头信息来协商具体行为

HTTP/1.0 200 OK
Server: xxxxxxx
Date: Sun,17 Sep 2019 02:01:16 GMT
--------------------------------实体首部
Content-Type: text/plain
Content-length: 18
--------------------------------实体主体

Hi! I'm a message!
--------------------------------

http 1.1 定义了以下基本的Head头信息字段:

  • Content-Type: 实体主体中的数据类型
  • Content-Length: 实体主体的长度或者大小
  • Content-Language: 和传输的数据最匹配的语言
  • Content-Encoding: 来标识服务端编码时所用的编码方式
  • Content-Location: 要返回的数据的地址
  • Content-Range: 如果是部分实体,用来标记它是实体的哪个部分
  • Content-MD5: 实体主体内容的校验和
  • Last-Modified: 所传输内容在服务器上创建或者最后修改的日期时间
  • Expires: 实体数据试下的日期时间
  • Allow: 所请求资源允许的请求方法
  • ETag: 资源的特定版本的标识符,可以让缓存更高效,并节省带宽
  • Cache-Control: 控制缓存机制的指令

当请求报文到达服务器时,服务器会对报文中的内容解析出来,根据方法、资源路径、首部、和主体来处理请求,然后通过对请求资源的访问结果,来构建响应,回送给客户端

这部分很重要,大家要记住,字段写错了请求就不会成功响应

Socket

Socket 套接字,是通信的基石,是会话层的部分,从代码角度看,是对传输层的直接封装。使用 Socket,我们可以选择传输层协议使用 TCP 还是 UDP,当然多数时候,Socket 都是用的 TCP

Socket 包含网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口

在应用层的角度,有可能多个程序使用同一个端口号进行并发通信,此时为了区分谁对谁,就得使用 Socket。应用层、传输层通过 Socket 接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务

进一步 ->

有人分析 Socket 的中文名为什么叫:套接字,他说既然有个套字,那就肯定是有谁对应着谁的关系。这里就引出了 Socket 4元组/5元组的说法,一般说4元组,4元组就是:IP:Port -> IP:Port,服务端的ip、端口,请求端的ip、端口有一个不一样都是另一个 Socket

一个网卡最多提供:65535个端口号,那一台计算机就只能创建 65535 个 TCP 连接嘛,不是的。这个问题也是一道面试题。机器上只要有多个网卡就能突破 65535 的限制,网卡可以是硬件,也可以虚拟处理

建立socket连接

Socket 连接1对2个套接字,一个运行于客户端 ClientSocket,一个运行于服务器端 ServerSocket

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认

  • 服务器监听: 服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
  • 客户端请求: 指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  • 连接确认: 当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发 给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

Socket 连接与 HTTP 连接

必须清楚 Socket 连接和 HTTP 连接的区别

一般 Socket 都用 TCP 协议,连接一旦建立,通信双方即可开始相互发送数据内容。实际应用中,客户端到服务器需要穿越多个中间节点,如路由器、网关、防火墙,防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态

使用 HTTP 协议的 Socket,必须遵守 HTTP 协议请求—响应的方式,客户端向服务器发出请求后,服务器端才能回复数据

客户端于服务端要保持长连接,做推送,就必须使用基于 TCP 协议的 Socket 连接。极光推送的文档,极光的长连接就是采用 TCP 协议来建立的长连接。对 TCP 长连接进行内部自己的实现逻辑来完成长连接

跨域

跨域这是个问题啊,我之前在公司碰到过,大家都在讨论这个问题,而我全程懵逼,好尴尬啊

浏览器有个同源策略限制,同源策略会阻止一个域的 javascript 脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

看懂这个图,知道啥叫跨域就行了



传数据之前3次握手,可以同时发多个包,