0%

[toc]

grpc线程模型

GRPC线程模型#

gRPC 服务端线程模型整体上可以分为两大类:

  • 网络通信相关的线程模型,基于 Netty4.1 的线程模型实现
    即HTTP/2 服务端创建、HTTP/2 请求消息的接入和响应发送都由 Netty 负责

  • 服务接口调用线程模型,基于 JDK 线程池实现
    即gRPC 消息的序列化和反序列化、以及应用服务接口的调用由 gRPC 的 SerializingExecutor 线程池负责。

177cdb317004eead36d0d20a2c7c723761179e43


grpc消息发送全流程详解#

  1. 服务端NIO Selector 轮询,监听客户端连接

  2. 如果监听到客户端连接,则创建客户端 SocketChannel 连接,从 workerGroup 中随机选择一个 NioEventLoop 线程,将 SocketChannel 注册到该线程持有的 NIO-Selector

  3. NioEventLoop 执行NIO的标记读取和read操作
    e1e0870117c36f6f75735174a48806629a7b6862

  4. Netty 的 NioEventLoop 线程切换到 gRPC 的 SerializingExecutor,进行消息的反序列化、以及服务接口的调用

  5. 响应消息的发送,由 SerializingExecutor 发起,将响应消息头和消息体序列化,

  6. 调用 Netty NioSocketChannle 的 write 方法,发送到 Netty 的 ChannelPipeline 中,由 gRPC 的 NettyServerHandler 拦截之后,真正写入到 SocketChannel 中
    8e37664c02c4ec85a0d547f1123063cc428598c0
    总结而言,就是网络处理线程 和 实际业务处理线程,分成2个了,这样网络IO和CPU计算可以分开处理,不会占用同一个线程。


Q: 为什么不在Netty线程里做序列化和反序列话?#

A:
序列化和反序列化操作,都是 CPU 密集型操作,更适合在业务应用线程池中执行,提升并发处理能力。因此,gRPC 并没有在 I/O 线程中做消息的序列化和反序列化。


Q: netty4的串行化线程模型是什么?#

A:
Netty4 之后,对线程模型进行了优化,通过串行化的设计避免线程竞争:
(当系统在运行过程中,如果频繁的进行线程上下文切换,会带来额外的性能损耗)。
从消息的读取、编码以及后续 Handler 的执行,始终都由 I/O 线程 NioEventLoop 负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险
05f64b5536ba94add7b5fc11865d5762f7ae240f

一个 NioEventLoop 聚合了一个多路复用器 Selector,因此可以处理成百上千的客户端连接,
Netty 的处理策略是每当有一个新的客户端接入,则从 NioEventLoop 线程组中顺序获取一个可用的 NioEventLoop

最终效果就是线程之间并没有交集,这样既可以充分利用多核提升并行处理能力,同时避免了线程上下文的切换和并发保护带来的额外性能损耗


Q: grpc启动2个客户端,访问同一个端口,会有2个连接吗?#

A:
如果与路由选中的服务端之间没有可用的连接,则创建NettyClientTransport和NettyClientHandler,发起HTTP/2连接
客户端使用的work线程组并非通常意义的EventLoopGroup,而是一个EventLoop:即HTTP/2客户端使用的work线程并非一组线程(默认线程数为CPU内核 * 2),而是一个EventLoop线程。这个其实也很容易理解,一个NioEventLoop线程可以同时处理多个HTTP/2客户端连接,它是多路复用的,对于单个HTTP/2客户端,如果默认独占一个work线程组,将造成极大的资源浪费,同时也可能会导致句柄溢出(并发启动大量HTTP/2客户端)。
grpc连接池:

https://www.cnblogs.com/xy-ouyang/p/10689908.html

[toc]

http://doc.oschina.net/grpc?t=60134
一个 双向流式 RPC 是双方使用读写流去发送一个消息序列。
两个流独立操作,因此客户端和服务器 可以以任意喜欢的顺序读写:
比如, 服务器可以在写入响应前等待接收所有的客户端消息
或者可以交替 的读取和写入消息
或者其他读写的组合。

每个流中的消息顺序被预留。你可以通过在请求和响应前加 stream 关键字去制定方法的类型。

1
2
3
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

客户端的双向流调用#

  1. 定义一个reponseOberserver, 即响应观察者,用于定义如何处理服务端返回的消息。 一般都是把消息放到一个某个阻塞队列或者单容量队列SettableFuture中。
  2. 调用stub.sendMessage(reponseOberserver), 即告诉grpc框架, 我要用这个reponseOberserver去处理sendMessage消息的响应。
    注意,这个sendMesage方法名,取决于我们的proto中怎么定义的。
  3. 然后stub.sendMessage()方法回返回给我们一个requestObserver,让我们用这个观察者.onNext()去发送请求,可以任意发多次,都是立刻返回的。
  4. 当不需要再发送时,可以调用onCompleted告知对方可以结束了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 public void routeChat() throws Exception {
info("*** RoutChat");
final SettableFuture<Void> finishFuture = SettableFuture.create();
// 定义了如何处理收到的返回消息观察者
StreamObserver reponseObserver = new StreamObserver<RouteNote>() {
@Override
public void onNext(RouteNote note) {
info("Got message \"{0}\" at {1}, {2}", note.getMessage(), note.getLocation()
.getLatitude(), note.getLocation().getLongitude());
}

@Override
public void onError(Throwable t) {
finishFuture.setException(t);
}

@Override
public void onCompleted() {
// 往finishFuture设置空时,说明完成了消息流关闭了
finishFuture.set(null);
}
};

// 框架返回给我一个请求流观察者,让我用这个观察者.onNext(message)去发请求, 返回结果和我传给他的responseServer绑定了。
StreamObserver<RouteNote> requestObserver =
asyncStub.routeChat();

try {
RouteNote[] requests =
{newNote("First message", 0, 0), newNote("Second message", 0, 1),
newNote("Third message", 1, 0), newNote("Fourth message", 1, 1)};

for (RouteNote request : requests) {
info("Sending message \"{0}\" at {1}, {2}", request.getMessage(), request.getLocation()
.getLatitude(), request.getLocation().getLongitude());
requestObserver.onNext(request);
}
requestObserver.onCompleted();

finishFuture.get();
info("Finished RouteChat");
} catch (Exception t) {
requestObserver.onError(t);
logger.log(Level.WARNING, "RouteChat Failed", t);
throw t;
}
}

服务端的处理方式:#

  1. 我们建立服务端的时候,需要调用nettyServer,建立netty服务,并绑定一个xxxServiceImpl抽象类。 这个xxxServiceImpl就是我们在proto中定义的server结构,支持处理我们定义的消息。
  2. xxxServiceImpl中, 有很多需要覆写的方法, 需要你定义如何处理收到的请求, 以及如何给客户端发送响应。发送响应的动作就是参数里的requestObserver.onNext(响应消息)
  3. 返回的xxxService类,会在第一步提供给netty以及grpc框架, 收到消息时,会通过他的异步机制,分隔网络线程和业务线程,走到这边执行的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class	xxxService extend   xxxServiceImpl{
@Override
public void listFeatures(Rectangle request, StreamObserver<Feature> responseObserver) {
int left = min(request.getLo().getLongitude(), request.getHi().getLongitude());
int right = max(request.getLo().getLongitude(), request.getHi().getLongitude());
int top = max(request.getLo().getLatitude(), request.getHi().getLatitude());
int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude());

for (Feature feature : features) {
if (!RouteGuideUtil.exists(feature)) {
continue;
}

int lat = feature.getLocation().getLatitude();
int lon = feature.getLocation().getLongitude();
if (lon >= left && lon <= right && lat >= bottom && lat <= top) {
responseObserver.onNext(feature);
}
}
responseObserver.onCompleted();
}
}

[toc]

第一题双指针#

剑指 Offer II 018. 有效的回文 - 力扣(LeetCode)

1661443175105

双指针,处理字符串比较麻烦

反正都是O(n),不追求空间的话,用收集字符后反转再比较更块

双指针做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public boolean isPalindrome(String s) {
int left = 0, right = s.length()-1;
s = s.toUpperCase();
// todo:Character.isLetterOrDigit(s.charAt(right))可以代替
while(left <= right && left <s.length() && !Character.isDigit(s.charAt(left)) && !Character.isAlphabetic(s.charAt(left))) {
left++;
}

while(left <= right && right >=0 && !Character.isDigit(s.charAt(right)) && !Character.isAlphabetic(s.charAt(right))) {
right--;
}
while(left <right) {

if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
while(left <= right && left <s.length() && !Character.isDigit(s.charAt(left)) && !Character.isAlphabetic(s.charAt(left))) {
left++;
}

while(left <= right && right >=0 && !Character.isDigit(s.charAt(right)) && !Character.isAlphabetic(s.charAt(right))) {
right--;
}
}
return true;
}
}

字符串翻转做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public boolean isPalindrome(String s) {
int left = 0, right = s.length()-1;
s = s.toUpperCase();
StringBuilder sb =new StringBuilder();
for (char c : s.toCharArray()) {
if (Character.isLetterOrDigit(c)) {
sb.append(c);
}
}
return sb.toString().equals(sb.reverse().toString());
}
}

注意Character.isLetterOrDigit©就是判断是否是字母或者数字的意思

注意sb.reverse()会改变自身

第二题博弈论,记忆化搜索#

1140. 石子游戏 II - 力扣(LeetCode)

1661443467395

这种博弈论问题,请直接dfs+记忆化搜索好吧,别纠结动态规划了

而且直接考虑根据当前人的不同,选取最大或者最小值,不要考虑那个简化正负的那种策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
Integer[][][] dp;

public int stoneGameII(int[] piles) {
dp = new Integer[2][piles.length][101];
return dfs(0, 1, piles, true);
}

// 返回alice能拿到的石头
int dfs(int index, int m, int[] piles, boolean isAlice) {

if (index == piles.length) {
return 0;
}
if (dp[isAlice ? 0 : 1][index][m] != null) {
return dp[isAlice ? 0 : 1][index][m];
}

int needDfsRes = (isAlice ? Integer.MIN_VALUE :
Integer.MAX_VALUE);
int sum = 0;
for (int i = index;i<index + 2*m && i < piles.length;i++) {
int dfsRes = dfs(i+1, Math.max(i - index + 1, m),
piles, !isAlice);
if (isAlice) {
sum += piles[i];
needDfsRes = Math.max(needDfsRes, sum + dfsRes);
} else {
needDfsRes = Math.min(needDfsRes, dfsRes);
}
}
dp[isAlice ? 0 : 1][index][m] = needDfsRes;
return needDfsRes;
}
}

第三题二分查找#

540. 有序数组中的单一元素 - 力扣(LeetCode)

1661443586836

要求O(logn),显然不能用异或的那个方法了

既然是有序的,说明相同的2个数字一定是连在一起的

那么在目标左边的连体数字,一定是偶数索引开头

目标右边的连体数字,一定是奇数索引开头

搞定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int singleNonDuplicate(int[] nums) {
int left = 0, right = nums.length;
while(left < right) {
int mid = left + (right - left)/2;
int num1 = nums[mid];
int index1 = -1;
if (mid-1 >=0 && nums[mid-1] == num1) {
index1 = mid-1;
} else if (mid+1<nums.length && nums[mid+1] == num1) {
index1 = mid;
}
if (index1 == -1) {
return num1;
}
// 相同2个数的第一个数的索引是偶数,说明那个要求的数字在右边
// 如果第一个索引是奇书,说明前面被某个单个数字插入过,改变了索引情况
if (index1 %2 == 0) {
left = mid+1;
} else {
right = mid;
}
}
return -1;
}
}

[toc]

RPC全程 Remote Procedure Call ,远程过程调用
gRpc是使用protobuf协议的RPC框架。

在服务端,服务器应用实现方法并启动一个gRPC服务器来处理客户端调用。
在客户端,客户端有一个叫做stub的组件(在很多语言中称为客户端),提供和服务端一致的方法。
c8d8584b41419d707fe757fa64e38706b0219f5e

服务service器代码和stub客户端代码都可以通过protobuf自动生成。

因为使用protobuf做协议交换, 因此多语言都可以支持, python、java、golang、C++都有protobuf的实现。


Q: GRPC相比HTTP的优势?#

A:

  1. 底层使用HTTP/2协议, HTTP/2的长期实时通信支持比HTTP要好。、
  2. ,用的protocBuf对数据进行序列化, 是一种轻量级的消息格式, 成本是json是要小的。
  3. 多语言通用, python、C++、
  4. 逻辑上支持双向流,简化了操作便携度。
  5. 支持tcp多路复用,避免了频繁的tcp连接建立。

Q: 刚才讲到了HTTP2/,能详细说下区别吗?#

A;

  • HTTP / 1.1使用文本数据,这通常在网络上效率较低。而HTTP / 2是二进制数据。
  • HTTP / 1.1的标头没有压缩,而HTTP / 2的标头是gzip压缩的。
  • 在HTTP / 1.1中,服务器必须以接收请求的相同顺序发送响应。而,HTTP / 2是异步处理响应的
  • HTTP / 2允许同时在同一(单个)连接上发送多个请求和响应消息(多路复用)。某个请求任务耗时严重,不会影响到其它连接的正常执行
  • HTTP/2可以不需要响应必须先接请求, 而是可以自己响应(服务端主动推送)
    比如你需要两个文件1.html,1.css,如果是http1.0则需要请求两次,服务端返回两次。但是http2.0则可以客户端请求一次,然后服务端直接回吐两次

Q: 为什么http/1.1不能实现多路复用而http2.0可以? -#

A:

  • 在二进制分帧层上,HTTP 2.0 会将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码 ,
  • 这样分帧以后这些帧就可以乱序发送,然后根据每个帧首部的流标识符号进行组装
  • 对比http/1.1, 因为http1.1是基于文本以换行符分割每一条key:value,则会有以下问题:
  1. 一次只能处理一个请求或者响应,因为这种以分隔符分割消息的数据,在完成之前不能停止解析
  2. 解析这种数据无法预知需要多少内存,会给服务端有很大压力


Q: 为什么说protobuf比json快?#

A:
protobuf比json快6倍,原因如下:

  • protobuf的存储格式为 tag-length-value,通过tag+length能快速对value进行解码。
  • 通过length控制长度,不需要分隔符(逗号,双引号等)就能分隔字段
  • 空字段会省略

Q: grep和dubbo这个grpc框架的对比?哪个好?#

A:
dubbo适合需要完整管理体系、高可靠性的分布式微服务调用体系。 它的配置方式多、负载均衡方式多、容错多、支持4种注册中心。

grep的话, 负载均衡和注册中心都要自己扩展实现,但有提供插拔接口等。
grpc只有api(代码配置)的方式,只有failover。
和dubbo相比, grpc的亮点在于http/2的高性能,以及protobuf的多语言兼容。
146220bbd653f4c601a5ae989065ee0c1d01bd2b

[toc]

春节后的1次小问题#

春节假期之后,我回到工位,发现开发用的3台实验机器中,有1台使用EIP(外网ip)登不上了, 只能使用vpn去连接小网ip登录。
咨询了一下物理机管理员,说假期因为实验室维护, 重启了机器,线路正常, 应该是机器的网络配置之前只临时生效,重启后就没了,让我自己查查看。
在这里插入图片描述

于是只能努力从大脑里挖掘有关计算机网络的回忆, 发现没啥好思路,去内网搜了下类似问题,才知道路由会影响eip, 对比了1下失效机器 和其他正常机器的路由表配置, 才发现缺少到NAT网关的路由,导致报文无法往回发。
在这里插入图片描述
修改了一波路由配置后 便通了。

反思#

这时候我才发现删减改查的业务做得多了,很多经典知识都忘记了。
心想要不要去找本书系统回忆下网络知识?

但是又懒得去番以前的教材,谢老的《计算机网络》?
算了,学生时代它那从物理层开始,再到链路层的超长阴影记忆尤新。
在这里插入图片描述

于是整了本经典的《TCP/IP详解》,抱着学习者的心态重新学习了1下。
在这里插入图片描述

可是,决定开始看之前,又犹豫了一下:
业务需求写完了吗?sql性能优化到最佳了吗? 模块可信、稳健了吗?有必要费时间看这些烂大街的,应届生们面经必背的常见知识?

换言之,作为浮于业务应用上层的java开发小兵,我需要去思考1个问题:当我们学习TCP/IP时,我们在学什么?

抛去以往的功利、应试等心态,我抱着这个问题看完了全书,给了我自己的几个思考

一、学习协议格式的常见设计思路#

IP和TCP的章节里,分别花了大章节,讲述协议首部内容。
学生时代很反感这个东西,感觉就是为了出计算题和记忆题, 我们又不去改报文,看这啥用。
然而工作后,发现这是计算机初学者们,最早能接触到的通讯协议格式设计思想了。
以IP报文格式为例, 可以看到
有 首部长度、总长度,, 还有用于处理报文切分的字段,, 以及校验字段,避免首部错误。
在这里插入图片描述
而如果自己需要设计一套以二进制流为基础的应用层通信协议,很大概率,也要包含以上的内容。
例如我们怎么确定数据多长,怎么保证数据不被篡改,怎么分多次而保证不会乱, 这些都是常见的协议设计重点问题。 而这些问题的解决思路,就可以从TCP、IP等经典协议中初见端倪。

二、学习图算法的应用#

数据结构里,最让很多人头疼的就是图那章了。然而图的内容却是应用最强力的东西。
IP层路由概念的设计,简直是图算法的完美应用常见:

  • 路由表就是很经典的图数据结构设计。
  • 如何动态更新最短路, RIP和OSPF协议也分别给了我们思路

甚至路由表里对子网最长匹配的应用,也用到了前缀树算法(线索树),让人感觉oj网站上算法题都亲切了不少。

三、学习可靠性流程的设计#

当重看TCP协议的握手挥手设计,以及重连机制,我还是会感到惊叹连连。
于是给自己提了个问题,如果不考虑TCP的可靠性,基于应用层,设计一套能稳定可靠的消息交互程序,我能想到有哪些要考虑的地方吗?基于那章,我大概想到了是以下几点

  1. 如何能确保对方收到了我的信息?——TCP引入了ack响应
  2. 怎样算两边开始正式交互?——能够证明两边的网络通道都能通,所以你我各需要1次ack响应来相互证明。
  3. 怎么直到我的消息丢了?——每次消息发出,都有个定时器检查,超时时间内没收到,就会重发,直到到达次数上限,则认为超时
  4. 超时时间怎么确定?——根据每次的时间样本定时更新
  5. 超时后怎么办?——拥塞避免算法
  6. 发少了太慢,发多了会阻塞,怎么确定1次可以发多少?——慢启动算法

可以看到TCP协议里,基本把上述可靠性的设计问题都考虑到了,而且到了及其细致的地步。当你有这类需求时,不妨第一时间回忆下TCP的优秀之处

[toc]

物理层作用#

  • 在传输媒体上传输比特流
  • 对链路层而言, 不需要关心通信手段、传输媒体的差异,因为这些东西都由物理层去处理
  • 这个层面怎么优化?
  1. 提升线路质量,提升带宽
  2. 通信原理应用,如何设计电平传输波形, 频分、波分、码分

如何评价物理层接口好坏#

  • 机械特性: 接线器大小、尺寸
  • 电气特性: 电压范围高低
  • 功能特性:某电平对应某个功能, 信号线。
  • 过程特性:各事件顺序
  • 传输方式转换

传输媒体#

导引型传输(固体传播)#

双绞线#

  • 2根绝缘铜线并排校合,可以减少电磁干扰
  • 电话到交换机就是这个
  • 线要接的距离太长怎么办。总是衰减?
  1. 放大器——模拟电路方式,对信号简单放大,噪音也会变大
  2. 中继器(转发器)——数字电路方式, 对信号做再生, 扩大网络规模
  3. 多款口中继器: 一个输入,转发到其他所有端口
    集线器Hub属于多款口中继器
    可以和交换器连接

同轴电缆#

  • 有线电视用这个
  • 比双绞线要厚,抗干扰更强
  • 包含内导体线、绝缘层、外导体屏蔽、保护外层

光缆#

  • 抗雷抗噪
  • 带宽大,100G/bits!
  • 核心是那个反射率超低的包层,全反射
  • 分为单模(细,远,贵)和多模(便宜,反射率比单模高一点,所以容易失真)

非导引#

  • 短波/微波: 通过电离层反射
  • 无线电微波: 卫星

其他特性#

  • 通信原理相关性质和应用都在物理层体现
  • 什么香农公式、奈式准则之类的
  • 信息交换3种方式
  1. 电路交换: 类似拨号, 始终占用固定带宽, 拨号结束则才挂断
  2. 报文交换: 报文可以很大, 大报文会占据一整片带宽
  3. 分组交换:报文拆成分组发送,可以大家一起传。

[toc]

1.链路层概念#

1.1链路层的功能#

  1. 控制物理介质访问,把传输协议加到链路上
  2. 对传输的数据可做管理和控制,避免因为电平出错导致数据错误却无法发现

1.2链路层协议设计的3个重要概念#

  1. 帧定界问题
    通过添加首部和尾部(例如SOH 0x01或者E OH0x04, 也称做界限符),把数据封装成一帧。
    帧的最大帧长度叫MTU

  2. 透明传输问题
    为了避免帧中数据也存在首部尾部导致获取时出错, 填充字符时,界限符需要做转义

  3. 差错控制问题
    纠错:
    为了避免传输过程因为电路波动存在问题,需要做检查。

    • 循环冗余CRC校验(ARQ方式中使用)
    • 海明码纠错

防丢失:超时重发
防重复:编号

2 常见协议#

2.1 PPP协议#

用于与ISP通信所用协议
用来通过拨号或专线方式建立点对点连接发送数据,使其成为各种主机、网桥和路由器之间简单连接的一种共通的解决方案
只可全双工

PPP也要解决上面提到的3个问题

2.1.1 PPP如何解决帧定界?#

PPP协议的帧格式如下:

7E FF 03 0021/C021/8021 xxxx ??? 7E
帧首 地址地段 控制字段 协议,IP/LCP/NCP三选一 数据报部分 FCS校验码部分 帧尾

从上面可以看到以下几点:

  • PPP协议的帧首和帧尾都是7E(01111110)
  • 地址字段和控制字段都是固定值,相当于已经废弃了(PPP是点对点协议,所以不需要MAC地址,因此地址字段是一个固定值)
  • PPP不止支持IP协议,海支持LCP\NCP

2.2.2 PPP如何解决透明传输?#

有2种方式:

  1. 异步传输时, 使用字节填充的方式:
  • 当数据报部分出现0x7E时, 转成0x7D+0x5E
  • 当出现0x7D时, 转成0x7D+0x5D
  • 出现ASCll码的控制字符(即数值小于0x20的字符),则在该字符前面要加入一个0x7D字节,同时将该字符的编码加以改变。
    可以看到异步传输是用0x7D来识别和转义可能误解的字符, 拆解时根据0x7D去反转
  1. 同步传输时,使用零比特填充
    帧首和帧尾都是0x7E(01111110), 即6个1)
    当数据报部分出现5个1时,会马上填充1个0(这样数据报中永远不会存在连续的6个1)
    当拆解时, 发现连续的5个1,就会删除后面的1个0来恢复

2.1.3 PPP如何解决差错控制?#

使用末尾的FCS校验码校验数据是否有0、1不正确。具体见CRC纠错原理。

2.2 HDLC高级链路控制协议#

HDLC是面向位协议,用"数据位"定义字段类型,而不用控制字符,通过帧中用"位"的组
合进行管理和控制。
帧格式为:
字段:开始标志 地址字段 控制字段 信息字段 校验序列 结束标志

广域网使用PPP和HDLC协议进行远距离数据传输

2.3 CSMA/CD协议(以太网)#

2.3.1 防冲突机制#

以太网是采用CSMA/CD访问方法的局域网

  • 无连接的工作方式,发出去就完事了

  • 地理范围和站点数量有限

  • 共用总线发送

  • 为了避免同一时间多个电平碰撞引发数据混乱,使用了碰撞检测
    其实就是看在争用期内,是否连续收到2端数据,如果收到了说明存在冲突,数据有问题。
    争用期概念:

以太网的端到端往返时延 2a 称为争用期,或碰撞窗口。通常,取 51.2 us 为争用期的长度。对于 10 Mb/s 以太网,在争用期内可发送512 bit,即 64 字节。以太网在发送数据时,若前 64 字节未发生冲突,则后续的数据就不会发生冲突。如果发生冲突,就一定是在发送的前 64 字节之内。由于一检测到冲突就立即中止发送,这时已经发送出去的数据一定小于 64 字节。以太网规定了最短有效帧长为 64 字节,凡长度小于 64 字节的帧都是由于冲突而异常中止的无效帧。

  • 发现碰撞,会重传,等待重传的时间使用二进制指数退避方法

  • 存在最短帧长(64字节),就是为了能有效做碰撞检测,不足就会填充空白

  • 使用曼彻斯特编码

详细见“
https://blog.csdn.net/weixin_43941364/article/details/105639195

2.3.2 以太网报文#

界限符 时钟同步码 目的地址 源地址 类型 数据报 FCS校验
0xAB 7字节,与适配器时钟同步 6字节 6字节 2字节 最短46字节 4字节(CRC)

从这个报文看到以下几点:

  1. 最短数据报文字节46, 是由 64 - 16 得到的, 64是之前提到的最短帧长(为了做冲突检测)
  2. 不需要帧结束符(不会连续存在,如果连续存在那可能就是发生冲突)
  3. 数据报文可能会填充很多空白的
  4. 无效帧判定: FCS错误、 帧长不在64~1518范围内

注意:快速以太网的最短帧总长46B

2.3.3 mac硬件地址概念#

  • mac地址固化在ROM的地址中(网络适配器)。 如果更换适配器,则地址改变。 更换局域网,mac地址不变。
  • 若一个网中有2个相同mac地址,则无法正常通信
  • 1个路由器或者主机中可以配置多个mac地址
  • mac地址总共6个字节,前3个字节伪注册管理机构分配(第一位判断是单站还是组地址,最低第二位判断是全球管理还是本地管理)
  • 后3个字节才是真正的地址

2.3.4 以太网交换机#

  • 用于局域网,做多主机之间的数据交换,属于链路层的扩展道具
  • 具有自学习功能:
    第一次使用,内部无mac缓存,拿到未知mac时会给所有端口发,拿到响应后就会记录下来,后续直接转发
    mac缓存有有效时间,不会一直存在
  • 交换机可以分割冲突域(物理层),但无法分隔广播域

广播域就是说如果站点发出一个广播信号后能接收到这个信号的范围。通常来说一个局域网就是一个广播域。
冲突域指一个站点向另一个站点发出信号。除目的站点外,有多少站点能收到这个信号。这些站点就构成一个冲突域。
冲突域通过集线器连接,广播域则通过交换机。

2.3.5 虚拟局域网VLAN#

  • 可隔离广播域和冲突域

2.4 CSMA/CA(无线网)#

  • 会事先避免冲突,会先侦听信道,等到预约到信道了再进入征用端口。

存在自动重传请求ARQ机制,有3种方式:

  1. 单帧滑动-停止等待协议
    • 一次只发一帧,1次只收一帧,并确认占有信道后才继续
    • 发送端:收到ack确认后,才会发下一个,每个都有个定时器
    • 接收端:序号不匹配递增的话都会丢弃。
  2. 后退N帧GBN
    • 发送端:连续发送,无需等待上一帧完成,每个帧有个定时器判断ack超时
    • 接收端:有序接收,1次只收1个,如果希望收4,却收到5,则丢弃,发送端会超时重发4
  3. 选择重传SR
    • 发送端: 和2一样, 无需等待, 每个帧有定时器
    • 接收端:乱序接收,存在窗口缓存,如果收到了2,而且缓存里有3和8,则会合并上交后,发ack8

2.5 PPPOE#