对于初学者,或者没有接触过网络编程的程序员,会觉得网络编程涉及的知识很高深,很难,其实这是一种误解,当你的语法熟悉以后,其实基本的网络编程现在已经被实现的异常简单了。
网络通信作为互联网的技术支持,已被广泛应用在软件开发中,无论是 Web,服务端,客户端还是桌面应用,都是必须掌握的一门技术。
网络编程是什么?
在软件开发层面实现远程数据交换的编程技术。网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给另外一个设备,然后接受另外一个设备反馈的数据。
OSI 参考模型
OSI 参考模型,也可以叫做 OSI 七层模型,在学习网络编程的时候,都会接触到 OSI 七层模型
, 我们一般使用的网络数据传输由下而上分为七层,这是一个理论的模型。分别是 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
。如下表所示:
层数 | 名称 | 描述 |
---|---|---|
第七层 | 应用层 | 应用层是 OSI 参考模型的最高层, 负责为用户的应用程序提供网络服务。与 OSI 其他层不同的是, 它不为任何其他 OSI 层提供服务, 而只是为 OSI 模型以外的应用程序提供服务。包括为相互通信的应用程序或进行之间建立连接、进行同步, 建立关于错误纠正和控 制数据完整性过程的协商等。应用层还包含大量的应用协议, 如分布式数据库的访问、文件的交换、电子邮件、虚拟终端等。 |
第六层 | 表示层 | 表示层以下的各层只关心可靠的数据传输, 而表示层关心的是所传输数据的语法和语义。它主要涉及处理在两个通信系统之间所交换信息的表示方式, 包括数据格式变换、数据加密与解密、数据压缩与恢复等功能。 |
第五层 | 会话层 | 会话层的功能是在两个节点间建立、维护和释放面向用户的连接。它是在传输连接的基础上建立会话连接, 并进行数据交换管理, 允许数据进行单工、半双工和全双工的传送。会话层提供了令牌管理和同步两种服务功能。 |
第四层 | 传输层 | 传输层是 OSI 七层模型中唯一负责端到端节点间数据传输和控制功能的层。传输层是 OSI 七层模型中承上启下的层, 它下面的三层主要面向网络通信, 以确保信息被准确有效地传输; 它上面的三个层次则面向用户主机, 为用户提供各种服务。传输层通过弥补网络层服务质量的不足, 为会话层提供端到端的可靠数据传输服务。它为会话层屏蔽了传输层以下的数据通信的细节, 使会话层不会受到下三层技术变化的影响。但同时, 它又依靠下面的三个层次控制实际的网络通信操作, 来完成数据从源到目标的传输。传输层为了向会话层提供可靠的端到端传输服务, 也使用了差错控制和流量控制等机制。 |
第三层 | 网络层 | 网络中的两台计算机进行通信时, 中间可能要经过许多中间结点甚至不同的通信子网。网络层的任务就是在通信子网中选择一条合适的路径, 使发送端传输层所传下来的数据能 够通过所选择的路径到达目的端。网络层必须使用寻址方案来确定存在哪些网络以及设备在这些网络中所处的位置, 不同网络层协议所采用的寻址方案是不同的。在确定了目标结点的位置后, 网络层还要负责引导数据包正确地通过网络, 找到通过网络的最优路径, 即路由选择。如果子网中同时出现过多的分组, 它们将相互阻塞通路并可能形成网络瓶颈, 所以网络层还需要提供拥塞控制机制以避免此类现象的出现。 |
第二层 | 数据链路层 | 数据链路层涉及相邻节点之间的可靠数据传输, 数据链路层通过加强物理层传输原始比特的功能, 使之对网络层表现为一条无错线路。为了能够实现相邻节点之间无差错的数据传送, 数据链路层在数据传输过程中提供了确认、差错控制和流量控制等机制。 |
第一层 | 物理层 | 物理层位于 OSI 参考模型的最低层, 它直接面向原始比特流的传输。为了实现原始比特流的物理传输, 物理层必须解决好包括传输介质、信道类型、数据与信号之间的转换、信号传输中的衰减和噪声等在内的一系列问题。另外, 物理层标准要给出关于物理接口的机械、电气、功能和规程特性, 以便于不同的制造厂家既能够根据公认的标准各自独立地制造设备, 又能使各个厂家的产品能够相互兼容。 |
为什么 OSI 要采用分层的形式?因为一个软件逻辑的实现,就是对其进行分层处理。最熟悉不过的就是MVC
, 如把后台的代码会分为,数据层,表现层,业务逻辑层,还有现在的前后台分离这种,其实都是分层的一种表现形式,让每一部分的工作都变的跟加的精确和具体。
但是在项目中不会像 IOS 参考模型中那样分层那么细致,比较常用的可能就是 TCP/IP
协议,那么说到这,什么又是 TCP/IP
协议:
TCP/IP
提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。它将软件通信过程抽象化为四个抽象层,采取协议堆栈的方式,分别实现出不同通信协议。协议族下的各种协议,依其功能不同,被分别归属到这四个层次结构之中,常被视为是简化的七层OSI 模型
。
层数 | 名称 | 涵盖协议 |
---|---|---|
第四层 | 应用层 | TELNET/SSH/HTTP/SMTP/POP/MIB/SIP/HTML(超文本传输协议)… |
第三层 | 传输层 | TCP/UDP/SCTP/DCCP… |
第二层 | 网际层 | ARP/IPv4/ICMP… |
第一层 | 物理层 | 以太网 / 无线 LAN/PPP… |
通过上表中可以看出各个层级所包含的协议,可以把第一层和第二层看作为底层协议,然而在编写软件过程中很少的时候会用到这两层,因为这辆层一般用到会涉及到物理设备,例如链路、无线这些东西,很少会涉及到软件的逻辑。第三层是在软件开发过程中比较常用的,比如 TCP,UDP 之后会详细介绍。应用层会更加的熟悉,在做远程登录,数据传输时会频繁的使用到。
DNS 解析
DNS
解析的过程就是寻找哪台机器上有你需要资源的过程。当你在浏览器中输入一个地址时,例如 www.google.com
,其实不是百度网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的IP
地址,但是 IP
地址并不方便记忆。用户更喜欢用方便记忆的网址去寻找互联网上的其它计算机,也就是上面提到的百度的网址。所以互联网设计者需要在用户的方便性与可用性方面做一个权衡,这个权衡就是一个网址到 IP 地址的转换,这个过程就是 DNS
解析。它实际上充当了一个翻译的角色,实现了网址到 IP
地址的转换。网址到 IP
地址转换的过程是如何进行的?
首先在本地域名服务器中查询 IP
地址,如果没有找到的情况下,本地域名服务器会向根域名服务器发送一个请求,如果根域名服务器也不存在该域名时,本地域名会向 com
顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器得到 google
的IP
地址并把它缓存到本地,供下次查询使用。从上述过程中,可以看出网址的解析是一个从右向左的过程: com -> google.com -> www.google.com
。但是你是否发现少了点什么,根域名服务器的解析过程呢?事实上,真正的网址是www.google.com
.,并不是我多打了一个.,这个. 对应的就是根域名服务器,默认情况下所有的网址的最后一位都是.,既然是默认情况下,为了方便用户,通常都会省略,浏览器在请求 DNS 的时候会自动加上,所有网址真正的解析过程为: . -> .com -> google.com. -> www.google.com
。
- 浏览器缓存 – 当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址(若曾经访问过该域名且没有清空缓存便存在);
- 系统缓存 – 当浏览器缓存中无域名对应 IP 则会自动检查用户计算机系统 Hosts 文件 DNS 缓存是否有该域名对应 IP;
- 路由器缓存 – 当浏览器及系统缓存中均无域名对应 IP 则进入路由器缓存中检查,以上三步均为客服端的 DNS 缓存;
- ISP(互联网服务提供商)DNS 缓存 – 当在用户客服端查找不到域名对应 IP 地址,则将进入 ISP DNS 缓存中进行查询。比如你用的是电信的网络,则会进入电信的 DNS 缓存服务器中进行查找;
- 根域名服务器 – 当以上均未完成,则进入根服务器进行查询。全球仅有 13 台根域名服务器,1 个主根域名服务器,其余 12 为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器 IP 告诉本地 DNS 服务器;
- 顶级域名服务器 – 顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的 IP 地址告诉本地 DNS 服务器;
- 主域名服务器 – 主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录;
- 保存结果至缓存 – 本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个 IP 地址与 web 服务器建立链接。
TCP 连接
HTTP
协议是使用 TCP
作为其传输层协议的,当 TCP
出现瓶颈时,HTTP
也会受到影响。我不知道把 HTTPS
放在这个部分是否合适,但是放在这里好像又说的过去。HTTP
报文是包裹在 TCP
报文中发送的,服务器端收到 TCP
报文时会解包提取出 HTTP
报文。但是这个过程中存在一定的风险,HTTP
报文是明文,如果中间被截取的话会存在一些信息泄露的风险。那么在进入 TCP
报文之前对 HTTP
做一次加密就可以解决这个问题了。HTTPS
协议的本质就是 HTTP+SSL(orTLS)
。在HTTP
报文进入 TCP
报文之前,先使用 SSL
对HTTP
报文进行加密。从网络的层级结构看它位于 HTTP
协议与 TCP
协议之间。
原理:Net 模块提供一个异步 API 能够创建基于流的 TCP 服务器,客户端与服务端简历连接后,服务器可以获得一个全双工 Socket 对象,服务器可以保存 Socket 对象列表,在接收某客户端消息时,推送给客户端。
dome
服务端:
const net = require("net");
const chatServer = net.createServer();
const clientList = [];
chatServer.on("connection",client => {client.write("Hi!\n");
clientList.push(client);
client.on("data",data => {console.log("receive:",data.toString());
clientList.forEach(v => {v.write(data);
})
})
});
chatServer.listen(9000);
TCP 特性
- TCP 提供一种面向连接的、可靠的字节流服务在一个 TCP 连接中,仅有两方进行彼此通信。广播和多播不能用于 TCP
- TCP 使用校验和,确认和重传机制来保证可靠传输
- TCP 给数据分节进行排序,并使用累积确认保证数据的顺序不变和非重复
- TCP 使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制
TCP 并不能保证数据一定会被对方接收到,因为这是不可能的。TCP 能够做到的是,如果有可能,就把数据递送到接收方,否则就(通过放弃重传并且中断连接这一手段)通知用户。因此准确说 TCP 也不是 100% 可靠的协议,它所能提供的是数据的可靠递送或故障的可靠通知。
三次握手与四次挥手
所谓三次握手 (Three-way Handshake
),是指建立一个TCP
连接时,需要客户端和服务器总共发送 3 个包。
三次握手的目的是连接服务器指定端口,建立 TCP
连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在socket
编程中,客户端执行connect()
时。将触发三次握手。
状态 | 描述 |
---|---|
第一次握手 | 客户端发送一个 TCP 的SYN 标志位置 1 的包,指明客户端打算连接的服务器的端口,以及初始序号 X , 保存在包头的序列号(Sequence Number ) 字段里。 |
第二次握手 | 服务器发回确认包 (ACK ) 应答。即 SYN 标志位和 ACK 标志位均为 1。服务器端选择自己 ISN 序列号,放到Seq 域里,同时将确认序号 (Acknowledgement Number ) 设置为客户的 ISN 加 1,即X+1 。发送完毕后,服务器端进入SYN_RCVD 状态。 |
第三次握手 | 客户端再次发送确认包 (ACK ),SYN 标志位为 0,ACK 标志位为 1,并且把服务器发来 ACK 的序号字段 +1,放在确定字段中发送给对方,并且在数据段放写ISN 的 +1。 |
TCP 的连接的拆除需要发送四个包,因此称为四次挥手 (Four-way handshake
),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在socket
编程中,任何一方执行close()
操作即可产生挥手操作。
状态 | 描述 |
---|---|
第一次挥手 | 假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为 1 的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。发送完毕后,客户端进入 FIN_WAIT_1 状态。 |
第二次挥手 | 服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。 |
第三次挥手 | 服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为 1。发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK 。 |
第四次挥手 | 客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT 状态,等待可能出现的要求重传的 ACK 包。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。客户端等待了某个固定时间之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED 状态。 |
TCP 断开连接时需要四次握手,为什么需要 4 次呢?这是由于 TCP 半关闭的性质造成的。所谓半关闭,就是可以发送数据,却不能接收数据或只能接收数据,不能发送数据。
HTTP 协议
说到这里就是老生常谈的话题了,作为一个前端码农来讲,在面试过程中的是时候这个问题已经被问烂了,基本每次面试都会被问到。
其实这部分又可以称为前端工程师眼中的 HTTP
,它主要发生在客户端。发送HTTP
请求的过程就是构建 HTTP
请求报文并通过 TCP
协议中发送到服务器指定端口(HTTP 协议 80/8080
,HTTPS 协议443
)。HTTP 请求报文是由三部分组成: 请求行, 请求报头和请求正文。
特性:
- HTTP 协议构建于 TCP/IP 协议之上,是一个应用层协议,默认端口号是 80
- HTTP 是无连接无状态的
请求报文
HTTP
协议是以 ASCII
码传输,建立在 TCP/IP
协议之上的应用层规范。规范把 HTTP
请求分为三个部分:状态行、请求头、消息主体。
HTTP 定义了与服务器交互的不同方法,最基本的方法有 4 种,分别是 GET,POST,PUT,DELETE
。URL
全称是资源描述符,我们可以这样认为:一个 URL
地址,它用于描述一个网络上的资源,而 HTTP
中的 GET,POST,PUT,DELETE
就对应着对这个资源的查,增,改,删 4 个操作。
- GET:用于请求访问已经被 URI(统一资源标识符)识别的资源,可以通过 URL 传参给服务器。
- POST:用于传输信息给服务器,主要功能与 GET 方法类似,但一般推荐使用 POST 方式。
- PUT:传输文件,报文主体中包含文件内容,保存到对应 URI 位置。
- HEAD:获得报文首部,与 GET 方法类似,只是不返回报文主体,一般用于验证 URI 是否有效。
- DELETE:删除文件,与 PUT 方法相反,删除对应 URI 位置的文件。
- OPTIONS:查询相应 URI 支持的 HTTP 方法。
所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET
请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。
GET 方法与 POST 方法的区别
GET | POST |
---|---|
GET 重点在从服务器上获取资源 | POST 重点在向服务器发送数据 |
GET 传输数据是通过 URL 请求,以 field(字段)= value 的形式,置于 URL 后,并用 ”?” 连接,多个请求数据间用 ”&” 连接 | POST 传输数据通过 Http 的 POST 机制,将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的 |
GET 传输的数据量小,因为受 URL 长度限制,但效率较高 | Post 可以传输大量数据,所以上传文件时只能用 Post 方式 |
GET 是不安全的,因为 URL 是可见的,可能会泄露私密信息,如密码等 | POST 较 GET 安全性较高 |
GET 方式只能支持 ASCII 字符,向服务器传的中文字符可能会乱码 | POST 支持标准字符集,可以正确传递中文字符 |
- GET 可提交的数据量受到 URL 长度的限制,HTTP 协议规范没有对 URL 长度进行限制。这个限制是特定的浏览器及服务器对它的限制
- 理论上讲,POST 是没有大小限制的,HTTP 协议规范也没有进行大小限制,出于安全考虑,服务器软件在实现时会做一定限制
总结
网络编程就是使用 IP 地址,或域名,和端口连接到另一台计算机上对应的程序,按照规定的协议 (数据格式) 来交换数据,实际编程中建立连接和发送、接收数据在语言级已经实现,做的更多的工作是设计协议,以及编写生成和解析数据的代码罢了,然后把数据转换成逻辑的结构显示或控制逻辑即可。
对于初学者,或者没有接触过网络编程的程序员,会觉得网络编程涉及的知识很高深,很难,其实这是一种误解,当你的语法熟悉以后,其实基本的网络编程现在已经被实现的异常简单了。