俗世游子:专注技术研究的程序猿
网络
大部分情况下,做开发的程序猿是是不需要和网络打交道的,就比如本人:工作这么多年,去年年初做过一次系统架构,做负载均衡的时候顺带了解了一下这方面的基础知识,其他时候根本用不到。
我们现在就来简单聊一聊,简单到什么程度:
- 开发涉及到网络IO方面的问题能知道该怎么解决,
- 面试能说个七七八八就够了
基本知识
首先我们先要明白什么是网络:
不负责任的说,网络是 网络是由若干节点和连接这些节点的链路构成,而这些物理链路将多台计算机连接在一起,组成了我们现在的互联网
促进网络产生的先决条件:
- 芯片技术
要知道,世界上第一台计算机有一个教室那么大,直到集成电路的产生,将电路做到一块完整的半导体硅板上,计算机的体积才下降下来
- 网络理论本身
第二个条件就是网络理论本身,我们现在知道,网络本身分为很多节点,各个节点之间相互关联,我们从起点A发送数据到终点B,发送的数据在网络中会拆分成小包,由于光电传输是非常快的,所以在数据包在网络中传输的时候会通过不同的路线到达终点B,然后在终点B中进行合并
在这个理论中,两个人的贡献非常大:
- Paul Baran 提出的分布式可适应信息块交换集成电路
- Donald Davies 提出的封包交换
两者说的是一个问题,就是封包交换算法,解决数据如何从一个点通过复杂网络到达另一个点的问题
- 材料的发展
在1858年的跨大西洋同轴电缆,每分钟传输120个字,而我们现在采用的双绞线电缆,通过导线两两缠绕,抵消掉信号的干扰,而不同材料传输速度也不同。目前我们市面上的传输速度已经能到达到100Mbps,还有比这个更快的,能够快10倍
而光纤采用的是光传输,能量在用光传输的时候损耗较低,所以速度最快,能够达到10Gbps
- 软件应用行业的发展
应用的不断发展也造成了网络的发展,包括现在的5G,卫星上网
网络模型
随着网络的出现,再加上当时商业力量的介入,网络得到的大力的发展,出现了OSI网络模型,即开放式系统互联模型,它是国际标准组织的一次伟大尝试,也是世界上第一个试图在世界范围内规范网络标准的框架
七层模型
模型从上向下分别为:
- 应用层
也就是我们平常使用的客户端,比如微信发送消息,浏览器请求地址等等,这里只负责将请求数据发送出去
- 表示层
负责协商用于传输的数据格式,并转换数据格式。比如现在说的HTTP协议,数据量太大的话,数据压缩在这一层,HTTPS数据加密也在这一层完成
- 会话层
负责管理两个联网实体间的网络,比如客户端和服务器端产生的连接,这条连接维持客户端和服务器端的通信,然后等到连接断开的时候释放链接
- 传输层
从传输层开始,将负责数据的拆成一个个小包,然后将小包进行封包,并将数据从一个实体(服务器或者应用)发送到另一个实体,但是并不负责数据的传输方式
同时在传输层还有以下功能:
- 当数据在传输过程中出现问题后采取方式进行纠正,重发或者其他方式
- 控制传输数据的速率
- 端口寻址,标明参与传输的实体的端口号
- 网络层
负责把封包从一个IP地址传输到另一个IP地址,这里的核心就是路由算法。需要通过路由算法得到下一个节点的地址
- 数据链路层
在这一层确保两个临近设备间数据的传输,并隐藏底层实现,同时还需要协调传输时速率问题
- 物理层
真正需要发送数据的实际手段,比如:网线,光纤等等的物理方式
看下面的图,用实际例子来理解一下:
需要注意的是,OSI模型是没有实际可行的方案
TCPIP协议
实际上,OSI模型还是存在一些问题比如说:
我们在命令行通过ping来测试一个网络,根据我们对7层网络模型的认识,这种程序对于会话层和表示层并不是必须的,
而且由于OSI模型在当时并没有实际可行的方案,所以罗伯特.卡恩和文顿.顿瑟夫提出了TCP协议,对比OSI模型只有5层,而且在1975年做了成功的实验,所以从这一点上TCP协议比OSI模型更容易让大家接受
它只有5层:
- 应用层
这里不变,也是将数据发送出去的过程
- 传输控制层
解决主机到主机之间的传输,在这里常用的也就是我们耳熟能详的TCP协议和UDP协议
- 网络层
在这一层提供了路由和寻址,通过路由判定和下一跳机制得到下一个要发送出去的节点
- 数据链路层
两个节点之间的物理连接,在这里对数据再次封包发送,常用的就是ARP协议
- 物理层
物理层和OSI模型不变
深入理解
下面我们来聊一聊在各个层面上我们经常使用的协议
应用层协议:HTTP协议
基本概念
HTTP协议,也叫超文本传输协议,是处理客户端和服务器端之间通信的一种协议,在HTTP协议中的的请求头和返回体都包含相同的数据格式:
- Request
GET / HTTP/1.1
Host: http://www.baidu.com
...
<消息请求body>
- Response
GET HTTP/1.1 200 ok
...
<返回体>
在Request和Response中包含很多的属性
只要我们能够按照HTTP指定的协议返回,那么我们也可以自己实现一个Http服务器,比如:
public class RawHttpSocket {
private ServerSocket serverSocket;
Function<String, String> handler;
public RawHttpSocket(Function<String, String> handler) {
this.handler = handler;
}
public static void main(String[] args) throws IOException {
String body = "<html><head></head><body><pre style=\"word-wrap: break-word; white-space: pre-wrap;\"><h1>Hello world!</h1>\n" +
"</pre></body></html>";
RawHttpSocket rawHttpSocket = new RawHttpSocket((str) -> {
System.out.println(str);
// 第一个 \n 是header结尾 第二个 \n 是header和body的分段
return "HTTP/1.1 200 ok\n\n"+body+"\n";
});
rawHttpSocket.listener(9000);
}
private void listener(int port) throws IOException {
// 建立ServerSocket,绑定端口
serverSocket = new ServerSocket(port);
while (true) {
// 等待Socket建立连接
Socket socket = serverSocket.accept();
new Thread(() -> {
try {
// 处理请求
this.accpet(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
private void accpet(Socket socket) throws IOException {
System.out.println("a socket created");
// 读取到请求数据
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
StringBuffer requestBuffer = new StringBuffer();
// line有可能出现null common-lang3下的工具类:StringUtil
while (StringUtil.isNotBlank((line = reader.readLine()))) {
requestBuffer.append(line).append("\n");
}
String request = requestBuffer.toString();
// 返回数据
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String response = handler.apply(request);
writer.write(response);
writer.flush();
socket.close();
}
}
URL
我们通常通过URL来访问一个网站,URL也就是我们所说的统一资源定位符,其中由以下部分构成:
像80端口,443端口,URL中的端口号是可以省略的
DNS解析
也就是域名解析系统,通常当我们通过URL访问某一个网站的的时候,通过DNS查询得到当前域名所对应的IP地址,然后浏览器再通过返回的IP来定位到服务器资源并返回给浏览器,这也就是DNS解析的工作原理
但是我们要想一个问题,到目前为止,存在了几十亿的网站,而且有时候一个域名下会解析N多个IP地址,这种情况下,快速解析到IP地址就成为了一个问题,于是出现了分级缓存策略,其实介绍下来就是这样的:
- 当用户访问一个网站的时候,首先会从本地缓存中查找是否存在DNS条目,如果有的话就根据该条目直接访问;如果没有的话,回去系统的hosts文件下查找,匹配到就访问,否则会请求到DNS本地服务商。
这一步的存在会屏蔽掉80%的流量请求出去
- 在DNS本地服务商中也会存在缓存,如果这里也没有会请求到根服务器中
- 根服务器在分布在全球,而且在其服务器上只存目录,请求过来之后通过网站域名后缀请求到顶级域名服务器中匹配
- 和根服务器一样,顶级域名服务器上也只存目录,最终通过顶级域名服务器的匹配将请求转发到了权威服务器
- 在权威服务器中得到指定的DNS条目,最终返回给客户端
和CDN技术一样,根据最优规划,DNS解析会给出最适合当前客户端的IP
CDN
CDN技术也是目前架构上使用市场广泛的一种技术,主要可以用来做:
- 流量分发
鸡蛋不放在同一个篮子里,同样的,我们的应用也不会部署在一起,当我们的应用分散在各地机房,应用的请求也需要做相应的调整,可以让用户请求离他们最近的服务器,也可以提升访问的效率
- 数据缓存,包括静态资源和一致性要求低的动态数据
- 不同服务器之间存储的内容都是一样的(镜像文件),这样在访问的时候才能保证一致性
传输控制层常用协议
TCP协议
TCP协议是面向连接的,安全可靠的传输协议,并且能够保证数据在传输过程中的完整性
TCP协议如果需要建立连接,需要进行三次握手操作:
- 客户端发送SYN(同步消息)给服务端,通知服务端准备好进行连接,
- 服务端接收到客户端发送的SYN后会把ACK和服务端的SYN消息同时发送给客户端
- 客户端接收到SYN消息之后再次给服务端发送一个ACK响应消息
三次握手操作,在客户端的角度上来讲:
- 验证了自身的输入流和输出流是没有问题的,
- 最后给服务端的响应消息也是明确告知服务端自己收到了响应消息
等到三次握手全部完成之后,应用层才会开辟线程,开辟对象,开辟文件描述符等等的系统资源
同时,如果客户端想要和服务端断开连接的话,那么要经历四次分手操作:
- 客户端发送断开请求FIN,服务端接收到FIN请求之后会给客户端返回ACK响应
- 服务端经过等待,确定可以断开当前连接的时候会给客户端发送FIN
- 客户端接收到服务端返回的FIN请求,当客户端处理完之后,就会给服务端发送ACK响应,这时这次的连接全部断开
有商有量,才是和平分手
在TCP协议中,三次握手,数据传输,四次分手三者是不可被分割的最小粒度
UDP协议
相对比TCP协议来讲,UDP比TCP简单了很多,UDP不需要连接,这也就造成了它不是安全可靠的连接,而且会出现数据包丢失的情况,但是UDP协议的传输速度比TCP快了很多
这种协议适用的场景包括:
- 音视频
- 广播
- ...
最后的话
好了,不聊了,这里欠个债:
网络层的下一跳机制和数据链路层的ARP协议,在后面再跟大家聊
骇,感觉有点水啊
来源:51CTO
作者:俗世游子
链接:https://blog.51cto.com/14948012/2630383