0%

Creating Meshes

The current way to create a mesh is using MeshBuilder

1
const mesh = BABYLON.MeshBuilder.Create < MeshType > (name, options, scene)

The options object will have different properties according to the type of mesh and can be empty . The scene parameter is optional and defaults to the current scene.

Create Set Shapes

Box

1
const box = BABYLON.MeshBuilder.CreateBox("box", options, scene)

Tiled Plane

1
const tiledPlane = BABYLON.MeshBuilder.CreateTiledPlane("plane", options, scene)

Tiled Plane is good example for understanding the options parameter, see more in this doc

Ground From Height Map

When the ground is created using groundFromHeightMap the surface of the ground can be perturbed by a grayscale image file called a height map. Lighter areas are displayed higher than darker areas. This is a way of creating hills and valleys on your ground.

1
const ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap("gdhm", url_to_height_map, options, scene)

see more in this docs

Create Parametric Meshes

These meshes are so called because their shape is defined by a an array of vector3 parameters and can be irregular in nature. You can produce a series of broken or unbroken lines across 3D space. In 2D you can produce an horizontal irregular convex or concave polygon, which may be extruded vertically into 3D. It is also possible to extrude the 2D outline along a path that travels in 3D space and vary its rotation and scale as it travels the path. You can also ‘lathe’ an outline to give a shape with rotational symmetry.

1
2
const mesh = BABYLON.MeshBuilder.Create<MeshType>(name, options, scene)
const mesh = BABYLON.MeshBuilder.Extrude<MeshType>(name, options, scene)

Extrusion

extrusion docs

Lathe

lathe docs

Polygon

polygon docs

Creating Polyhedra Shapes

Create Custom Meshes

Vertex Normals

vertex normals docs

Mesh Transformation

Centered On

Mesh Rotation

When you use the rotation method on a mesh then the rotation is applied in local space, the order is - a rotation of beta about the local y axis, then alpha about the local x axis and finally a rotation of gamma about the local z axis.

Coordinate Transformation

The first step in understanding coordinate transformation Babylon.js is to understand how the data describing a mesh is stored. The positions of each vertex is kept in an array of coordinates in the local space of the mesh. Each transformation applied to the mesh is stored in a matrix called the World Matrix. For each rendered frame the current World Matrix is used on the local space vertex data to obtain the world data for the mesh. Except for exceptional circumstance such as baking a transformation or a user updating it, the mesh vertex data remains unchanged.

Baking Transformations

Usually, within Babylon.js, positioning, rotating and scaling a mesh changes its world matrix only and the vertex position data of a mesh is left unchanged.

In certain situations you might be interested in applying a transform (position, rotation, scale) directly to the mesh vertices and leave world matrix unchanged. This is called baking.

Unless you have good reasons to use baking transformations then your are better with parents and pivots.

Parents and Pivots

When parent property is set on a mesh, transforming the parent will apply the same transformations to the child. Translations, rotations and scaling of the child will take place relative to the local origin of the child. Setting the position of the child will be relative to the local origin of the parent. In simpler terms, moving the child will not move the parent and moving the parent will move the child.

TransformNode

A TransformNode is an object that is not rendered but can be used as a center of transformation. This can decrease memory usage and increase rendering speed compared to using an empty mesh as a parent and is less complicated than using a pivot matrix.

Parent and Children

To add or remove a parent, it’s better to directly set the parent of target mesh, instead calling setParent() and addChild(), as those two method may cause unwanted transformation.

1
2
3
4
5
6
7
8
meshC.parent = meshP; // 1
meshC.parent = null; // 1

meshC.setParent(meshP); // 2
meshC.setParent(null); // 2

meshP.addChild(meshC); // 3
meshP.removeChild(meshC); // 3

Copies, Clones and Instances

Clones

This simply creates a deep copy of the original mesh and saves memory by sharing the geometry(vertices). Each clone can have its own material and transformation.

Cloning With the Solid Particle System

There is more to the Solid Particle System (SPS) than just producing multiple copies of a mesh and these are considered in full in the Particles section. The SPS places multiple copies of a mesh all together into just one mesh. This means that instead of multiple draw calls there is just one draw call for the single mesh.

1
2
3
4
5
6
7
8
9
SPS = new BABYLON.SolidParticleSystem("SPS", scene);  //create the SPS
SPS.addShape(someMesh, 1500/*copy count*/);
someMesh.dispose(); //dispose of the original mesh
const spsMesh = SPS.buildMesh(); //builds the SPS mesh
SPS.initParticles = () => {
for (let p = 0; p < SPS.nbParticles; p++) {
// do something with each particle/mesh
}
} ;

instances

Instances are an excellent way to use hardware accelerated rendering to draw a huge number of identical meshes (let’s imagine a forest or an army). Each instance has the same material as the root mesh. They can vary on the following properties:

  • position
  • rotation
  • rotationQuaternion
  • setPivotMatrix
  • scaling
1
let newMesh = someMesh.createInstance("name")

instancing a glTF object

When you instanciate a glTF object, you need to make sure that the new instance will be under the same parent or you need to remove the parent from the source object.

This is because every gltf file comes from a right handed world. To get it into Babylon.js left handed world, we are adding an arbitrary parent that is adding a negative scale on z.

So when instancing a glTF object you have to (either):

  • Call source.setParent(null)
  • Or call newInstance.setParent(source.parent)

Thin Instances

instances are an excellent way to use hardware accelerated rendering to draw a huge number of identical meshes.

However, regular instances still have a performance penalty on the javascript side because each instance is its own object (InstancedMesh): if you have 10000 instances in your scene, the engine must loop over all those objects to make a number of processing

Thin instances don’t create new objects so you don’t incur any penalty on the javascript side by having thousands of them. This performance increase does come with a cost:

  • all thin instances are always all drawn (if the mesh is deemed visible) or none. It’s all or nothing.
  • adding / removing a thin instance is more costly than with InstancedMesh

Thin instances should be used when you need a lot of static instances that you know won’t change often / at all. Think of the seats of a stadium

A thin instance is represented by a position/rotation/scaling data packed into a matrix.

1
2
let matrix = BABYLON.Matrix.Translation(-2, 2, 0);
let index = someMesh.thinInstanceAdd(matrix); // You can also pass an array of matrices to thinInstanceAdd if you want to create multiple thin instances at once.

Note that someMesh itself is not rendered. If you want to render it, use thinInstanceAddSelf():

1
someMesh.thinInstanceAddSelf();

Levels Of Detail

This feature allows you to specify different meshes based on distance to viewer (by default), or screen coverage.

1
someMesh.addLODLevel(15, anotherLessLODMesh);

The first parameter used with addLODLevel defines the distance to the camera. Each level is independent and can have its own material. By defining a level of detail to null, you disable rendering of the current mesh when it is viewed beyond the indicated distance to camera.

When a mesh is used as a level of detail for another mesh, it is linked to it and cannot be rendered directly.

You can remove a LOD level by using removeLODLevel:

1
baseMesh.removeLODLevel(anotherLessLODMesh);

You can alternatively add LOD levels by specifying screen coverage cutoff limits. Screen coverage is computed as a ratio between 0 and 1 of the mesh’s rendered screen surface area, over the total screen surface area. This method has the notable advantage over distance comparison to be scale-independant, as a big object rendered from a long distance, can still be big on the screen.

To specify that your LOD levels use screen coverage instead of distance, use:

1
2
baseMesh.useLODScreenCoverage = true;
baseMesh.addLODLevel(0.7,anotherLessLODMesh);

Interactions

To avoid costly calculation by checking many details on a mesh, Babylon engine creates a bounding box around the object, and tests for intersection between this box, and the colliding mesh.

Mesh intersections

We will use the intersectsMesh() function, with two parameters: the mesh to be checked, and the precision of the intersection (boolean), which determines what kind of bounding box will be used.

1
mesh1.intersectsMesh(mesh2, false)

The other function you can use is intersectsPoint() with a specific point

1
mesh.intersectsPoint(pointToIntersect)

Mesh Picking

Raycasts

1
2
let ray = new BABYLON.Ray(origin, direction, length);
let hitInfo = scene.pickWithRay(ray, predicate);

secon parameter predicate is a filter to choose which meshes will be selectable

1
2
3
4
5
6
function predicate(mesh) {
if (mesh == mesh1 || mesh == mesh2) {
return false;
}
return true;
}

in the code example above, the mesh1 and mesh2 will not be hit by the ray.

We can use scene.multiPickWithRay if we don’t want that the ray to stop at the first obstacle:

1
let hitInfoList = scene.multiPickWithRay(ray);

Node Geometry

The NodeGeometry feature was inspired by Blender Geometry Nodes where you can use a node system to build geometry procedurally.

Several reasons to use Node Geometry:

  • The cost of downloading assets on the web is a strong limiting factor. Procedural data can help solve that by limiting the download to the core pieces that are assembled later by the NodeGeometry system
  • The system is dynamic and can produce infinite variants at run time. While there are many digital content creation tools that can create procedural meshes with the same infinite possibilities, the limiting factor again becomes downloading assets created offline. Examples would be terrain or vegetation generation.
  • It allows a new way of modelling in Babylon.js by assembling core shapes and playing around with a node system

How to use

The NodeGeometry class is an utility class, meaning it is autonomous and does not require access to an engine or a scene.

1
const nodeGeometry = new BABYLON.NodeGeometry("my node geometry");

Once created, the system will expect you to create a flow from a source to an endpoint.

By default, the NodeGeometry system supports the following sources:

  • Box
  • Capsule
  • Cylinder
  • Disc
  • Grid
  • Icosphere
  • Mesh
  • Plane
  • Sphere
  • Torus
1
2
3
4
5
6
7
8
9
10
11
12
// Create node geometry
var nodegeo = new BABYLON.NodeGeometry("nodegeo");

// Create source sphere
var sphere = new BABYLON.SphereBlock("sphere");

// Create output
var output = new BABYLON.GeometryOutputBlock("geometryout");
nodegeo.outputBlock = output;
sphere.geometry.connectTo(output.geometry);
nodegeo.build();
var mesh = nodegeo.createMesh("nodegeomesh");

It better to use a Node Geometry Editor to create visual graph and generate code. See more about this editor

Drawing Bounding Boxes

To draw the bounding box around your mesh, all you need to do is set the showBoundingBox property to true.

1
mesh.showBoundingBox = true;

BillBoard Mode

Billboard is a special mode for meshes, ensuring that the mesh is always facing towards the camera. This is commonly used to display information to the user, sprites or particles for instance.

1
mesh.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL

Billboard Mode Value

value Type
0 BILLBOARDMODE_NONE
1 BILLBOARDMODE_X
2 BILLBOARDMODE_Y
4 BILLBOARDMODE_Z
7 BILLBOARDMODE_ALL

Decals

Decals are usually used to add details on meshes (bullets hole, local details, etc…). A decal is either a mesh produced from a subset of a previous one with a small offset in order to appear on top of it, or an additional texture applied to a mesh (a “decal map”).

Mesh Decals

1
const decal = BABYLON.MeshBuilder.CreateDecal("decal", mesh, options, scene);

Decal Maps

Decal Maps are new in Babylon.js since 5.49.0. They are a way to add decal to a mesh without having to create a new mesh. It is a texture that is applied to a mesh in such a way that it appears to be a decal on the mesh.

1
2
3
4
5
6
7
8
9
10
const decalMap = new BABYLON.MeshUVSpaceRenderer(mesh, scene);
const texture = new BABYLON.Texture("bullet.png", scene);
const decalSize = new BABYLON.Vector3(1, 1, 1);
// find the position and the normal of the mesh where you want to add the decal
...

decalMap.renderTexture(texture, position, normal, decalSize);

mesh.decalMap = decalMap;
mesh.material.decalMap.isEnabled = true;

Highlightindg Meshes

Before anything else, you must ensure that your engine was created with stencil on:

1
const engine = new BABYLON.Engine(canvas, true, { stencil: true });

In the most basic shape, you only need to instantiate one highlight layer in your scene and add the meshes you want to highlight in it.

1
2
3
4
const hl = new BABYLON.HighlightLayer("hl1", scene);
hl.addMesh(sphere, BABYLON.Color3.Green()); // add hightlight to a mesh

hl.removeMesh(sphere); // remove highlight to a mesh

Making Meshes Glow

Emissive meshes are equivalent to self lit meshes. Both the emissive color and texture of the material determine how the mesh will self lit.

Only one line is needed to make all the emissive parts of a scene glow:

1
const gl = new BABYLON.GlowLayer("glow", scene);

To control the intensity of the color in the glow layer, you can use the dedicated property

1
gl.intensity = 0.5;

In order to control the shape of the blur, you can rely on the creation option:

  • mainTextureRatio: Multiplication factor applied to the canvas size to compute the render target size used to generate the glowing objects (the smaller the faster).
  • mainTextureFixedSize: Enforces a fixed size texture to ensure resize independant blur to prevent the shape of the blur to change according to the target device size.
  • blurKernelSize: How big is the kernel of the blur texture.
1
2
3
4
const gl = new BABYLON.GlowLayer("glow", scene, {
mainTextureFixedSize: 1024,
blurKernelSize: 64,
});

Controlling glow color per mesh

By default the glow layer will use emissive texture and emissive color to generate the glow color of every active mesh. But you can override this behavior with the following callbacks:

  • customEmissiveColorSelector: (mesh: Mesh, subMesh: SubMesh, material: Material, result: Color4) => void: Callback used to let the user override the color selection on a per mesh basis
  • customEmissiveTextureSelector(mesh: Mesh, subMesh: SubMesh, material: Material) => Texture: Callback used to let the user override the texture selection on a per mesh basis
1
2
3
4
5
6
7
gl.customEmissiveColorSelector = function (mesh, subMesh, material, result) {
if (mesh.name === "lightsaber") {
result.set(1, 0, 1, 1);
} else {
result.set(0, 0, 0, 0);
}
};

Anti Aliasing

Depending on your setup, some aliasing artifacts might appear in the glow. To prevent this behavior, you can specify the number of samples to use for MSAA on the main render target. Please note that it will only work on WebGL2 capable browsers.

1
2
3
const gl = new BABYLON.GlowLayer("glow", scene, {
mainTextureSamples: 4,
});

Exclude or Include Mesh

Some helper functions have been introduced to exclude or only include some meshes from the scene.

1
2
gl.addExcludedMesh(mesh);
gl.addIncludedOnlyMesh(mesh);

Using the function will automatically switch mode and only render the included meshes. Kind of like the conception in light.

Merging Meshes

To easily merge a number of meshes to a single mesh use the static MergeMeshes of the Mesh class:

1
2
3
4
5
6
7
8
const newMesh = BABYLON.Mesh.MergeMeshes(
arrayOfMeshes,
disposeSource,
allow32BitsIndices,
meshSubclass,
subdivideWithSubMeshes,
multiMultiMaterials
);
merging mesh will merge the vertex data and material data

Facet Data

A mesh can have some planar faces. For example, a box has 6 sides, so 6 planar squared faces. Each of its faces are drawn at the WebGL level with 2 triangles. We call “facets” these elementary triangles.

FacetData is a feature that can be enabled on a mesh. As it requires some extra memory, it’s not enabled by default. This feature provides some methods and properties to access each facet of a mesh, like the facet positions, normals.

To enable this feature, just call once updateFacetData().

1
2
3
const mesh = BABYLON.MeshBuilder.CreateTorusKnot("t", { radius: 2.0 }, scene);
mesh.updateFacetData();
console.log(mesh.facetNb);

If the mesh belongs to some parent-child relationship, the feature is then not enabled for its parents or children.

Unless the mesh is updated or morphed afterwards, you don’t need to call this method anymore once it has been done.

you can disabled it to release the memory with mesh.disableFacetData().

1
2
3
4
mesh.updateFacetData();
console.log(mesh.isFacetDataEnabled); // displays "true"
mesh.disableFacetData();
console.log(mesh.isFacetDataEnabled); // displays "false"

Facet Data

You can get the position of the i-th facet of a mesh with getFacetPosition(i). This returns a new Vector3 that is the world coordinates of the facet center.

1
const pos = mesh.getFacetPosition(50);

If you don’t want to allocate a new Vector3 per call, you can use getFacetPositionToRef(i, ref) instead.

1
2
const pos = BABYLON.Vector3.Zero();
mesh.getFacetPositionToRef(50, pos);

All the methods dealing with the world coordinates use the mesh world matrix. As you may know, this matrix is automatically computed on the render call.
If you’ve just moved, scaled or rotated your mesh before calling the facetData methods using the world values and you’re not sure about this, you can ever force the world matrix computation.

1
2
3
mesh.rotate.y += 0.2; // the mesh will be rotated on the next render call, but I need a rotated normal
mesh.computeWorldMatrix(true); // force the world matrix computation
const norm = mesh.getFacetNormal(50); // returns the world normal of the mesh 50th facet

Morphing

When talking about morphing, we mean here changing the vertices positions of an existing mesh. Indices remain unchanged.

  • To create an updatable mesh, it is mandatory to set its updatable parameter to true when calling CreateXXX() method.
  • To update then an existing parametric shape, we just have to use the same CreateXXX method as we used to construct it.
  • Only the existing mesh and the data relative to new positions (path, pathArray, array of points) must be passed to this method, the other parameters are ignored.
  • If we want to morph the mesh, we then use the CreateXXX() method within the render loop.
    In this case, it is important not to allocate new memory each frame : we access our arrays by indexes and just change values instead of creating new arrays, we access existing objects instead of instantiating new ones, etc. We also take care about the weight of each object (number of sides, number of vertices, etc).

More speed

The former CreateXXX() update functions try to be as much optimized as possible to run fast in the render loop. However, you may need some more speed for any reason (huge mesh with dozens of thousands of vertices for instance). So, if your mesh doesn’t need to reflect the light (emissive color only for instance), you can skip the normals re-computation which is a CPU consuming process. Use then the freezeNormals() method just after your mesh is created :

1
2
3
4
const tube = BABYLON.Mesh.CreateTube("tube", path, 3, 12, null, BABYLON.Mesh.NO_CAP, scene, true);
tube.freezeNormals();
// path update here ...
tube = BABYLON.Mesh.CreateTube(null, path, 3, null, null, null, null, null, null, tube);

Morph Targets

Front face & back face

Triangles in WebGPU have the concept of front facing and back facing. By default a front facing triangle has its vertices go in a counter clockwise direction in clip space. A back facing triangle has its vertices go in a clockwise direction in clip space.

The gpu has the ability to draw only forward facing or only back facing triangles. We can turn that feature on by modifying the pipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const pipeline = device.createRenderPipeline({
label: '2 attributes',
layout: 'auto',
vertex: {
module,
entryPoint: 'vs',
},
fragment: {
module,
entryPoint: 'fs',
targets: [{ format: presentationFormat }],
},
primitive: {
cullMode: 'back',
},
});

With cullMode set to back, “back facing” triangles will be culled. “Culling” in this case is a fancy word for “not drawing”

Importantly, as far as WebGPU is concerned, whether or not a triangle is considered to be going clockwise or counter clockwise depends on the vertices of that triangle in clip space. In other words, WebGPU figures out whether a triangle is front or back AFTER you’ve applied math to the vertices in the vertex shader. That means for example, a clockwise triangle that is scaled in X by -1 becomes a counter clockwise triangle or, a clockwise triangle rotated 180 degrees becomes a counter clockwise triangle.

Depth texture

In vertex shader, for Z, clip space is 0 to +1. Anything outside of this range is clipped. Before WebGPU draws a color pixel it will check the corresponding depth pixel. If the depth (Z) value for the pixel it’s about to draw does not match some condition relative to the value of the corresponding depth pixel then WebGPU will not draw the new color pixel.Otherwise it draws both the new color pixel with the color from your fragment shader AND it draws the depth pixel with the new depth value. This means, pixels that are behind other pixels won’t get drawn.

Basics

WebGPU is a very simple system. All it does is run 3 types of functions on the GPU.

  • Vertex Shaders: A Vertex Shader computes vertices. The shader returns vertex positions. For every group of 3 vertices the vertex shader function returns, a triangle is drawn between those 3 positions

  • Fragment Shaders: A Fragment Shader computes colors (Fragment shaders indirectly write data to textures. Colors in WebGPU are usually specified as floating point values from 0.0 to 1.0. That data does not have to be colors. For example, it’s common to output the direction of the surface that pixel represents.) . When a triangle is drawn, for each pixel to be drawn the GPU calls your fragment shader. The fragment shader then returns a color.

  • Compute Shaders: It’s effectively just a function you call and say “execute this function N times”. The GPU passes the iteration number each time it calls your function so you can use that number to do something unique on each iteration.

The shaders reference resources (buffers, textures, samplers) indirectly through Bind Groups

To execute shaders on the GPU, you need to create all of these resources and set up this state. Creation of resources is relatively straightforward. One interesting thing is that most WebGPU resources can not be changed after creation.You can change their contents but not their size, usage, format, etc…If you want to change any of that stuff you create a new resource and destroy the old one.

Drawing triangles

WebGPU can draw triangles to textures. The <canvas> element represents a texture on a webpage. In WebGPU we can ask the canvas for a texture and then render to that texture.(There are actually 5 modes:

  • 'point-list': for each position, draw a point
  • 'line-list': for each 2 positions, draw a line
  • 'line-strip': draw lines connecting the newest point to the previous point
  • 'triangle-list': for each 3 positions, draw a triangle (default)
  • 'triangle-strip': for each new position, draw a triangle from it and the last 2 positions)

steps:

  1. create shader module

  2. create pipeline

  3. create command encoder (command buffer)

  4. submit command buffer

WebGPU takes every 3 vertices we return from our vertex shader and uses them to rasterize a triangle. It does this by determining which pixels’ centers are inside the triangle. It then calls our fragment shader for each pixel to ask what color to make it.

Positions in WebGPU need to be returned in clip space where X goes from -1.0 on the left to +1.0 on the right, and Y goes from -1.0 at the bottom to +1.0 at the top. This is true regardless of the size of the texture we are drawing to.

Inter-stage Variables

Inter-stage variables come into play between a vertex shader and a fragment shader.When a vertex shader outputs 3 positions a triangle gets rasterized. The vertex shader can output extra values at each of those positions and by default, those values will be interpolated between the 3 points (every time the GPU called fragment shader, it passed in a color that was interpolated between all 3 points).

An important point, like nearly everything in WebGPU, the connection between the vertex shader and the fragment shader is by index. For inter-stage variables, they connect by location index.

for inter-stage variables, all that matters is the @location(?). So, it’s common to declare different structs for a vertex shader’s output vs a fragment shader’s input.

Interpolation Settings

The outputs from a vertex shader, are interpolated when passed to the fragment shader. There are 2 sets of settings that can be changed for how the interpolation happens. Setting them to anything other than the defaults is not extremely common but there are use cases.

Interpolation type:

  • perspective: Values are interpolated in a perspective correct manner (default)
  • linear: Values are interpolated in a linear, non-perspective correct manner.
  • flat: Values are not interpolated. Interpolation sampling is not used with flat interpolated, the value passed to the fragment shader is the value of the inter-stage variable for the first vertex in that triangle.

Interpolation sampling:

  • center: Interpolation is performed at the center of the pixel (default)
  • centroid: Interpolation is performed at a point that lies within all the samples covered by the fragment within the current primitive. This value is the same for all samples in the primitive.
  • sample: Interpolation is performed per sample. The fragment shader is invoked once per sample when this attribute is applied.

You specify these as attributes. For example:

1
2
@location(2) @interpolate(linear, center) myVariableFoo: vec4f;
@location(3) @interpolate(flat) myVariableBar: vec4f;
Note that if the inter-stage variable is an integer type then you must set its interpolation to `flat`. ## @builtin(position) In a vertex shader `@builtin(position)` is the output that the GPU needs to draw triangles/lines/points In a fragment shader, `@builtin(position)` is an input. It’s the pixel coordinate of the pixel that the fragment shader is currently being asked to compute a color for. Pixel coordinates are specified by the edges of pixels. The values provided to the fragment shader are the coordinates of the center of the pixel # [WebGPU Data Memory Layout](https://webgpufundamentals.org/webgpu/lessons/webgpu-memory-layout.html) In WebGPU, nearly all of the data you provide to it needs to be layed out in memory to match what you define in your shaders. In WGSL when you write your shaders, it’s common to define `struct`s. You declare members of a struct and when providing the data **it’s up to you** to compute where in a buffer that particular member of the struct will appear. In WGSL v1, there are 4 base types - `f32` (a 32bit floating point number) - `i32` (a 32bit integer) - `u32` (a 32bit unsigned integer) - `f16` (a 16bit floating point number, optional feature) Every type has alignment requirements. For a given type it must be aligned to a multiple of a certain number of bytes. arrays and structs have their own own [special alignment rules](https://www.w3.org/TR/WGSL/#alignment-and-size) Computing sizes and offsets of data in WGSL is probably the largest pain point of WebGPU. You are required to compute these offsets yourself and keep them up to date. If you add a member somewhere in the middle of a struct in your shaders you need to go back to your JavaScript and update all the offsets. Get a single byte or length wrong and the data you pass to the shader will be wrong. You won’t get an error, but your shader will likely do the wrong thing because it’s looking at bad data. Your model won’t draw or your computation will produce bad results. Fortunately there are libraries to help with this. Here’s one: [webgpu-utils](https://github.com/greggman/webgpu-utils)
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
import {
makeShaderDataDefinitions,
makeStructuredView,
} from 'https://greggman.github.io/webgpu-utils/dist/0.x/webgpu-utils-1.x.module.js';

const code = `
struct Ex4a {
velocity: vec3f,
};

struct Ex4 {
orientation: vec3f,
size: f32,
direction: array<vec3f, 1>,
scale: f32,
info: Ex4a,
friction: f32,
};
@group(0) @binding(0) var<uniform> myUniforms: Ex4;

...
`;

const defs = makeShaderDataDefinitions(code);
const myUniformValues = makeStructuredView(defs.uniforms.myUniforms);

// Set some values via set
myUniformValues.set({
orientation: [1, 0, -1],
size: 2,
direction: [0, 1, 0],
scale: 1.5,
info: {
velocity: [2, 3, 4],
},
friction: 0.1,
});
# Uniforms Uniforms are kind of like global variables for your shader. You can set their values before you execute the shader and they’ll have those values for every iteration of the shader. You can set them to something else the next time you ask the GPU to execute the shader. # Storage Buffer Storage buffers are similar to uniform buffers in many ways. If all we did was change `UNIFORM` to `STORAGE` in our JavaScript and `var` to `var` in our WGSL The major differences between uniform buffers and storage buffers are: 1. Uniform buffers can be faster for their typical use-case It really depends on the use case. A typical app will need to draw lots of different things. Say it’s a 3D game. The app might draw cars, buildings, rocks, bushes, people, etc… Each of those will require passing in orientations and material properties similar to what our example above passes in. In this case, using a uniform buffer is the recommended solution. 2. Storage buffers can be much larger than uniform buffers. - The minimum maximum size of a uniform buffer is 64K - The minimum maximum size of a storage buffer is 128M By minimum maximum, there is a maximum size a buffer of a certain type can be. For uniform buffers, the maximum size is at least 64K. For storage buffers, it’s at least 128M. See more about limits in [another article](https://webgpufundamentals.org/webgpu/lessons/webgpu-limits-and-features.html). 3. Storage buffers can be read/write, Uniform buffers are read-only. # Vertex Buffers We can put vertex data in a storage buffer and indexed it using the builtin `vertex_index`. While that technique is growing in popularity, the traditional way to provide vertex data to a vertex shader is via vertex buffers and attributes. Vertex buffers are just like any other WebGPU buffer; they hold data. The difference is we don’t access them directly from the vertex shader. Instead, we tell WebGPU what kind of data is in the buffer and how it’s organized. It then pulls the data out of the buffer and provides it for us. Vertex attributes do not have the same padding restrictions as structures in storage buffers so we no longer need the padding. Attributes in WGSL do not have to match attributes in JavaScript,because attributes always have 4 values available in the shader. They default to `0, 0, 0, 1` so any values we don’t supply get these defaults # Index Buffer One last thing to cover here are index buffers. Index buffers describe the order to process and use the vertices. You can think of `draw` as going through the vertices in order
1
0, 1, 2, 3, 4, 5, ....
With an index buffer we can change that order. # Textures Textures most often represent a 2d image. What makes textures special is that they can be accessed by special hardware called a *sampler*. A sampler can read up to 16 different values in a texture and blend them together in a way that is useful for many common use cases. The interesting WGSL functions for textures are ones that filter and blend multiple pixels. These WGSL functions take a texture which represents that data, a sampler which represents how we want to pull data out of the texture, and a texture coordinate which specifies where we want to get a value from the texture. Texture coordinates for sampled textures go from 0.0 to 1.0 across and down a texture regardless of the actual size of the texture

Flipping the data is common enough that there are even options when loading textures from images, videos, and canvases to flip the data for you.

to draw something with a texture we have to create the texture, put data it in, bind it to bindGroup with a sampler, and reference it from a shader.

Texture Types and Texture Views

There are 3 types of textures

  • 1d
  • 2d
  • 3d

demension can be passed when creating texture, see device.createTexture

In some way you can kind of consider a “2d” texture just a “3d” texture with a depth of 1. And a “1d” texture is just a “2d” texture with a height of 1. Two actual differences, textures are limited in their maximum allowed dimensions. The limit is different for each type of texture “1d”, “2d”, and “3d”.

Another is speed, at least for a 3d texture vs a 2d texture, with all the sampler filters set to linear, sampling a 3d texture would require looking at 16 texels and blending them all together. Sampling a 2d texture only needs 8 texels.

There are 6 types of texture views

  • “1d”
  • “2d”
  • “2d-array”
  • “3d”
  • “cube”
  • “cube-array”
“1d” textures can only have a “1d” view. “3d” textures can only have a “3d” view. “2d” texture can have a “2d-array” view. If a “2d” texture has 6 layers it can have a “cube” view. If it has a multiple of 6 layers it can have a “cube-array” view. You can choose how to view a texture when you call `someTexture.createView`. Texture views default to the same as their dimension, by default, a view of a 2d texture with more than 1 layer gets the dimension: '2d-array'

A “2d-array” is an array of 2d textures. You can then choose which texture of the array to access in your shader. They are commonly used for terrain rendering among other things.

3d textures can be used in cases like 3dLUTS.

Each type of texture has its own corresponding type in WGSL.

type WGSL types
1d texture_1d or texture_storage_1d
2d texture_2d or texture_storage_2d or texture_multisampled_2d as well as a special case for in certain situations texture_depth_2d and texture_depth_multisampled_2d
2d-array texture_2d_array or texture_storage_2d_array and sometimes texture_depth_2d_array
3d texture_3d or texture_storage_3d
cube texture_cube and sometimes texture_depth_cube
cube-array texture_cube_array and sometimes texture_depth_cube_array

Texture Formats

“unorm” is unsigned normalized data (0 to 1) meaning the data in the texture goes from 0 to N where N is the maximum integer value for that number of bits. That range of integers is then interpreted as a floating point range of (0 to 1). In other words, for an 8unorm texture, that’s 8 bits (so values from 0 to 255) that get interpreted as values from (0 to 1).

“snorm” is signed normalized data (-1 to +1) so the range of data goes from the most negative integer represented by the number of bits to the most positive. For example 8snorm is 8bits. As a signed integer the lowest number would be -128 and the highest is +127. That range gets converted to (-1 to +1).

Texture Altas

A Texture Atlas is a fancy name for a texture with multiple images it in. We then use texture coordinates to select which parts go where.

Using Video Effectively

copyExternalImageToTexture. This function copies the current frame of video from the video itself into a pre-existing texture that we created.WebGPU has another method for using video. It’s called importExternalTexture and, like the name suggests, it provides a GPUExternalTexture. This external texture represents the data in the video directly. No copy is made. (What actually happens is up to the browser implementation. The WebGPU spec was designed in the hope that browser would not need to make a copy)

There are a few big caveats to using an texture from importExternalTexture

  • The texture is only valid until you exit the current JavaScript task. An implication of this is that you must make a new bindgroup each time you call importExternalTextureso that you can pass the new texture into your shader.

  • You must use texture_external in your shaders

  • You must use textureSampleBaseClampToEdge in your shaders. Like the name suggests, textureSampleBaseClampToEdge will only sample the base texture mip level (level 0). In other words, external textures can not have a mipmap. Further, the function clamps to the edge, meaning, setting a sampler to addressModeU: 'repeat' will be ignored.

Storage Texture

Multi-Sampling Anti-aliasing

Setting colorAttachment[0].resolveTarget says to WebGPU, “when all the drawing in this render pass has finished, downscale the multisample texture into the texture set on resolveTarget. If you have multiple render passes you probably don’t want to resolve until the last pass. While it’s fastest to resolve in the last pass it’s also perfectly acceptable to make an empty last render pass to do nothing but resolve. Just make sure you set the loadOp to 'load' and not 'clear' in all the passes except the first pass otherwise it will be cleared.

Pipeline-Overridable constants

pipeline-overridable constants are a type of constant you declare in your shader but you can change when you use that shader to create a pipeline.

Pipeline overridable constants can only be scalar values so boolean (true/false), integers, floating point numbers. They can not be vectors or matrices.

If you don’t specify a value in the shader then you must supply one in the pipeline. You can also give them a numeric id and then refer to them by their id.

Canvas alphaMode

By default a WebGPU canvas is opaque. Its alpha channel is ignored. To make it not ignored we have to set its alphaMode to 'premultiplied' when we call configure. The default is 'opaque'

1
2
3
4
5
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
});

alphaMode: 'premultiplied' means the colors you put in the canvas must have their color values already multiplied by the alpha value.

Blending

Where color is what happens to the rgb portion of a color and alpha is what happens to the a (alpha) portion.

operation can be one of

  • add
  • subtract
  • reverse-subtract
  • min
  • max

srcFactor and dstFactor can each be one of

  • zero
  • one
  • src
  • one-minus-src
  • src-alpha
  • one-minus-src-alpha
  • dst
  • one-minus-dst
  • dst-alpha
  • one-minus-dst-alpha
  • src-alpha-saturated
  • constant
  • one-minus-constant

Most of them are relatively straight forward to understand. Think of it as

1
result = operation((src * srcFactor), (dst * dstFactor))

Of the blend factors above, 2 mention a constant, 'constant' and 'one-minus-constant'. The constant referred to here is set in a render pass with the setBlendConstant command and defaults to [0, 0, 0, 0]. This lets you change it between draws.

Data Copying

  • writeBuffer copies data from a TypedArray or ArrayBuffer in JavaScript to a buffer. This is arguably the most straight forward way to get data into a buffer.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    device.queue.writeBuffer(
    destBuffer, // the buffer to write to
    destOffset, // where in the destination buffer to start writing
    srcData, // a typedArray or arrayBuffer
    srcOffset?, // offset in **elements** in srcData to start copying
    size?, // size in **elements** of srcData to copy
    )
    //If srcOffset is not passed it’s 0.
    // If size is not passed it’s the size of srcData.
    // srcOffset and size are in elements of srcData, for example,
    // if srcData is Float32Array and srcOffset is 6, it will copy
    // starting at 24 bytes
  • writeTexture copies data from a TypedArray or ArrayBuffer in JavaScript to a texture.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    device.writeTexture(
    // details of the destination
    { texture, mipLevel: 0, origin: [0, 0, 0], aspect: "all" },

    // the source data
    srcData,

    // details of the source data
    { offset: 0, bytesPerRow, rowsPerImage },

    // size:
    [ width, height, depthOrArrayLayers ]
    )
    • texture must have a usage of GPUTextureUsage.COPY_DST

    • mipLevel, origin, and aspect all have defaults so they often do not need to be specified

    • bytesPerRow: This is how many bytes to advance to get to the next block row of data. This is required if you are copying more than 1 block row. It is almost always true that you’re copying more than 1 block row so it is therefore almost always required.

    • rowsPerImage: This is the number of block rows to advance to get from the the start of one image to the next image. This is required if you are copying more than 1 layer. In other words, if depthOrArrayLayers in the size argument is > 1 then you need to supply this value.

    • aspect really only comes into play when copying data to a depth-stencil format. You can only copy to one aspect at a time, either the depth-only or the stencil-only.

  • copyBufferToBuffer, like the name suggests, copies data from one buffer to another.

    1
    2
    3
    4
    5
    6
    7
    encoder.copyBufferToBuffer(
    source, // buffer to copy from
    sourceOffset, // where to start copying from
    dest, // buffer to copy to
    destOffset, // where to start copying to
    size, // how many bytes to copy
    )
    • source must have a usage of GPUBufferUsage.COPY_SRC
    • dest must have a usage of GPUBufferUsage.COPY_DST
    • size must be a multiple of 4
  • copyBufferToTexture, like the name suggests, copies data from a buffer to a texture.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    encoder.copyBufferToTexture(
    // details of the source buffer
    { buffer, offset: 0, bytesPerRow, rowsPerImage },

    // details of the destination texture
    { texture, mipLevel: 0, origin: [0, 0, 0], aspect: "all" },

    // size:
    [ width, height, depthOrArrayLayers ]
    )
    • texture must have a usage of GPUTextureUsage.COPY_DST

    • buffer must have a usage of GPUBufferUsage.COPY_SRC

    • bytesPerRow must be a multiple of 256

  • copyTextureToBuffer like the name suggests, copies data from a texture to a buffer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    encoder.copyTextureToBuffer(
    // details of the source texture
    { texture, mipLevel: 0, origin: [0, 0, 0], aspect: "all" },

    // details of the destination buffer
    { buffer, offset: 0, bytesPerRow, rowsPerImage },

    // size:
    [ width, height, depthOrArrayLayers ]
    )
    • texture must have a usage of GPUTextureUsage.COPY_SRC

    • buffer must have a usage of GPUBufferUsage.COPY_DST

    • bytesPerRow must be a multiple of 256

  • copyTextureToTexture copies a portion of one texture to another, The two textures must be must either be the same format, or they must only differ by the suffix '-srgb'.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    encoder.copyTextureToBuffer(
    // details of the source texture
    src: { texture, mipLevel: 0, origin: [0, 0, 0], aspect: "all" },

    // details of the destination texture
    dst: { texture, mipLevel: 0, origin: [0, 0, 0], aspect: "all" },

    // size:
    [ width, height, depthOrArrayLayers ]
    );
    • src.texture must have a usage of GPUTextureUsage.COPY_SRC
    • dst.texture must have a usage of GPUTextureUsage.COPY_DST
    • width must be a multiple of block width
    • height must be a multiple of block height
    • src.origin[0] or .x must be a multiple block width
    • src.origin[1] or .y must be a multiple block height
    • dst.origin[0] or .x must be a multiple block width
    • dst.origin[1] or .y must be a multiple block height
  • Shaders: Shaders can write to storage buffers, storage textures, and indirectly they can render to textures. Those are all ways of getting data into buffers and textures. In other words you can use shaders to generate data.

  • mapping buffers: You can map a buffer. Mapping a buffer means making it available to read or write from JavaScript. At least in version 1 of WebGPU, mappable buffers have severe restrictions, namely, a mappable buffer can can only be used as a temporary place to copy from. A mappable buffer can not be used as any other type of buffer (like a Uniform buffer, vertex buffer, index buffer, storage buffer, etc…)

    You can create a mappable buffer with 2 combinations of usage flags.

    • GPUBufferUsage.MAP_READ | GPU_BufferUsage.COPY_DST

      This is a buffer you can use the copy commands above to copy data to from another buffer or a texture, then map it to read the values in JavaScript

    • GPUBufferUsage.MAP_WRITE | GPU_BufferUsage.COPY_SRC

      This is a buffer you can map in JavaScript, you can then put data in it from JavaScript, and finally unmap it and use the and the copy commands above to copy its contents to another buffer or texture.

    The process of mapping a buffer is asynchronous. You call buffer.mapAsync(mode, offset = 0, size?) where offset and size are in bytes. If size is not specified it’s the size of the entire buffer. mode must be either GPUMapMode.READ or GPUMapMode.WRITE and must of course match the MAP_ usage flag you passed in when you created the buffer.

    mapAsync returns a Promise. When the promise resolves the buffer is mappable. You can then view some or all of the buffer by calling buffer.getMappedRange(offset = 0, size?) where offset a byte offset into the portion of the buffer you mapped. getMappedRange returns an ArrayBuffer

    Once mapped, the buffer is not usable by WebGPU until you call unmap. The moment unmap is called the buffer disappears from JavaScript.

  • mappedAtCreation: true is a flag you can add when you create a buffer. In this case, the buffer does not need the usage flags GPUBufferUsage.MAP_WRITE. This is a special parameter to let you put data in the buffer on creation. You add the flat mappedAtCreation: true when you create the buffer. The buffer is created, already mapped for writing.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const buffer = device.createBuffer({
    size: 16,
    usage: GPUBufferUsage.UNIFORM,
    mappedAtCreation: true,
    });
    const arrayBuffer = buffer.getMappedRange(0, buffer.size);
    const f32 = new Float32Array(arrayBuffer);
    f32.set([1, 2, 3, 4]);
    buffer.unmap();

Optional Features and limits

When you request an adapter with

1
const adapter = await navigator.gpu?.requestAdapter()

The adapter will have a list of limits on adapter.limits and array of feature names on adapter.features.

By default, when you request a device, you get the minimum limits and you get no optional features. The hope is, if you stay under the minimum limits, then your app will run on all devices that support WebGPU.

But, given the available limits and features listed on the adapter, you can request them when you call requestDevice by passing your desired limits as requiredLimits and your desired features as requiredFeatures

1
2
3
4
5
const adapter = await navigator.gpu?.requestAdapter();
const device = adapter?.requestDevice({
requiredLimits: { maxBufferSize: 1024 * 1024 * 1024 },
requiredFeatures: [ 'float32-filterable' ],
});

The recommended way to use features and limits is to decide on what you absolutely must have and throw errors if user’s device can not support those features.

WGSL

attributes

The word attributes has 2 meanings in WebGPU. One is vertex attributes. The other is in WGSL where an attribute starts with @.

For a vertex shader, inputs are defined by the @location attributes of the entry point function of the vertex shader.

1
2
3
4
5
6
7
@vertex vs1(@location(0) foo: f32, @location(1) bar: vec4f) ...

struct Stuff {
@location(0) foo: f32,
@location(1) bar: vec4f,
};
@vertex vs2(s: Stuff) ...

For inter stage variables, @location attributes define the location where the variables are passed between shaders.

1
2
3
4
5
6
7
8
9
10
11
12
13
struct VSOut {
@builtin(position) pos: vec4f,
@location(0) color: vec4f,
@location(1) texcoords: vec2f,
};

struct FSIn {
@location(1) uv: vec2f,
@location(0) diffuse: vec4f,
};

@vertex fn foo(...) -> VSOut { ... }
@fragment fn bar(moo: FSIn) ...

For fragment shaders, @location specifies which GPURenderPassDescriptor.colorAttachment to store the result in.

1
2
3
4
5
struct FSOut {
@location(0) albedo: vec4f;
@location(1) normal: vec4f;
}
@fragment fn bar(...) -> FSOut { ... }

@builtin attribute is used to specify that a particular variable’s value comes from a built-in feature of WebGPU.

Builtin Name Stage IO Type Description
vertex_index vertex input u32 Index of the current vertex within the current API-level draw command, independent of draw instancing.

For a non-indexed draw, the first vertex has an index equal to the firstVertex argument of the draw, whether provided directly or indirectly. The index is incremented by one for each additional vertex in the draw instance.

For an indexed draw, the index is equal to the index buffer entry for the vertex, plus the baseVertex argument of the draw, whether provided directly or indirectly.

instance_index vertex input u32 Instance index of the current vertex within the current API-level draw command.

The first instance has an index equal to the firstInstance argument of the draw, whether provided directly or indirectly. The index is incremented by one for each additional instance in the draw.

position vertex output vec4 Output position of the current vertex, using homogeneous coordinates. After homogeneous normalization (where each of the x, y, and z components are divided by the w component), the position is in the WebGPU normalized device coordinate space. See WebGPU § 3.3 Coordinate Systems.
fragment input vec4 Framebuffer position of the current fragment in framebuffer space. (The x, y, and z components have already been scaled such that w is now 1.) See WebGPU § 3.3 Coordinate Systems.
front_facing fragment input bool True when the current fragment is on a front-facing primitive. False otherwise.
frag_depth fragment output f32 Updated depth of the fragment, in the viewport depth range. See WebGPU § 3.3 Coordinate Systems.
local_invocation_id compute input vec3 The current invocation’s local invocation ID, i.e. its position in the workgroup grid.
local_invocation_index compute input u32 The current invocation’s local invocation index, a linearized index of the invocation’s position within the workgroup grid.
global_invocation_id compute input vec3 The current invocation’s global invocation ID, i.e. its position in the compute shader grid.
workgroup_id compute input vec3 The current invocation’s workgroup ID, i.e. the position of the workgroup in the workgroup grid.
num_workgroups compute input vec3 The dispatch size, vec(group_count_x, group_count_y, group_count_z), of the compute shader dispatched by the API.
sample_index fragment input u32 Sample index for the current fragment. The value is least 0 and at most sampleCount-1, where sampleCount is the MSAA sample count specified for the GPU render pipeline.
See WebGPU § 10.3 GPURenderPipeline.
sample_mask fragment input u32 Sample coverage mask for the current fragment. It contains a bitmask indicating which samples in this fragment are covered by the primitive being rendered.
See WebGPU § 23.3.11 Sample Masking.
fragment output u32 Sample coverage mask control for the current fragment. The last value written to this variable becomes the shader-output mask. Zero bits in the written value will cause corresponding samples in the color attachments to be discarded.
See WebGPU § 23.3.11 Sample Masking.

Builtin functions

See the WGSL Function reference.

域名系统

域名系统(Domain Name System,DNS)是因特网使用的命令系统,用来把便于人们记忆的具有特定含义的主机名转换为便于机器处理的IP地址。

DNS系统采用C/S模型,其协议运行在UDP之上,使用53号端口。

层次域名空间

英特网采用层次树状结构的命名方法。采用这种命名方法,任何一个连接到因特网的主机或路由器,都有一个唯一的层次结构名称,即域名。

域名又分为顶级域名、二级域名、三级域名等。每个域名都由标号序列组成,每个域名之间使用“.”分隔。

例如www.google.com,其中com是顶级域名,google是二级域名,www是三级域名

在域名中有以下几点需要注意:

  • 标号中的英文不区分大小写。

  • 标号中除连字符(-)外不能使用其他的标点符号

  • 每个标号不超过63个字符,多标号组成的完整域名最长不超过255个字符

  • 级别最低的域名写在最左边,级别最高的顶级域名写在最右边

顶级域名分为以下三大类:

  • 国家顶级域名。国家和某些地区的域名,如“.cn”表示中国,“.us”表示美国,“.uk”代表英国,“.hk”代表中国香港特别行政区。

  • 通过顶级域名。常用的有“.com”(公司)、“.net”(网络服务机构)、“.org”(非营利性组织)和“.gov”(美国政府部分)等。

  • 基础结构域名。这种顶级域名只有一个,即`arpa,用于反向域名解析,因此又称反向域名

b02501e61f0baf468dcdec51aedfcee0.jpeg

在域名系统中,每个域分别由不同的组织进行管理。每个组织都可以将它的域再分成一定数目的子域,并将这些子域委托给其他组织去管理。

域名服务器

根域名服务器

根域名服务器是最高层次的域名服务器,所有的根域名服务器都知道所有的顶级域名服务器的IP地址。

根域名服务器是最重要的域名服务器,不管是哪个本地域名服务器服务器,若要对因特网上任何一个域名进行解析,只要自己无法解析,就首先求助于一个根域名服务器。

需要注意的是,根域名服务器用来管辖顶级域(如.com),通常它并不直接把待查询的域名直接转换成IP地址,而直接告诉本地域名服务器下一步应当找哪个顶级域名服务器进行查询。

顶级域名服务器

这些顶级域名服务器负责管理在该顶级域名服务器注册的所有二级域名。收到DNS查询请求时,就给出相应的回答(可能是最后的结果,也可能是下一步应当查找的域名服务器的IP地址)

授权域名服务器(权限域名服务器)

每台主机都必须在授权服务器处登记。实际上,许多域名服务器都同时充当本地域名服务器和授权域名服务器。授权域名服务器总是能将其管辖的主机名转换为该主机的IP地址

本地域名服务器

当一台主机发出DNS查询请求时,这个查询请求报文就发送给该主机的本地域名服务器。

域名解析过程

域名解析是指把域名映射成IP地址或把IP地址映射成域名的过程。前者称为正向解析,后者称为反向解析

当一个DNS服务器收到收到DNS查询结果时,它能将该DNS信息缓存在高速缓存中。DNS服务器将在一段时间后丢弃高速缓存中的信息。

域名解析有两种方式:递归查询和递归与迭代想结合的查询。

递归查询

3d9502fb237133b7bb0d61c018010372.jpeg

如果本地主机所询问的本地域名服务器不知道被查询域名的IP地址,那么本地域名服务器就以DNS客户的身份,向根域名服务器继续发出查询请求报文。然后根域名服务器继续向顶级域名发出查询请求。

在这种情况下,本地域名服务器只需要向根域名服务器查询一次,后面的几次查询都是递归地在其他几个域名服务器之间进行的。本地域名服务器从根域名服务器得到所需的IP地址。

迭代查询

50571c98a84e4b3ae3e42922c98484ea.jpeg

当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出要查询的IP地址,要么告诉本地域名服务器顶级域名服务器的IP地址,然后让本地域名服务器向这个顶级域名服务器进行后续的查询。同样,顶级域名服务器收到查询报文后,要么给出最后的IP地址,要么给出本地域名服务器权限域名服务器的IP地址,然后本地域名服务器向权限域名服务器发出查询然后的到最后结果。

电子邮件系统

电子邮件系统的组成结构

一个电子邮件系统有三个最重要的组成构件:用户代理(User Agent)、邮件服务器和电子邮件使用的协议。

4e673307b243d2ebdcc974913fa63a9a.jpeg

用户代理

用户与电子邮件系统的接口。用户代理使用户能够通过一个很友好的接口发送和接收邮件,通常情况下,用户代理就是一个运行在PC上的程序。

邮件服务器

邮件服务器采用C/S工作方式,但它能够同时充当客户和服务器。当邮件服务器作为邮件的接收方时充当服务器,当邮件服务器作为邮件的发送方时充当客户

邮件发送协议和读取协议

邮件发送协议用于用户代理向邮件服务器发送邮件或在邮件服务器之间发送邮件,通常使用的是SMTP;邮件读取协议用于用户代理从邮件服务器读取邮件,如POP3。

SMTP采用的是Push的通信方式,即在用户代理向邮件服务器发送邮件及在邮件服务器之间发送邮件时,使用SMTP将邮件推送到服务器端。

POP3采用的是Pull的通信方式,即用户读取邮件时,用户代理向邮件服务器发出请求,拉取用户邮箱中的邮件

电子邮件格式与MIME

电子邮件格式

一个电子邮件分为信封和内容两大部分,邮件内容又分为首部和主体两部分。用户写好首部后,邮件系统自动地将信封所需的信息提取出来并写在信封上,用户不需要亲自填写信封上的信息

邮件内容的首部包含一些首部行,每个首行由一个关键字后跟冒号再后跟值组成。有些关键字是必需的,有些是可选的。

To是必需的关键字,后面跟一个或多个收件人的电子邮件地址。电子邮件地址的规定格式为:收件人邮箱名@邮箱所在主机的域名。例如xxxx@xxx.com

From是必填关键字,但它通常由邮件系统自动填入

Subject是可选关键字,是邮件的主题,反映了邮件的主要内容。

邮件内容的首部与主体之间用一个空行进行分割。

多用途网络邮件扩充(MIME)

由于SMTP只能传送一定长度的ASCII码,许多其他国家的文字和一些视频、二进制文件无法传送,因此提出了多用途网络邮件扩充(Multipurpose Internet Mail Extensions, MIME)

MIME并未改动SMTP或取代它。MIME继续使用目前的格式,但增加了邮件主体的结构,并定义了传送非ASCII码的编码规则。也就是说,MIME邮件可以在现有的电子邮件程序的协议下传送

8eef9850688ce5c36f7286f754c199a0.jpeg

MIME主要包括以下三个内容:

1、5个新的邮件首部字段,包括MIME版本、内容描述、内容标识、内容传送编码和内容类型

2、定义了许多邮件内容的格式,对多媒体电子邮件的表示方法进行了标准化

3、定义了传送编码,可对任何内容格式进行转换,而不会被邮件系统改变

SMTP和POP3

SMPT

简单邮件传输协议(Simple Mial Transfer Protocol, SMTP)是一种提供可靠且有效的电子邮件传输的协议,他控制两个互相通信的SMTP进程交换信息。SMTP使用TCP连接,端口号为25

连接建立

发件人的邮箱发送到发送方的邮件服务器的邮件缓存后,SMTP客户就每隔一定时间对邮件缓存扫描一次。如发现有邮件,就使用SMTP与接收方邮件服务器建立TCP连接。连接建立后,接收方SMTP服务器就发出220 service ready。然后SMTP发送方向SMTP接收方发出HELO命令,附上发送方的主机名。

SMTP不使用中间邮件服务器。TCP连接总是在发送方和接收方这两个邮件服务器之间直接建立,而不管他们相隔多远。接收方的邮件服务器因故障暂时不能建立连接时,发送方的邮件服务器只能等待一段时间后再次尝试连接。

邮件传送

连接建立后,就可开始传送邮件。邮件的传送从MAIL命令开始,MAIL命令后面有发件人的地址。如MAIL FROM:xxx@xxx.com。若SMTP服务器已准备好接收邮件,则回答250OK。接着SMTP客户端发送一个或多个RCPT(收件人recipient的缩写),格式为RCPT TO: <收件人地址>。每发送一个RCPT命令,都应有相应的信息从SMTP服务器返回,如250 OK或550 No such user here

RCPT命令的作用是,先弄清接收方系统是否已做好接收邮件的准备,然后才发送邮件。

获得OK的回答后,客户端就使用DATA命令,表示要开始传输邮件的内容。正常情况下,SMTP服务器端回复是354 Start mail input; end with .表示回车换行。此时SMTP客户端就可开始传送邮件内容,并用.(两个回车,中间一个点)表示邮件内容的接收

连接释放

邮件发送完毕后,SMTP客户端发送QUIT命令,SMTP服务器端返回的信息是221(服务关闭),表示SMTP同意释放连接。邮件传送的全过程就此结束

POP3

邮局协议(Post Office Protocol, POP)是一个非常简单但功能有限的邮件读取协议,现在使用的是它的第三个版本POP3。当用户读取邮件时,用户代理向邮件服务器发出请求,拉取用户邮箱中的邮件。

POP也使用C/S的工作方式,在传输层使用TCP,端口号为110。POP有两种工作方式——下载并保留和下载并删除。在下载并保留方式下,用户从邮件服务器上读取邮件后,邮件依然会保存在邮件服务器上,而使用下载并删除的方式时,邮件一旦被读取,就被从邮件服务上删除

另一个邮件接收协议是网际报文存取协议(IMAP),它要比POP复杂得多,但目前还只是因特网的建议标准。

此外,随着万维网的发展,目前出现了很多基于万维网的电子邮件。这种电子邮件的特点是浏览器与邮件服务器之间邮件发送或接收使用的是HTTP,而仅在不同邮件服务器之间传送邮件时才使用SMTP。

常用应用层协议端口使用情况

应用程序 FTP数据连接 FTP控制连接 TELNET SMTP DNS TFTP HTTP POP3 SNMP
使用协议 TCP TCP TCP TCP UDP UDP TCP TCP UDP
端口号 20 21 23 25 53 69 80 110 161

传送层的功能

传输层属于面向通信部分的最高层,同时也是用户功能中的最底层。(注意通信子网没有传输层)

复用和分用

复用是指发送方不同的应用进程都可使用同一个传输层协议传送数据:分用是指接收方的传输层在剥去报文的首部后能够把这些数据正确交付到目的应用进程。

传输层的寻址和端口

端口的作用

端口是传输层服务访问点,端口标识的是主机中的应用进程

数据链路层的服务访问点(SAP)是MAC,网络层的SAP是IP地址,传输层的SAP是端口。

端口号

端口号长度为16bit,能够表示65536(0~65535)个不同的端口号。端口号只有本地意义,即端口号只表示本计算机应用层中的各进程,在因特网中不同计算机的相同端口号是没有联系的。根据端口号范围可将端口分为两类:

1、服务端使用的端口号。这里又分为两类,最重要的一类是熟知端口号,数值为0~1023,IANA(互联网地址指派机构)把这些端口指派给了TCP/IP最重要的一些应用程序,让所有的用户都知道。另一类称为登记端口号,数值为1024~49151。它是供没有熟知端口号的应用程序使用的。使用这类端口号必须在IANA登记。

2、客户端使用的端口号,数值为49152~65535。这类端口号仅在客户进程运行时才动态选择,因此又称短暂端口号(也称临时端口)。通信结束后,刚用过的客户端口号就不复存在,从而这个端口号就可提供其他客户进程使用。

套接字

在网络中采用发送发和接收方的套接字(socket)组合来识别端点。套接字实际上是一个通信端点,套接字=(主机IP地址,端口号)。它唯一地标识网络中的一台主机和其上的一个应用(进程)

无连接服务与面向连接服务。

TCP/IP协议族在IP层之上使用了两个传输协议:一个是面向连接的传输控制协议(TCP),采用TCP时,传输层向上提供的是一条全双工的可靠逻辑信道;另一个是无连接的用户数据报协议(UDP),采用UDP时,传输层向上提供的是一条不可靠的逻辑信道。TCP不提供广播和组播服务

UDP

UDP只是做了传输协议能够做的最少工作,它仅在IP和数据报服务之上增加了两个最基本的服务:复用和分用以及差错检查。

UDP具有以下优点:

1、UDP无须建立连接,因此UDP不会引入建立连接的时延。

2、无连接状态。UDP不维护连接状态。

3、分组首部开销小,TCP有20B的首部开销,而UDP只有8B的开销。

4、应用层能更好地控制要发送的数据和发送时间。UDP没有拥塞控制,因此网络中的拥塞不会影响主机的发送效率。某些实时应用要求以更稳定的速度发送,能容忍一些数据的丢失,但不允许有较大的时延,而UDP正好满足这些应用的需求。

UDP是面向报文的。发送方UDP协议对应用层交下来的报文,在添加首部后就向下交付给IP层,既不合并,也不拆分,而是保留这些报文的边界;接收方UDP对IP层交上来UDP用户数据报在去除首部后就原封不动地交给上层应用程序,一次交付一个完整的报文。因此报文不可分割,是UDP数据报处理的最小单位。

UDP的首部格式

2b8fed7f7a91118fbcbef80137bcebc8.jpeg

UDP首部有8B,由4个字段组成,每个字段的长度都是2B。

1、源端口。源端口号,在需要对方回信时选用,不需要时可用0.

2、目的端口。目的端口号,这在终点交付报文时必须使用到。

3、长度。UDP数据报的长度(包括首部和数据),其最小值为8(仅有首部)

4、校验和。校验首部和数据部分,检测UDP数据报在传输中是否有错。有错就丢弃。该字段是可选的,当源主机不想计算校验和,则直接令该字段为0。

如果接收方UDP发现收到的报文中的目的端口号不正确(即不存在对应端口号的应用程序),那么就丢弃该报文,并由ICMP发送“端口不可达”差错报文给发送方。

UDP校验

在计算校验和的时,要在UDP数据报之前增加12B的伪首部,得到临时的UDP数据报。校验和就是按照这个临时的UDP数据报计算的。伪首部既不向下传送也不向上传递c557e6b203553e314753972f16d00b78.jpeg

发送方首先把全0放入校验和字段并添加伪首部,然后把UDP数据报视为许多16位的字连接起来。若UDP数据报的数据部分不是偶数字节,则要在数据部分末尾添加一个全0字节(次字节最后不会发送,仅在计算校验和时使用)。计算校验和时,采用二进制反码计算再求反码,最后结果作为校验和。

接收方把收到的UDP数据报加上伪首部(如果不是偶数个字节,那么还需要补上全0字节),按二进制反码计算出校验和。当无差错时结果应全为1,否则表明有差错出现。

TCP

TCP协议的特点

1、TCP是面向连接的传输层协议

2、每条TCP连接只能是点对点的(一对一)

3、TCP提供可靠的交付服务,保证传送数据的无差错、不丢失、不重复且有序

4、TCP提供全双工通信,为此TCP连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据

5、TCP是面向字节流的,TCP把应用程序交下来的数据仅视为一连串的无结构的字节流

TCP数据报

TCP传送的数据单元称为报文段,其首部的前20B是固定的。TCP报文段的首部最短为20B,后面有4N字节是根据需要而增加的选项,通常为长度的整数倍。

536fcd6dbc7d56f295a5364e1186693e.jpeg

  • 源端口和目的端口字段。各占2B。

  • 序号字段。占4B。TCP连接传送的数据流中的每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号

  • 确认号字段。占4B,是期望收到对方的下一个报文段的数据的第一个字节的序号。若确认号为N,则表明到序号N-1为止的所有数据都已正确收到

  • 数据偏移(即首部长度)。占4位,数据偏移的单位为4B。因此当字段的值为15时,达到TCP首部的最大长度60B

  • 保留字段。占6位,保留为今后使用,但目前应置值为0,该字段可以忽略不计。

  • 紧急位URG。URG=1时,表明紧急指针字段有效。它告诉系统报文段中有紧急数据,应尽快传送,但URG需要和紧急指针配套使用,即数据从第一个字节到紧急指针所指字节就是紧急数据。

  • 确认位ACK。只有当ACK=1时确认号字段才有效。在建立连接后所有传送的报文都必须把ACK置为1

  • 推送位PSH(Push)。接收TCP收到PSH=1的报文段,就尽快交付给接收应用程序,而不再等到整个缓存都填满后再向上交付。

  • 复位位RST(Reset)。RST=1时,表明TCP连接中出现严重的差错,必须释放连接,然后再重新建立传输连接。

  • 同步位SYN,SYN=1时表示这是一个连接请求或连接接收报文

  • 终止位FIN(Finish)。用来释放一个连接。FIN=1表明此报文段的发送方的数据已发送完毕,并要求释放传输连接。

  • 窗口字段。占2B。它指出现在允许对方发送的数据量,窗口值作为接收方让发送方设置其发送窗口的依据,单位为字节。

  • 校验和。占2B。校验和字段检验的范围包括首部和数据两部分。在计算校验和时,和UDP一样,要在TCP报文段的前面加上12B的伪首部(只需要将UDP伪首部的第4个字段,即协议字段的17改成6,其他的和UDP一样)

  • 紧急指针字段。占16位,指出在本报文段中紧急数据共有多少字节(紧急数据放在本报文段数据的最前面)

  • 选项字段。长度可变。TCP最初之规定了一种选项,即最大报文段长度(Maximum Segment Size, MSS)。MSS是TCP报文段中的数据字段的最大长度。

  • 填充字段,填充字段。这是为了整个首部长度是4B的整数倍。

TCP连接管理

TCP是面向连接的协议,因此每个TCP连接都有三个阶段:连接建立、数据传送和连接释放。在TCP连接建立的过程中,要解决以下三个问题:

1、要使每一方都能够确知对方的存在。

2、要允许双方协商一些参数(如最大窗口值,是否使用窗口扩大哦选项、时间戳选项及服务质量等)

3、能够对运算实体资源(如缓存大小、连接表中的项目等)进行分配

TCP连接的端口称为套接字(socket)。每条TCP连接唯一地被通信两端的两个套接字确定。TCP连接中,主动发起连接建立的应用进程称为客户机(Client),而被动等待建立的应用程序称为服务器(Server)

连接的建立

TCP连接的建立要经历以下3个步骤,通常称为三次握手。

47d7cdc1abe00d8c2c24b70a4187972b.jpeg

第一步:客户机TCP首先向服务器的TCP发送一个连接请求报文段。这个特殊的报文段中不含有应用层数据,其首部中的ACK=0,SYN=1。另外,客户机会随机选择一个起始序号seq=x(连接请求报文不携带数据,但要消耗一个序号)

第二步:为该TCP连接分配TCP缓存和变量。在确认报文段中,SYN和ACK位都被置为1,确认号字段的值为x+1,并且服务器随机产生起始序号seq=y(确认报文不携带数据,但也要消耗一个序号)

第三部:当客户机收到确认报文段,还要向服务器给出确认,并且也要给连接分配缓存和变量。这个报文段的ACK=1,seq=x+1。该报文段可以携带数据

由于服务器端的资源是在完成第二次握手时分配的,所以服务器易收到SYN洪泛攻击。

TCP连接的释放

TCP连接释放的过程通常称为四次挥手

6b05fd7b1e103d89e8b7f3c89c52319e.jpeg

第一步:客户机打算关闭连接时,向其TCP发送一个连接释放报文段,并停止发送数据。该报文段的ACK=1,FIN=1,seq=u,它等于前面已经发送过的数据的最后一个字节的序号+1(FIN报文段即使不携带数据,也要消耗一个序号)。发送FIN报文时,发送FIN的一端不能再发送数据,即关闭了其中一条数据通路,但对方还可以发送数据

第二步:服务器收到连接释放报文段后发出确认,确认号是ack=u+1,而这个报文段的自己的序号v,等于它前面已传送过来的数据的最后一个字节的序号+1。此时,从客户机到服务器这个方向的连接就释放了,TCP连接处于半关闭状态。但服务器若发送数据,客户机仍要接收,即从服务器到客户机这个方向的连接并未关闭。

第三步:若服务器已经没有要向客户机发送的数据,就通知TCP释放连接,此时其发出FIN=1的连接释放报文段。

第四步:客户机收到释放连接报文段后,必须发出确认。在确认报文段中,ACK=1。此时TCP连接还未释放,必须经过时间等待计时器设置的时间2MSL(Maximum Segment Lifetime,最大分节生命期后,这是一个IP数据包在网络上生存的最长时期),客户端才进入连接关闭状态

TCP可靠传输

TCP提供的可靠传输服务保证接收方进程从缓冲区读出的字节流与发送方发出的字节流完全一样。TCP使用了校验、序号、确认和重传等机制来达到这一目的。

序号

TCP首部的序号字段用来保证数据能有序提交给应用层,TCP把数据视为一个无结构但有序的字节流,序号建立在传送的字节流之上,而不建立在报文段之上。

确认

TCP默认使用累计确认,即TCP只确认数据流中至第一个丢失字节为止的字节,例如,接收方收到了发送方字节的0~2字节和6~7字节。由于某种原因,接收方还未收到字节3~5的报文段,此时B仍在等待字节3(和其后面的字节),因此接收方到发送方的下一个报文段将确认号字段置为3。

重传

有两种事件会导致TCP对报文段进行重传

超时

TCP每发送一个报文段,就对这个报文段设置一次计时器。计时器设置的重传时间到期但还未收到确认时,就要重传这一报文段。

冗余ACK(冗余确认)

超时触发重传存在的一个问题时超时周期往往太长,冗余ACK可以解决这一问题。冗余ACK就是再次确认某个报文段的ACK。TCP规定每当比期望序号大的失序报文段到达时,就发送一个冗余ACK。TCP规定当发送方收到对同一个报文段的3个冗余ACK时(总共接收4个ACK),就可以认为跟在这个被报文段已经丢失。这种技术通常称为快速重传。

TCP流量控制

在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小,这称为接收窗口rwnd,即调整TCP报文段首部中的“窗口”字段值,来限制发送方向网络注入报文的速率。同时,发送方根据其对当前网络拥塞程序的估计而确定的窗口值,这称为拥塞窗口cwnd发送方的发送窗口的实际大小取决于rwnd和cwnd的最小值。

数据链路层的滑动窗口协议的窗口大小不能动态变化,传输层的则可以动态变化

TCP拥塞控制

出现拥塞时,端点并不了解到拥塞发生的细节,对通信连接的端点来说,拥塞往往表现为通信时延的增加。

拥塞控制与流量控制的区别:拥塞控制是让网络能够成熟现有的网络负荷,是一个全局性的过程,设计所有的主机、所有的路由器。流程控制往往是指点对点的通信量的控制,它所要做的是抑制发送端发送数据的速率,以便使接收端来得及接收。

慢开始和拥塞控制

慢开始算法

在TCP刚刚连接好并开始发送TCP报文段时,先令拥塞窗口cwnd=1,即一个最大报文段长度MSS。每收到一个对新报文段的确认后,将cwnd加1,即一个最大报文段长度MSS。在经过一个RTT后(也称一个传输轮次),拥塞窗口的大小变为一个轮次前的两倍(一个传输轮次中包含多次发送和确认的过程)。

使用慢开始算法后,每经过一个传输轮次(即往返时延RTT),拥塞窗口cwnd就会加倍。这样,慢开始一直把拥塞窗口cwnd增大到一个规定的慢开始门限ssthread(域值),然后改用拥塞避免算法。

拥塞避免算法

发送端的拥塞窗口cwnd每经过一个往返时延RTT就增加一个MSS的大小,而每当出现一次超时(网络拥塞)时,令慢开始门限ssthread等于当前cwnd的一半。

当拥塞窗口等于阈值时,既可以使用慢开始算法,又可以使用拥塞避免算法(通常做法)。

网络拥塞的处理

网络出现拥塞时,无论是在慢开始阶段还是在拥塞避免阶段,只要发送发检测到超时事件的发生,就要把慢开始门限ssthread设置为出现拥塞时的发送方的cwnd值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。

注意在慢开始阶段cwnd不能跃过ssthread值,也就是说当cwnd * 2 > ssthread时,cwnd = ssthread。

一个例子:

6e8a9b01fa8b423a25e3b7715cd528b7.jpeg

快重传和快恢复

快重传

当发送方连续收到三个重复的ACK报文时,直接重传对方尚未收到的报文段,而不必等待那个报文段设置的重传计时器超时。

快恢复

发送端收到连续三个冗余ACK(即重复确认)时,把慢开始门限ssthread设置为出现拥塞时发送方cwnd的一半,把cwnd的值设置为慢开始门限ssthread改变后的数值(不是变为1),然后开始执行拥塞避免算法(每过一个RTT,cwnd值加1),使拥塞窗口缓慢地线性增大。

由于跳过了cwnd从1开始的慢开始过程,所以被称为快恢复。

一个例子:

176defecda15addb2938ae91681fc889.jpeg

虚线为慢开始的处理过程

在流量控制中,发送方发送数据的量由接收方决定,而在拥塞用控制中,则由发送方自己通过检测网络状况决定。

实际上,慢开始、拥塞避免、快重传和快恢复几种算法应是同时应用在拥塞控制机制中,当发送方检测到超时,就采用慢开始和拥塞避免,当发送方接收到冗余ACK时,就采用快重传和快恢复

网络层的功能

异构网络互联

网络层所要完成的任务之一就是使异构网络实现互联,所谓网络互联,是指将两个以上的计算机网络使用中间设备相互连接起来,中间设备又称为中间系统或中继系统

路由与转发

路由器主要完成两个功能:一是路由选择(确定哪一条路径),二是分组转发(当一个分组到达时所采取的动作)。前者是根据特定的路由选择协议构造出路由表,同时经常或定期地和相邻路由器交换路由信息而不断地更新和维护路由表。后者处理通过路由器的数据流,关键操作是转发表查询、转发及相关的队列管理和任务调度等。

路由表根据路由选择算法得到,而转发表是从路由表得出的。转发表的结构应当使查找过程最优化,路由表则需要对网络拓扑变化的计算最优化。

拥塞控制

在通信子网中,因出现过量的分组而引起网络性能下降的现象称为拥塞。单一地增加资源并不能解决拥塞。

拥塞控制不同于流量控制,流量控制所要做的是抑制发送端发送数据的速率,而拥塞控制必须确保通信子网能够传送待传送的数据,是一个全局性的问题

拥塞控制的方法有两种:

  • 开环控制(对手工操作开)。在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞。这是一种静态的预防方法,一旦整个系统启动并运行,中途就不再修改。开环控制的手段包括确定何时可接收新流量、何时可丢弃分组及丢弃那些分组,确定何种调度决策等。所有这些手段的共性是,在做决定时不考虑当前网络的状态。

  • 闭环控制(对手工操作闭)。事先不考虑有关发生拥塞的各种因素,采用监测网络系统去监视,及时监测哪里发生了拥塞,然后调整网络系统的运行。闭环控制基于反馈环路的概念,是一种动态的方法。

IPv4

一个IP分组由首部和数据两部分组成。首部前一部分的长度固定,共20B,是所有IP分组必须具有的。后面是一些可选字段,其长度可变。

IP数据报的格式如下:

cc66143dbe9f2a4a0d3f775a66233312.png

  • 版本:指IP的版本,目前广泛使用的版本号为4

  • 首部长度:占4位,以32位(4B)为单位,最大值为60B(15*4B)。最常用的首部长度为20B。

  • 区分服务:说明数据报的优先级,值越大优先级越高

  • 总长度:占16位,指首部和数据之和的长度,单位为1B,因此数据报的最大长度为$2^{16}-1=65535B$。以太网帧的最大传输单元(MTU)为1500B,因此当一个IP数据报封装成帧时,数据报的总长度(首部加数据)一定不能超过下面数据链路层的MTU值。

  • 标识:占16位,它是一个计数器,每产生一个数据报就加1,并赋值给标识字段。但它并不是“序号”(IP是无连接服务)。当一个数据报的长度超过网络的MTU时,必须分片,此时每个数据报片都复制一次标识号,以便能正确重装成原来的数据报

  • 标志:占3位,标志字段的最低位(第二行第18位)为MF(More Fragment),MF=1表示后面还有分片,MF=0表示最后一个分片,标志字段中间一位为DF(Don’t Fragment),只有当DF=0(Don't Fragment=false)时才允许分片

  • 片偏移:占13位,它指出较长的分组在分片后,某片在原分组中的相对位置。片偏移以8B为单位,即每个分片的长度一定时8B的整数倍(数据部分,不算头部,除却最后一个分片)

  • 首部校验和:占16位,只校验分组的首部,而不校验数据部分

  • 生存时间(TTL,Time To Live):占8位,数据报在网络中可通过的路由器的最大值,以确保分组不会永远在网络中循环。路由器在转发分组,先把TTL减1。若TTL被减为0,则该分组必须丢弃

  • 协议:占8位,指出此分组应交给哪个传输层协议。其中值6表示TCP,值为17表示UDP。

  • 源地址字段:占4B,标识发送方的IP地址

  • 目的地址地段:占4B,标识接收方的IP地址

  • 填充:使首部长度为4B的整数倍

IP数据报分片

一个链路层数据报能承载的最大数据称为最大传送单元(MTU)。当IP数据报的总长度大于链路MTU时,就需要将IP数据报中的数据分在两个或多个较小的IP数据报中,这些较小的数据报称为片。片在目的地的网络层被重新组装。由于偏移值的单位是8B,所以除最后一个片外,其他所有片中的有效数据载荷都是8的倍数。

网络层转发分组

从数据报的首部提取目的主机的IP地址D,得出目的网络地址N。若网络N与此路由器直接相连,则把数据报直接交付给目的主机D,这称为路由器的直接交付;否则是间接交付

在不同网络中传送时,MAC帧中的源地址和目的地址都要发生变化,但在网桥和交换机转发帧时,不改变帧的源地址

IPv4地址与NAT

IPv4地址

传统的IPv4地址是分类地址,分为A、B、C、D、E五类,无论哪类IP地址,都由网络号和主机号两部分组成。网络号标识主机(或路由器)所连接到的网络。一个网络号在整个因特网范围内必须是唯一的。一台主机号在它前面的网络号所指明的网络范围内必须是唯一的。

分类的IP地址:

5acdc63b4eb39e9d31a4762feb423baf.png

每个IP地址最前面的数字这样设计的原因是为了构成前缀码,否则将会有解释歧义

在各类IP地址中,有些IP地址具有特殊用途,不用做主机的IP地址:

  • 主机号全为0表示网络本身,不用做IP地址

  • 主机号全为1表示本网络的广播地址,又称直接广播地址

  • 网络号为127的IP地址是环回测试地址,127.0.0.0地址表示主机本身

  • 32位全为0,即0.0.0.0表示本网络上的主机

  • 32位全为1,即255.255.255.255表示整个TCP/IP网络的广播地址,又称受限广播地址

常见的三种类别IP地址的使用范围

网络类别 最大可用网络数 第一个可用的网络号 最后一个可用的网络号 每个网络的最大主机数
A $2^7-2$ 1 126 $2^{24}-2$
B $2^{14}-1$ 128.1 191.255 $2^{16}-2$
C $2^{21}-1$ 192.0.1 223.255.255 $2^8-2$

IP地址是标志一台主机(或路由器)和一条链路的接口,当一台主机同时连接到两个网络中时,该主机就必须同时具有两个相应的IP地址。因此IP网络上的路由器必须至少应具有两个IP地址(路由器每个端口必须至少分配一个IP地址)

网络地址转换NAT(Network Address Translation)

NAT指通过将专用网络地址转换为公用地址,从而对外隐藏内部管理的IP地址。

私有地址划分:

A类:1个A类网段,即10.0.0.0~10.255.255.255

B类:16个B类网段,即172.16.0.0~172.31.255.255

C类:256个C类网段,即192.168.0.0~192.168.255.255

NAT路由转发

NAT路由器至少有一个有效的外部全球地址,NAT表中存放着{本地IP地址:端口}到{全球IP地址:端口}的映射。通过{IP地址:端口}这样的映射方式,可让多个私有IP地址映射到同一个全球IP地址。

一个典型的NAT转换表:

WAN端 LAN端
138.76.29.7:5001 192.168.0.2:2233
138.76.29.7:5060 192.168.0.3:1234

普通路由器在转发IP数据报时,不改变其源IP地址和目的IP地址。而NAT路由器在转发IP数据报时,一定要更换其IP地址(转换源IP地址或目的IP地址)。普通路由器仅工作在网络层,而NAT路由器转发数据报时需要查看和转换传输层的端口号,故NAT路由器工作在传输层

子网划分与子网掩码、CIDR

子网划分

子网划分属于一个网络内部的事情,网络对外仍表现为没有划分子网的网络。

从主机号借用若干比特作为子网号,当然主机号也就相应减少了相同的比特。三级IP地址的结构如下:IP地址={<网络号>、<子网号>、<主机号>}

RFC950规定,对分类的IPv4地址进行子网划分时,子网号不能全为1或全0。但随着CIDR的广泛使用,现在全1和全0的子网号也可以使用

不论是分类的IPv4还是CIDR,其子网中的主机号全0和全1的地址都不能被指派

子网掩码

子网掩码是一个与IP地址相对应的、长32bit的二进制串,他由一串1和跟随的一串0组成。1对应于IP地址中的网络号及子网号,而0对应于主机号。计算机只需将IP地址和其对应的子网掩码逐位“与”,就可以得出相应子网的网络地址。

现在的因特网标准规定,所有的网络都必须使用子网掩码。如果一个网络未划分子网,那么就采用默认的子网掩码。A、B、C类地址默认的的子网掩码为:255.0.0.0、255.255.0.0、255.255.255.0

无分类域间路由选择(CIDR,Classless Inter-Domain Routing)

CIDR消除了传统A、B、C类(只消除了ABC类)地址及划分子网的概念,因而可以更加有效分配IPv4的地址空间。CIDR使用“网络前缀”的概念代替子网络的概念。因此,IP地址的无分类两级编址为:IP={<网络前缀>、<主机号>}。CIDR使用“斜线记法”,即IP地址/网络前缀所占比特数。其中网络前缀所占的比特数对应于网络号的部分,等效于子网掩码中连续1的部分。

CIDR虽然不使用子网,但仍使用”掩码“一词。CIDR不使用子网是指CIDR并没有在32位地址中指明若干位作为子网字段。分配到一个CIDR地址块的组织,仍可以在本组织内根据需要划分一些子网。

将网络前缀都相同的连续IP地址组成“CIDR”地址块。这种地址的聚合称为路由聚合,或称构成超网。

CIDR的优点在于网络前缀长度的灵活性。使用CIDR时,路由表中的每个项目由“网络前缀”和“下一路由地址”组成。在查找路由表时,可能会得到不止一个匹配结果。此时,应该从匹配结果中选择具有最长网络前缀的路由,因为网络前缀越长,其地址块就越小,因而路由就越具体。

ARP、DHCP、ICMP

IP地址与硬件地址

由于路由器的隔离,IP网络中无法通过广播方式依靠MAC地址来完成跨网络的寻址。因此在IP网络的网络层只使用IP地址来完成寻址。IP分组通过多次路由转发到达目标网络后,改为在目标LAN中通过数据链路层的MAC地址以广播方式寻址。这样可以提高路由选择的效率

路由器由于互联多个网络,因此它不仅有多个IP地址,也有多个硬件地址

地址解析协议(ARP,Address Resolution Protocol)

无论在网络层使用什么协议,在实际网络的链路上传送数据帧时,最终必须使用硬件地址。所以需要一种方法来完成IP地址到MAC地址的映射,这就是地址解析协议。每台主机都设有一个ARP高速缓存,用来存放局域网上各主机和路由器的IP地址到MAC地址的映射表,称ARP表。

ARP工作在网络层,其工作原理如下:

1、主机A欲向本局域网上的某台主机B发送IP数据报,先在其ARP高速缓存中查看有无主机B的IP地址。如有,就可查出其对应的硬件地址,再将此硬件地址写入MAC帧,然后通过局域网将该MAC帧发往此硬件地址。如果没有,那么就通过使用目的MAC地址为FF-FF-FF-FF-FF-FF的帧来封装并广播ARP请求分组,使同一个局域网里的所欲主机收到ARP请求。

2、主机B收到该ARP请求后,向主机A发送响应ARP分组,分组中包含主机B的IP与MAC地址的映射关系,主机A在收到后将此映射写入ARP缓存,然后按查询到的硬件地址发送MAC帧。

ARP用于解决同一个局域网上的主机或路由器的IP地址和硬件地址的映射问题。如果所要找的主机和源主机不再同一局域网上,那么就要通过ARP找到一个位于本局域网上的某个路由器的硬件地址,然后将分组发送给这个路由器,让这个路由器把分组转发给下一个网络,剩下的工作就由下一个网络来做。

尽管ARP请求分组是广播发送的,但ARP响应分组是普通的单播。

动态主机配置协议(DHCP,Dynamic Host Configuration Protocol)

DHCP常用于给主机动态地分配IP地址,它提供了即插即用联网的机制,这种机制允许一台计算机加入新的网络和获取IP地址不用手工参与。DHCP是应用层协议,它是基于UDP的

其工作流程如下:

1、DHCP客户机广播“DHCP发现”消息,试图找到网络中的DHCP服务器。

2、DHCP服务器收到“DHCP发现”消息后,向网络中广播“DHCP提供”信息,其中包括提供DHCP客户机的IP地址和相关配置信息

3、DHCP客户机收到“DHCP提供”消息,如果接收DHCP服务器所提供的相关参数,那么通过广播“DHCP请求”消息向DHCP服务器请求提供IP地址

4、DHCP服务器广播“DHCP确认”消息,将IP地址分配给DHCP客户机

DHCP允许网络上配置多台DHCP服务器,当DHCP客户机发出DHCP请求时,有可能收到多个应答消息。这是,DHCP客户机只会挑选其中的一个,通常挑选最先到达的

网际控制报文协议(ICMP,Internet Control Message Protocol)

ICMP让主机或路由器报告差错和异常情况。ICMP报文作为IP层数据报文的数据,加上数据报的首部,组成IP数据报发送出去。ICMP是IP层协议

ICMP报文的种类有两种,即ICMP差错报告报文和ICMP询问报文

ICMP差错报告报文

ICMP差错报告报文用于目标主机或目标主机路径上的路由器向源主机报告差错和异常情况。共有5种类型:

  • 终点不可达:当路由器或主机不能交付数据报时,就向源点发送终点不可达报文。

  • 源点抑制:当路由器或主机由于拥塞而丢弃数据报时,就向源点发送源点抑制报文,使源点知道应当把数据报的发送速率放慢。

  • 时间超过:当路由器收到生存时间(TTL)为0的数据报时,丢弃该数据报外,还要向源点发送时间超过报文。当终点在预先规定的时间内不能收到一个数据报的全部数据报片时,就把已收到的数据报片都丢弃,并向源点发送参数问题报文

  • 改变路由(重定向)。路由器把改变路由报文发送给主机,让主机知道下次应将数据报发送给另外的路由器(可通过更好的路由)

不应当发送ICMP差错报告报文的几种情况:

1、对ICMP差错报告报文不再发送ICMP差错报告报文

2、对第一个分片的数据报片的所有后续数据报片都不发送ICMP差错报告报文

3、对具有组播地址的数据报都不发送ICMP差错报告报文

4、对具有特殊地址(如127.0.0.0和0.0.0.0)的数据报不发送ICMP差错报告报文

ICMP询问报文

ICMP询问报文有四种类型:回答请求和回答报文,时间戳请求和回答报文,掩码地址请求和回答报答,路由器询问和通告报文,最常用的是前两类。

PING使用了ICMP回答请求和回答报文。

PING工作在应用层,它直接使用网络层的ICMP,而未使用传输层的TCP或UDP。

IPv6

IPv6的特点

1、更大的地址空间,IPv6使用128位(16B)的IP地址。

2、扩展的层次结果

3、灵活的首部格式

4、改进的选项

5、允许协议继续扩充

6、支持即插即用

7、支持资源的预分配

8、IPv6只有在包的源节点才能分片,是端到端的,传输路径中的路由器不能分片,所以从一般意义上说,IPv6不允许分片(不允许类似IPv4在路由分片)

9、IPv6首部长度必须是8B的整数倍,而IPv4首部是4B的整数倍

10、增大了安全性。身份验证和保密功能是IPv6的关键特征

IPv6本身与IPv4不兼容

IPv6简化了IP分组头,它包含8个域(IPv4是12个域)

IPv6地址

IPv6在IPv4单播和多播的基础上又增加了一种类型——任播。任播的目的站是一组计算机,但数据报在交付时只交付给其中的一台计算机,通常是距离最近的一台计算机

IPv6标准中指定了一种比较紧凑的表示法,即把地址中的每4位用一个十六进制数表示,并用冒号分隔每16位,如4BF5:AA12:0216:FEBC:BA5F:039A:BE9A:2170

通常可以把IPv6地址缩写成更紧凑的形式。当每16位域的开头有一些0时,可以采用一种缩写表示法,但是域中必须至少有一个数字。例如,可以把地址4BF5:0000:0000:0000:BA5F:039A:000A:2176缩写成4BF5:0:0:0:BA5F:39A:A:2176

当有相继的0值域出现时,可以进步一缩写,这些域可以用双冒号缩写,但是双冒号表示法在一个地址中仅能出现一次。这样前面的地址就可以进一步缩写为4BF5::BA5F:39A:A:2176

IPv6拓展了IPv4地址的分级概念,它使用一下3个等级:第一级(顶级)指明全球都知道的公有拓扑;第二级(场点级)指明单个场点;第三级指明单个网络接口。IPv6地址采用多级体系主要是为了使路由器能够更快地查找路由

目前IPv4向IPv6过渡采用双协议栈和隧道技术两种策略:双协议栈是指在完全过渡到IPv6之前,使一部分主机(路由器)装有两个协议栈,即一个IPv4和IPv6,通过双协议栈进行转换;隧道技术是将整个IPv6数据报封装到IPv4数据报的数据部分

路由算法

静态路由与动态路由

静态路由算法(又称非自适应路由算法)。指由网络管理员手工配置的路由器信息,当网络的拓扑结构或链路的状态发生变化时,网络管理员需要手工去修改路由表中相关的静态路由信息。

动态路由算法(又称自适应性路由算法)。指路由器上的路由表项是通过相互连接的路由器之间彼此交换信息,然后按照一定的算法优化出来的。而这些路由信息会在一定时间间隙里不断更新,以适应不断变化的网络,以随时获得最优的寻路效果。

距离-向量路由算法

所有节点都定期地将它们的整个路由选择表传送给所有与之直接相邻的节点。这种路由选择表包含:

  • 每条路径的目的地(另一结点)

  • 路径的代价(也称距离)

这里的距离是一个抽象的概念,不一定是真正物理上的距离,可能是通过的路由器数。

RIP路由信息协议

RIP(Routing Information Protocol)路由信息协议是内部网关协议,是一种分布式的基于距离向量的路由选择协议。RIP是应用层协议,它使用UDP传送数据(端口520)

RIP规定

1、网络中的每个路由器都要维护从它自身到其他每个目的网络的距离记录

2、距离也称跳数(Hop Count),规定从一个路由器到直接连接网络的距离(跳数)为1。而每经过一个路由器,距离(跳数)增加1。

3、RIP认为好的路由就是它通过的路由器的数目少

4、RIP允许一条路径最多只能包含15个路由器。因此当距离等于16时,它表示网络不可达。距离向量路由可能出现环路的情况,规定最高跳数目的是为了防止数据报在环路上不断循环。

5、在任意两个使用RIP的路由器之间每30秒广播一次RIP路由更新信息。

6、在RIP中不支持子网掩码的RIP广播,所有RIP中每个网络的子网掩码必须相同。在新的RIP2中,支持变长子网掩码和CIDR。

RIP的特点

1、仅和相邻路由器交换信息

2、路由器交换的信息是当前路由器所知道的全部信息,即自己的路由表

3、按固定的时间间隔交换路由信息

4、如果180秒(RIP默认超时时间为180秒)还没有收到相邻路由器的更新路由表,那么把此相邻路由器记为不可达路由器,即把距离设置为16。

RIP的优缺点

RIP最大的优点是实现简单,开销小,收敛过程快。RIP的缺点如下:

1、RIP限制了网络的规模,它能使用的最大距离为15

2、路由器之间交换的是路由器中的完整路由表,因此网络规模越大,开销也越大。

3、网络出现故障时,会出现慢收敛的现象,即需要很长时间才能将此信息传送到所有路由器。俗称“坏消息传得慢”

链路状态路由算法

使用该算法的路由结点需要执行以下两项任务。第一,主动测试所有邻接结点的状态。第二,定期地将链路状态传播给所有其他结点,典型的链路状态算法是OSPF算法。

链路状态路由算法主要有三个特征:

1、向本自治系统中所有路由器发送信息,这里使用的方法是洪泛法,即路由器通过所有的端口向所有相邻的路由器发送消息。而每个相邻路由器又将此信息发往其所有相邻路由器(但不发送给刚刚发来信息的那个路由器)

2、发送的信息是与路由器相邻的所有路由器的链路状态,但这只是路由器所知道的部分信息

3、只有当链路状态发生变化时,路由器才向所有路由器发送此消息

链路状态报文的大小与网络中的路由结点数目无关,因此链路状态算法比距离-向量算法有更好的规模可扩展性

开放最短路径优先(OSPF)协议

开放最短路径优先协议是使用分布式链路状态路由算法的典型代表,也是内部网关协议(IGP)的一种。OSPF是网络层协议,它不使用UDP或TCP,而直接用IP数据报传送

OSPF特点

1、OSPF对不同的链路可根据IP分组的不同服务类型(TOS)而设置成不同的代价。

2、如果到同一个目的网络有多条相同代价的路径,那么可以将通信分配到这几条路径。这称为多路径间的负载均衡。

3、所有在OSPF路由器之间交换的分组都具有鉴别功能,因而保证了仅在可信赖的路由器之间交换链路状态信息。

4、支持可变长度的子网划分和CIDR

5、每个链路状态都带上一个32位的序号,序号越大,状态就越新。

OSPF的基本工作原理

各路由器之间频繁的交换链路状态信息,因此所有路由器最终都能建立一个全网的拓扑结构图。然后每个路由器根据这个全网拓扑结构图,使用Dijkstra算法计算从自己到各目的网络的最优路径。

为了使OSPF能够用于规模更大的网络,OSPF将一个自治系统再划分若干更小的范围,称为区域。划分区域的好处是将利用洪泛法交换链路状态信息的范围局限于每个区域而非整个自治系统。在一个区域内部的路由器只知道本区域的完整网络拓扑。这些区域也有层次之分,处在上层的域称为主干区域,负责连通其他下层的区域,并且还连接其他自治域。

OSPF的五种分组类型

1、问候分组,用来发现和维持临站的可达性

2、数据库描述分组,向临站给出自己的链路状态项目的摘要信息

3、链路状态请求分组,向对方请求发送某些链路状态项目

4、链路状态更新分组,用洪泛法对全网更新链路状态

5、链路状态确认分组,对链路更新分组的确认

通常每隔10秒,每两个相邻路由器要交换一次问候分组。

为了确保自己保存的链路状态与全网的状态保持一致,OSPF还规定每隔一段时间(如30分钟)就刷新一次数据库中的链路状态。

OSFP没有“坏消息传得慢”的问题

边界网关协议(BGP)

边界网关协议(Border Gateway Protocol,BGP)是不同自治系统的路由器之间交换路由信息的协议,是一种外部网关协议。

由于BGP使用的环境,BGP要选择最佳路由是不现实的。由于因特网的规模太大,使得自治系统之间路由选择非常困难。

BGP只能力求寻找一条能够到达目的网络且比较好的路由,而并非寻找一条最佳路由。BGP采用的是路径向量路由选择协议,BGP是应用层协议,它是基于TCP的

每个自治系统的管理员要选择至少一个路由器(可以有多个)作为该自治系统的“BGP发言人”。一个BGP发言人要与其他自治系统中BGP发言人要交换路由信息。每个BGP发言人除必须允许BGP外,还必须运行该AS所用的内部网关协议,如OSPF或RIP。

BGP所交换的网络可达性信息就是要到达某个网络(用网络前缀表示)所要经过的一系列AS。

BGP的特点

1、BGP交换的路由信息的结点数量级是自治系统的数量级,要比这些自治系统中的网络数少很多。

2、每个自治系统中BGP发言人(或边界路由器)的数目是很少的。这样就使得自治系统之间的路由选择不至于过分复杂。

3、BGP支持CIDR

4、在BGP刚运行时,BGP的邻站交换整个BGP路由表,但以后只需在发生变化时更新有变化的部分。

BGP-4使用的四种报文

1、打开(Open)报文。用来与相邻的另一个BGP发言人建立关系。

2、更新(Update)报文。用来发送某一路由的信息,以及列出要撤销的多条路由。

3、保活(Keepalive)报文。用来确认到开报文并周期性地证实邻站关系。

5、通知(Notification)报文。用来发送监测到的差错。

移动IP

移动IP技术是指移动结点以固定的网络IP地址实现跨越不同网段的漫游功能。并保证基于网络IP的网络权限在漫游过程中不发生任何改变。移动IP的目标是把分组自动地投递给移动结点。一个移动结点是把其连接点从一个网络或子网改变到另一个网络或子网的主机。

基于IPv4的移动IP定义三种功能实体:移动结点、归属代理(也称本地代理)和外部代理。归属代理和外地代理统称为移动代理。

  • 移动结点:具有固定网络IP地址的移动主机

  • 本地代理:一个移动结点的IP所属的网络被称为归属网络,在归属网络中代表移动结点执行移动管理功能的实体称为为本地代理。

  • 外部代理:在外部网络中帮助移动结点完成移动管理功能的实体成为外部代理。

注意移动IP和移动自组网络的区别,移动IP的核心网络功能仍然是基于固定互联网中一直使用的各种路由选择协议,移动自组网络是将移动性功能拓展到无线领域中的自治系统,它具有自己的独特的路由选择协议,并且可以不和因特网相联。

移动IP和动态IP的区别,动态IP指的是局域网中的计算机可以通过网络中的DHCP服务器动态地获得一个IP地址,而不需要用户在计算机的网络设置指定IP地址。

移动IP通信过程。

在移动IP中,每个移动结点都有一个唯一的本地地址。当移动结点移动时,它的本地地址是不变,在本地网络链路上每个本地结点还必须有一个本地代理来为它维护当前的位置信息。这就需要引入转交地址。当移动结点连接到外地网络链路上时,转交地址就用来标识移动结点现在所处的位置,以便进行路由选择。移动结点的本地地址与当前转交地址的联合成为移动绑定或简称绑定。

移动IP技术的基本通信流程如下:

  • 移动结点在本地网络时,按传统的TCP/IP方式进行通信

  • 移动结点漫游到一个外地网络中时,仍然使用固定的IP地址进行通信。为了能够收到通信对端发给它的IP分组,移动结点需要向本地代理注册当前的位置地址,这个地址就是转交地址(它可以是外部代理的地址或动态配置的一个地址)

  • 本地代理接收到来自转交地址的注册后,会构建一条通向转交地址的隧道,将截获的发送给移动结点的IP分组通过隧道送给转交地址处。

  • 在转交地址处解除隧道封装,恢复原始的IP分组,最后送到移动结点,这样移动结点在外网就能够收到这些发送给它的IP分组。

  • 移动结点在外网通过外网的路由器或外部代理向通信对端发送IP数据包

  • 移动结点来到另一个网络是,只需要向本地代理更新注册的转交地址

  • 移动结点回到本地网时,移动结点向本地代理注销转交地址

数据链路层的功能

数据链路层的主要作用是加强物理层传输的原始比特流的功能,将物理层提供的可能出错的物理连接改造为逻辑上无差错的数据链路

  • 为网络层提供服务
    • 无确认的无连接的服务,不需建立链路连接,不发回确认,不重发丢失帧而交由上层处理。适用于实时通讯或误码率较低的网络,如以太网。
    • 有确认的无连接服务,不需建立链路连接,发回确认,重发丢失帧
    • 有确认的面向连接的服务,建立连接,发回确认,重发丢失帧
    • 有连接就一定要确认,即不存在无确认的面向连接的服务
  • 链路管理
    • 数据链路层连接的建立、维持和释放过程称为链路管理
  • 帧定界、帧同步、透明传输
    • 将一段数据的前后分别加上首部和尾部,就构成了帧。首部和尾部中含有很多控制信息,它们的一个重要作用就是确定帧的界限,即帧定界
    • 帧同步指的是接收方应能从接收到的二进制比特流中区分出帧的起始和结尾
    • 透明传输指不管传输的数据是什么样的比特组合,都应能在链路上传送
  • 流量控制
    • 限制发送方的数据流量,使其发送速率不超过接收方的接收能力
  • 差错控制
    • 位错指帧中的某些位出现了错误,通常采用校验位发现位错,通过自动重传请求(Automatic Repeat reQuest,ARQ)方式来重传出错的帧

组帧

组帧是为了在出错时只重发出错的帧,而不必重发全部数据

字符计数法

在帧头部使用一个计数字段来标明帧内字符数。目的结点的数据链路层收到字节数值时,就知道后面跟随的字节数,从而可以确定帧结束的位置(计数字段提供的字节数包含自身所占的一个字节

字符填充的首位定界符法

字符填充法使用一些特定的字符来定界一帧的开始(DLE STX)与结束(DLE ETX)。为了使信息位中出现的特殊字符不被误判为帧的首尾定界符,可以在特殊字符前面填充一个转义字符(DLE)来加以区分(转义字符是ASCII码中的控制字符,是一个字符而不是“D”“L”“E”三个字符的组合)

比特填充的首位标志法

比特填充法允许数据帧包含任意个数的比特,也允许每个字符的编码包含任意个数的比特。它使用一个特定的比特模式,即01111110来标志一帧的开始和结束。为了不使信息位中出现的比特流01111110被误判为帧的首位标志,发送方的数据链路层在信息位中遇到5个连续的”1“时,将自动在其后插入一个“0”;而接收方每收到5个连续的”1“时,自动删除后面紧跟的”0“以恢复信息

违规编码法

在物理层进行比特编码时,通常采用违规编码。例如,曼切斯特编码中“高-高”电平对和“低-低”电平对在数据比特中是违规的(即没有采用),可以借用这些违规编码序列来定界帧的起始和终止。违规编码法不采用任何填充技术

目前较常用的组帧方法是比特填充法和违规编码法

差错控制

传输中的差错都是由噪声造成的,一类是信道所固定的、持续存在的随机热噪声;另一类是由外界特定的短暂原因所造成的冲击噪声,前者可以通过提高信噪比来减少或避免干扰,而后者不可能靠提高信号幅度来避免干扰造成的差错,是产生差错的重要原因。前者也叫随机差错,后者也叫突发差错。

差错控制主要有两类:自动重传请求(ARQ)和前向纠错(Forward Error Correction,FEC)。

在ARQ中,接收端检测出差错时,设法通知发送端重发,直到接收到正确的码字为止。

在FEC中,接收端不但能发现差错,而且能确定二进制数码的错误位置。

因此,差错控制可分为检错编码(Error-Detecting Code)和纠错编码(Error-Correcing Code)

纠错编码

对于码距大于等于2的数据校验码,开始具有纠错的能力。码距越大,检错、纠错的能力就越强,而且检错能力总是大于等于纠错能力

奇偶校验码

它的码距为2,可以检测出技术位错误,但不能确定出错的位置,也不能够检测出偶数位错误。

一位校验位,校验位取0或1使这个码字中“1”的个数为奇数(奇校验)或偶数(偶检验)

循环冗余校验(Cyclic Redundancy Check,CRC)

使用二进制多项式作为除数(多项式表示为二进制数时最高位和最低位必须为1

  1. 用除数对被除数最高位做模2减法(异或),不借位

  2. 除数右移一位,若余数最高位为1,商1,若余数最高位为0,商0,除数继续右移一位。

  3. 循环直到余数位数小于除数时,该余数为最终余数

循环冗余校验可以发现和纠正一位或多位错(与多项式的选取有关)

海明(汉明)码

设n为有效信息的位数,k为校验位的位数,n位和校验位k应满足${n + k \leqslant 2^k - 1}$

若要检测两位错误,则需要再增加1位校验位,即k+1位

它能发现双比特错,但只能纠正单比特错误

海明码纠错d位,需要码距为2d+1的编码方案;检错d位,则只需码距为d+1

流量控制与可靠传输机制

流量控制、可靠传输与滑动窗口协议

停止-等待流量控制基本原理

发送方每发送一帧,都要等待接收方的应答信号,之后才能发送下一帧

滑动窗口流量控制基本原理

发送窗口的大小$W_T$表示在还未收到对方确认信息的情况下发送方最多还可以发送多少个数据帧。

  1. 接收端收到数据帧之后,将窗口向前移一个位置,并发回确认帧,若收到的数据帧落在接收窗口之外,则一律丢弃。

  2. 发送端每收到一个确认帧,发送窗口就向前滑动一个帧的位置。

滑动窗口的重要特性

  1. 只有接收窗口向前移动(同时接收方发送了确认帧)时,发送窗口才有可能(只有发送方收到确认帧后才一定)向前滑动

  2. 接收窗口的大小为1时,才保证帧的有序接收

  3. 数据链路层的滑动窗口协议中,窗口的大小在传输过程中是固定的

  4. 发送窗口中的帧编号和接收窗口中的接收帧编号是一一对应的

  5. 窗口帧的编号是重复使用的,例如在2比特对帧编号的情况下,第一传输用了0、1、2这三个编号,下一次发送就使用3、0、1这几个编号。

可靠传输机制

数据链路层的可靠传输通常使用确认和超时重传两种机制来完成。

有些情况为了提高传输效率,将确认捎带在一个回复帧中,称为捎带确认

数据链路层中流量控制机制和可靠传输机制是交织在一起的

单帧滑动窗口与停止-等待协议

从滑动窗口机制的角度看,停止-等待协议相当于发送窗口和接收窗口大小均为1的滑动窗口协议

停等协议中,除数据帧丢失外,还可能出现以下两种差错:

  1. 到达目的站的帧可能已遭破坏

  2. 数据帧正确而确认帧被破坏,此时接收方已收到正确的数据帧,但发送方收不到确认帧

多帧滑动窗口与回退N帧协议(Go-back N,GBN)

从滑动窗口机制的角度看,停止-等待协议相当于发送窗口大小为n,接收窗口大小为1的滑动窗口协议,可以保证按序接收数据帧。

发送方可以连续发送帧,当接收方检测出失序的信息帧后或者某个帧未被接收方确认接收,则需要重发该帧及该帧之后的窗口中的所有帧。即接收方只允许按顺序接收帧。

源站每发送一帧就要为该帧设置超时计时器,GBN协议还规定接收端不一定每收到一个正确的数据帧就必须立即发回一个确认帧,而可以在连续收到好几个正确的数据帧后,才对最后的一个数据帧发确认消息,对某一数据帧的确认就表明数据帧和此前所有的数据帧均已正确无误的收到。$ACK_n$表示对n号帧的确认,表示接收方已正确收到第n号帧及以前的所有帧。

若采用n比特对帧编号,则其发送窗口的尺寸$W_T$应该满足${1\leqslant{W_T}\leqslant{2^n-1}}$

多帧滑动窗口与选择重传协议(Selective Repeat,SR)

从滑动窗口机制的角度看,停止-等待协议相当于发送窗口大小为n,接收窗口大小为m的滑动窗口协议,一般m=n>1

接收端每个发送缓冲区对应一个计时器,当计时器超时时,只用重发该帧。另外SR使用了更有效的差错处理策略,即一旦接收方怀疑帧出错,就会发一个否定帧NAK给发送方,要求发送方对NAK中指定的帧进行重传。

若使用n比特对帧编号,接收窗口$W_R$ + 发送窗口$W_T$ $\leqslant 2^n$,且接收窗口$W_R$不应该超过发送窗口$W_T$(否则无意义),即$W_R \leqslant 2^{n-1}$,接收窗口为最大值时,$W_Tmax = W_Rmax = 2^{n-1}$,一般情况下,在SR中,接收窗口的大小和发送窗口的大小是相同的

接收端需要使用缓冲区,暂存未按序正确收到的帧,接收端不能接受窗口外的帧,因此所需缓冲的数目等于窗口的大小,而不是序号数目。

信道利用率和信道吞吐率

信道的效率也称信道利用率,指发送方在一个发送周期的时间内,有效发送数据所需要的时间站整个发送周期的比率,可以为100%

信道吞吐率=信道利用率 $\times$ 发送发的发送速率

介质访问控制

介质访问的主要任务是,为使用介质的每个结点隔离来自同一信道上其它结点所传送的信号,以协调活动结点的传输。

信道划分介质访问控制(静态划分信道)

多路复用是实现信道划分介质访问控制的途径。多路复用技术可把多个信道的信息整合到一个复用的信道中,在接收端把收到的信息分离出来并传送到对应的输出信道

345d5a21edbfeb0013de3f03c9b0ee6d.jpeg

实际上就是把广播通道转变为点对点通道。

频分多路复用(FDM,Frequency Division Mutiplexing)

将多路基带信号调制到不同频率载波上,在叠加形成一个复合信道的多路复用技术。

每个子信道分配的带宽可不相同,但他们的总和必须不超过信道的总带宽。在实际应用中,为了防止子信道之间的干扰,相邻信道之间需要加入“保护频带”

时分多路复用(TDM,Time Division Mutiplexing)

将一条物理信道按时间分为若干时间片,轮流的分配给多个信号使用,每个时间片由复用的一个信号占用。

统计时分多路复用(STDM,Statistical Time Division Multiplexing,又称异步时分多路复用),它不固定分配时隙,而按需动态的分配时隙,当终端有数据传送时,才会分配到时间片。

波分多路复用(WDM,Wave Division Mutiplexing)

波分多路复用即光的频分多路复用,它在一根光纤中传输多种不同波长(频率)的光信号

码分多路复用(CDM,Code Division Multiplexing)

码分多路复用是采用不同的编码来区分各路原始信号的一种复用方式。它既共享信道的频率,又共享时间

码分多址(CDMA,Code Division Multiple Access,CDMA)是码分复用的一种方式。它将每个比特时间被分成m个更短的时间槽,称为“码片”,通常每比特有64或128个码片。每个站点分配唯一的m位代码或码片序列,站点发送1时,站点发送码片序列;发送0时,站点发送码片序列的反码。

定义向量的规格化内积:${S·T = \frac{1}{m}\sum_{i=0}^mS_iT_i}$,S,T为向量,${S_i}$ $T_i$表示向量的元素

例如A站和B站共享一条物理信道发送信息给C站,A站点的码片序列为00011011,则A站点发送00011011就表示发送比特1,发送11100100就表示发送比特0。将码片中的0写为-1,将1写为+1,A站的码片序列就是-1-1-1+1+1-1+1+1。

B站的码片序列需要和A站的码片序列相互正交(规格化内积为0,-1和1表示的情况下),所以在这个例子中可以选择B站的码片序列为-1-1+1-1+1+1+1-1(不止这一种)。当A站发送1(-1 1 -1 +1 +1 -1 +1 +1),B站发送0(+1 +1 -1 +1 -1 -1 -1 +1)时,两个信号在信道上叠加,即$A_1-B_0$得(0 0 -2 2 0 -2 0 -2)。数据发送到C站后,C站将收到的结果于A站的码片进行规格化内积,如果结果为1,表示A站发送的是1,如果结果是-1,表示结果为0。同理相获得B站的信息主要和B站的码片做规格化内积即可。

码分多路复用技术主要用于无线通信系统,特别是移动通信系统

随机访问介质访问控制(动态划分信道)

其核心思想是胜利者通过争用获得信道,从而获得信息的发送权。因此随机访问介质控制协议又称争用型协议

采用随机访问控制机制,各结点之间的通信就即不共享时间,也不共享空间。实质上也是将广播信道转化为点到点的通信。

纯ALOHA协议

当网络中的任何一个站点需要发送数据时,可以不进行任何检测就发送数据。如果在一段时间内未收到确认,那么站点就认为传输过程中发生了冲突。发送站点需要等待一段时间后再发送数据,直至发送成功。

纯ALOHA系统采用的重传策略是让各站等待一段随机的时间,然后进行重传。纯ALOHA网络的最大吞吐量为0.184

时隙ALOHA协议

它将所有各站在时间上同步起来,并将时间划分为一段段等长的时隙(Slot),规定只能在每个时隙开始才能发送一个帧。时隙ALOHA的最大吞吐量为0.368

CSMA(Carrier Sense Multiple Access,载波侦听多路访问)协议

1-坚持CSMA

一个结点要发送数据时,首先侦听信道,如果信道空闲,那么立即发送数据。如果信道忙,那么等待,同时继续侦听直至信道空闲。如果发生冲突,那么随机等待一段时间后,再重新开始侦听信道。

非坚持CSMA

如果信道空闲,那么立即发送数据;如果信道忙,则放弃监听,等待一个随机的时间后再重复上述过程。

非坚持CSMA降低了多个结点等待信道空闲后同时发送数据导致冲突的概率,但也会增加数据在网络中的平均延迟。

P-坚持CSMA

如果信道忙,那么等待下一个时隙在侦听,如果信道空闲,那么以概率p发送数据,以1-p的概率推迟到下一个时隙;如果在下一个时隙信道仍然空闲,那个仍然以概率p发送数据,以概率1-p推迟到下一个时隙。一直持续这个过程知道发送成功或者检测到信道忙,若是后者,则等待一个随机的时间后重新开始侦听。

三种CSMA协议比较

信道状态 1-坚持 非坚持 p-坚持
空闲 立即发送数据 立即发送数据 以概率p发送数据,以概率1-p推迟到下一个时隙
继续坚持侦听信道 放弃侦听,等待一个随机的时间后再侦听 持续侦听,等待下一个时隙再侦听
发生冲突 随机等待一段时间后再开始侦听 随机等待一个随机的时间后重新开始侦听

CSMA/CD(Carrier Sense Multiple Access With Collision Detection,载波侦听多路访问/碰撞检测)协议

其流程可概括为先听后发,边听边发(区别于CSMA协议),冲突停发,随机重发:

  1. 适配器侦听信道,如果信道空闲,则开始传输该帧,如果适配器侦听到信道忙,那么它将等待直至侦听到没有信号能量,然后开始传输该帧。

  2. 在传输过程中,适配器检测到其它适配器的信号能量,如果检测到来自其它适配器的信号能量并且自己没有传输完该帧,则停止该帧的传输,取而代之传输一个48比特的拥塞信号。

  3. 在传输拥塞信号后,适配器采用截断二进制指数退避算法等待一段时间,后返回步骤1。

CSMA/CD中的站点不能同时发送和接收,因此采用CSMA/CD协议的以太网不太可能进行全双工通信,而只能进行半双工通信。

把以太网端到端(最远两端)往返时间称为争用期(又称冲突窗口或碰撞窗口)2815a283496c61f654f8dfed86cabe0c.jpeg

CSMA/CA中帧的传输时延至少要两倍于信号在线路中的传播时延,所以CSMA/CD总线网中的所有数据帧都必须要大于一个最小帧长,任何站点收到帧长小于最小帧长的帧时,就把它当做无效帧立即丢弃。最小帧长 = 总线传播时延 $\times$ 数据传输率 $\times$ 2。

例如,以太网规定最小帧长为64B,如果前64B未发生冲突,那么后续数据也就不会发生冲突(表示以成功抢占信道)。如果数据小于64B,那么就需要在MAC子层中在数据字段后面加入一个整数字节的填充字段。以保证以太网的MAC帧的长度不小于64B。

二进制指数退避(动态退避)算法

  1. 确定基本退避时间,一般取两倍的争用期时间。

  2. 定义参数k,它等于重传次数,但k不超过10,即k=min[重传次数,10]

  3. 从离散的整数集合[0, 1, 2, 3, … , $2^k-1$]中随机取出一个数r,重传所需要退避的时间就是r倍的基本退避时间

4.当重传达16次仍不能成功时,认为此帧永远无法正确发出,抛弃此帧向高层报告出错

CSMA/CA(Carrier Sense Multiple Access With Collision Avoidance,载波侦听多路访问/碰撞避免)

碰撞避免并不是指协议可以完全避免碰撞,而是指协议的设计要尽量降低碰撞发生的概率。

CSMA/CA也采用二进制指数退避算法。即使信道从忙变为空闲时,任何一个站要发送数据帧时,不仅都须等待一个时间间隔,而且还要进入争用期窗口。并计算随机退避时间以便在此试图接入信道,而且仅当检测到信道空闲并且这个数据帧是要发送的第一数据帧时,才不使用退避算法

CSMA/CA还采用预约信道,ACK帧,RTS/CTS帧等三种机制来实现碰撞避免:

  1. 预约信道,发送方在发送数据的同时向其它站点通知自己传输数据需要的时间长度,以便让其它站点在这段时间内不发送数据,从而避免碰撞。

  2. ACK帧,所有站点在正确接收到发给自己的数据帧(除广播帧和组播帧)后,都需要向发送方发回一个ACK帧。发送方在规定时间内如果未收到ACK帧,那么认为发送失败,此时进行该数据帧的重发,直到收到ACK帧或达到规定重发次数为止。

  3. RTS/CTS帧(Request To Send/Clear To Send,请求发送/清除发送)。可选的碰撞避免机制,主要用于解决无线网络中的隐蔽站的问题。

CSMA/CD与CSMA/CA的区别

  1. CSMA/CD可以检测冲突,但无法避免;CSMA/CA发送包的同时不能检测信道上有无冲突(就是因为无法检测冲突,所以自己的站点不能判断一个帧是否发送成功,所以要使用ACK

  2. 传输介质不同,CSMA/CD用于总线以太网,CSMA/CA用于无线局域网

  3. 检测信道空闲的方法不同。

  4. 在本结点处有(无)冲突,并不意味着在在接收点处就有(无)冲突。(两种协议都是)

CSMA/CD与CSMA/CA总结

CSMA/CA协议的基本思想是在发送数据时先广播告知其他结点,让其他结点在某段时间内不要发送数据,以免出现碰撞。CSMA/CD协议的基本思想是发送前侦听,边发送边侦听,一旦出现碰撞马上停止发送。

轮询访问介质控制协议:令牌传递协议

在轮询访问中,通过一个集中控制的监控站,以循环方式轮询每个结点,在决定信道的分配。当某个结点使用信道时,其他结点都不能使用信道。典型的轮询访问控制协议是令牌传递协议,它主要用于令牌环形局域网中。

令牌传递协议中,一个令牌在各结点间以某个固定次序交换(在环上单向传输)。令牌是由一组特殊的比特组合而成的帧。当环上的一个站希望传送帧时,必须等待令牌。一旦收到令牌,站点便可启动发送帧。帧在环上传送时,不管该帧是否是发送给本站点的,所有站点都进行转发,直到该帧发回它的始发站,并由该始发站撤销该帧。帧的目的站除转发帧外,应针对该帧维持一个副本,通过在帧的尾部设置“相应比特”来指示已收到此副本。站点在发送完一帧后,应立即释放令牌,以便让其他站使用。

因为令牌只有一个,所以不会发生冲突

轮询介质访问控制非常适合负载很高的广播信道。因为对于负载很高的信道采用随机介质访问控制,发生冲突的概率会很大。

轮询介质访问既不共享时间,也不共享空间。

基本概念

数据、信号与码元

模拟信号和数字信号

  • 连续变化的数据(或信号)称为模拟数据(或模拟信号)

  • 取值仅为允许为有限的几个离散值的数据(或信号)称为数字数据(或数字信号)

串行传输和并行传输

  • 串行传输是指一个一个的比特按照时间顺序传递

  • 并行传输是指多个比特通过多条通信信道同时传输

码元是指用一个固定时长的信号波形(信号脉冲)表示一位k进制数,这个时长内的信号称为k进制码元,而该时长称为码元宽度一个码元可以携带多个比特信息

信源、信道与信宿

信源是产生和发送数据的源头,信宿是接收数据的终点

信道与电路并不相同,信道是信号的传输媒介,一个信道可视为一条线路的逻辑部件,多个用户共用通信电路时,每个用户在通信电路上都有一个信道

信道可分为模拟信道、数字信道;无线信道、有线信道

信道上传送的信号有基带信号和宽带信号之分:基带信号传送数字信号(称为基带传送);宽带信号传送模拟信号(称为宽带传输)

三种基本通信交互方式:

  • 单工通信。只有一个方向的通信而没有反方向的交互

  • 半双工通信。双方都能发送和接收信息,但是任意一方不能同时发送和接收信息,此时需要两条信道

  • 全双工通信。通信双方可以同时发送和接收信息,也需要两条信道

信道的极限容量是指信道的最高码元传输速率或信道的极限信息传输速率

速率,波特与带宽

1、码元传输速率,它表示单位时间内数字通信系统所传输的码元个数,单位是波特(Baud)。码元速率与进制速率无关

2、信息传输速率。又称信息率、比特率,它表示单位时间内数字通信系统传输的二进制码元个数(即比特数),单位是比特/秒(b/s)

若一个码元携带n比特的信息量,则M波特率的码元传输速率所应对的信息传输速率为Mn比特/秒

带宽原指信号具有的频带宽度,单位是Hz

奈奎斯特定律与香农定理

奈奎斯特(Nyquist)定理

它指出在理性低通(没有噪声、带宽有限)的信道中,极限码元速率为2W波特,其中W是理想低通信道的带宽,单位是Hz

若用V表示么个码元能表示的离散电平的数目,则极限数据率为$2W\log_2V$ 即2W $\times$ 一个码元携带的比特数

若传输速率超过此上限,就会出现严重的码间串扰问题

香农(shannon)定理

香农定理给出了带宽受限且有高斯白噪声干扰的信道的极限传输率。

信道的极限数据传输率=$W\log_2(1 + S/N)$

W为信道的带宽,S为信道所传输信号的平均功率,N为信道内部的高斯白噪声功率。S/N为信噪比。当信噪比使用分贝计量时,信噪比为$10\log_{10}S/N$,在香农定理中使用的信噪比计量为无单位的信噪比

编码与调制

把数据变为模拟信号的过程称为调制,把数据变换为数字信号的过程称为编码。

数字数据编码为数字信号

对于这种编码方式,具体用什么样的数字信号表示0及用什么样的数字信号表示1就是所谓的编码。

  • 非归零编码(non return to zero,NRZ)用两个电压来代表两个二进制数字,这种编码虽然容易实现,但是没有检错功能,也无法判断一个码元的开始和结束

  • 曼切斯特编码(Manchester Encoding),前一个间隔为高电平而后一个间隔为低电平表示码元1,码元0则相反。位中间的调变即作为时钟信号(可用于同步),但它所占的频带宽度是原始基带宽度的两倍。以太网使用的编码方式就是曼切斯特编码

  • 差分曼切斯特编码,常用于局域网的传输,若码元为1,则前半个码元的电平与上一码元的后半个码元的电平相同;若码元为0,则前半个码元的电平与上一码元的后半个码元的电平相反。每个码元中间有一个电平的跳转,可以实现自同步

  • 4B/5B编码,将欲发送数据流的每四位作为一组,然后按照4B/5B编码规则将其转换为相应的5位码,由于多一位码之后可以表示的信息变多了,将多出来的作为控制码

数字数据调制为模拟信号

数字数据调制技术在发送端将数字信号转换为模拟信号,而在接收端将模拟信号还原为数字信号。基本的调制方法有如下几种:

  • 幅移键控(amplitude shift keyed,ASK),通过改变载波信号的振幅来表示数字信号1和0

  • 频移键控(FSK),通过改变载波信号的频率来表示数字信号1和0

  • 相移键控(PSK),通过改变载波信号的相位来表示数字信号1和0,分为绝对调相和相对调相

  • 正交振幅调制(quadracture amplitude modulation,QAM)。在频率相同的前提下,将ASK与PSK结合起来,设波特率为B,采用m个相位,每个相位有n种振幅,则该QAM技术的数据传输率R为${\frac{B}{log_2mn}}$

模拟数据编码为数字信号

最典型的例子是对音频信号进行编码的脉码调制(pulse code modulation,PCM)(说是调制,其实本质是编码)。它主要分三个步骤,即采样、量化和编码

1、采样是指对模拟信号进行周期性扫描,把时间上连续的信号变成时间上离散的信号,采样频率必须大于等于原始信号中的最大频率的两倍,才能保证采样后的数字信号完整保留原始模拟信号的信息

2、量化是把采样取得的电平幅值按照一定的分级标度转化为对应的数字值并取整数

3、编码是把量化的结果转换为与之对应的二进制编码

模拟数据调制为模拟信号

电话机和本地交换机采用模拟信号传输模拟数据的方式

电路交换、报文交换、分组交换

电路交换

在进行数据传输前,两个结点必须先建立一条专用(双方独占)的物理通信路径,用户始终占用端到端的固定传输带宽

电路上的任何结点都采用“直通方式”接收数据和发送数据,即不会存在存储转发所耗费的时间

报文交换

报文交换在交换结点采用的是存储转发的传输方式。

发送前无需建立连接,动态分配线路。

一个报文可以同时发送给多个目的地址

报文交换对报文的大小没有限制

分组交换

分组交换也采用存储转发的方式,但是分组交换闲置了每次传送的数据块大小的限制,把大的数据块划分为合理的小数据块

分组交换比报文交换的传输时延少。

分组到达目的结点时,要对分组按编号进行排序等工作

数据报和虚电路

分组交换可进一步分为面向连接的虚电路方式无连接的数据报方式。这两种服务都由网络层提供。

数据报

虚电路

在分组发送前,要求在发送方和接收方建立一条逻辑上相连的虚电路并且连接一旦建立,就固定了虚电路所对应的物理路径。

建立虚电路时,选择一个未使用过的虚电路号分配给该虚电路以区别于本系统中的其它虚电路。

在虚电路网络中的每个结点上都维持一张虚电路表,它是在虚电路的建立过程中确定的。

数据的传输是双向的。

虚电路也存在存储交换(因为是分组交换的一种形式)、

虚电路的路由选择体现在连接建立阶段,连接建立后,就确定了传输路径。

虚电路提供了可靠的通信功能

分组首部并不包含目的地址,而包含虚电路标识符

注意:虚电路之所以是“虚“的,是因为这条电路并不是专用的,每个结点到其它结点之间的链路可能同时有若干虚电路通过,也可能同时与多个结点之间建立虚电路。

数据报服务和虚电路服务的标记

* 数据报服务 虚电路服务
连接的建立 不需要 必须有
目的地址 每个分组都有完整的目的地址 仅在建立连接阶段使用,之后每个分组使用长度较短的虚电路号
分组顺序 不保证分组的有序到达 保证分组的有序到达
可靠性 不保证可靠通信,可靠性由用户主机来保证 保证分组的有效到达
对网络故障的适应性 出故障的结点丢失分组,其他分组路径选择发生变化时可以正常传输 所有经过故障结点的虚电路不能正常工作
差错处理和流量控制 由用户主机进行流量控制,不保证数据报的可靠性 可由分组交换网负责,也可以由用户主机保证

计算机网络分层结构

把计算机网络的各层及其协议的集合称为网络的体系结构。这些功能究竟是用何种硬件或软件完成的则是一个遵循这种体系结构的实现问题,体系结构是抽象的,而实现是具体的,计算机网络的体系结构通常都具有可分层的特性。

分层的原则:

  • 每层都实现一种相对独立的功能,降低大系统的复杂度

  • 各层之间界面自然清晰、易于理解,相互交流尽可能少

  • 各层功能的精确定义独立于具体的实现方法

  • 保持下层对上层的独立性,上层单向使用下层提供的服务

  • 整个分层结构应能促进标准化工作

将分层后的网络从低层到高层依次称为第1层、第2层、……、第n层

第n层中的活动元素(硬件或软件进程,通常是一个特定的软件模块)

不同机器上的同一层称为对等层,同一层的实体称为对等实体

SDU、PCI、PDU

报文分为两个部分:数据部分:SDU,控制信息部分:PCI

  • SDU:服务数据单元,第n层的服务数据单元记为n-SDU

  • PCI:协议控制信息,第n层的协议控制信息记为n-PCI

  • PDU:协议数据单元,对等层之间传送的数据单元称为该层的PDU。第n层的协议数据单元记为n-PDU。

第n层的SDU和第n层的PCI共同构成第n层的PDU,第n层的PDU作为第n-1层的SDU

层次结构的含义

  • 第n层的实体向第n+1提供本层的服务,该服务是第n层及其下面各层提供的服务的总和
  • 上一层只能通过相邻层间的接口使用下一层的服务,下层实现细节对上一层透明
  • 对等层在逻辑上有一条直接信道(实际上并没有)

计算机网络协议、接口、服务的概念

协议

为进行网络中的数据交换而建立的规则、标准或约定称为网络协议,它是控制两个或多个对等实体进行通信的规则的集合,是水平的不对等实现是没有协议的

协议由语法、语义和同步三部分组成。语法规定了传输数据的格式;语义定义了所要完成的功能;同步规定了执行各种操作的条件、时序关系等。一个完整的协议通常应具有线路管理(建立、释放接口、差错控制、数据转化等功能)

接口

接口是同一结点内相邻两层间交换信息的连接点,不能夸层定义接口
在典型的接口上,同一结点的相邻两层的实体通过服务访问点(Service Access Point,SAP)进行交互。服务是通过SAP提供给上层使用的。SAP是一个抽象的概念,他实际上是一个逻辑接口

服务

服务指下层为紧邻的上层提供的功能调用,它是垂直的

OSI服务原语:
1、请求
2、指示
3、相应
4、证实

有应答的服务包括全部四类原语,而无应答服务则只有请求和指示两类原语

协议和服务的区别:
1、只有本层协议的实现才能保证向上一层提供服务
2、协议是“水平的”,服务是“垂直的”
3、并非在一层内完成的全部功能都称为服务,只有那些能够被高一层实体“看得见”的功能才称为服务

面向连接服务与无连接服务

在面向连接服务中,通信前双方必须建立连接,分配相应的资源(如缓冲区)、以保障通信能正常进行,传输结束后释放连接和所占用的资源。因此这种服务可以分为连接建立、数据传输、和连接释放三个阶段。

在无连接的服务中,通信双方不需要先建立连接,需要发送数据时可直接发送。这是一种不可靠的服务,也叫做“尽最大努力交付”,例如IP和UDP就是无连接服务的协议

可靠服务和不可靠服务

可靠服务是指网络具有纠错、检查、应答机制,能确保数据正确、可靠地送达目的地
不可靠服务是指网络只是尽量正确、可靠地传送,而不确保数据正确、可靠地传送到目的地,是一种尽力而为的服务(对于提供不可靠服务的网路,其网络的正确性、可靠性要由应用或用户来保障)

有应答服务和无应答服务

有应答服务指接收方在收到数据后向发送方给出相应的应答,该应答由传输系统内部自动实现,而不由用户实现

无应答服务指接收方收到数据后不自动给出应答,若需要应答,则由高层实现

ISO/OSI参考模型和TCP/IP模型

OSI(open system interconnection)参考模型(开放系统互连参考模型)

OSI自上而下依次为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层,第三层统称为通信子网、高三层称为资源子网。

物理层

物理层传输单位是比特,任务是透明的传输比特流

1、物理层规定了通信链路接口的参数
2、物理层也规定了通信链路上传输的信号的意义和电器特征、例如某种信号代表数字0,某种则代表1

传输信息所利用的一些物理媒体,例如光缆,并不在物理层协议之内而在物理层协议的下面

物理层的协议有EIA-232C、EIA/TIA RS-449、CCITT的X.21等

数据链路层

数据链路层的传输单位是帧,数据链路层的功能可以概括为成帧、差错控制、流量控制和传输管理以及物理寻址

链路层协议有SDLC、HDLC、PPP,STP等

网络层

网络层的传输单位是数据报,主要任务是把网络层的协议数据单元(分组)从源端传到目的端。关键问题是对分组进行路由选择,并实现流量控制、阻塞控制、差错控制和网际互联(网络寻址

网络层也称为网际层或者IP层

网络层协议有IP、IPX、ICMP等

传输层

传输层也称运输层,传输单位是报文段(TCP)或用户数据报(UDP),传输层负责主机中两个进程之间的通信,功能是为端到端连接提供可靠的传输服务,为端到端连接提供流量控制、差错控制、服务质量、数据传输管理等服务

传输层协议有TCP、UDP等

会话层

为表示层实体或用户进程建立连接并在连接上有序地传输数据,这就是会话,也称建立同步

会话层可以使用校验点使通信会话在通信失效时从校验点继续恢复通信,实现数据同步

表示层

表示层主要处理在两个通信系统中交换信息的表示方式。
为了使不同表示方法的数据和信息之间能够相互交换,表示层采用抽象的标准方法定义数据结构,并采用标准的编码形式。数据压缩、加密和解密也是表示层可提供的数据表示变换功能。

应用层

应用层为特定类型的网络应用提供访问OSI环境的手段。应用层采用不同的应用协议来解决不同类型的应用要求。

典型协议有FTP、SMTP、HTTP

TCP/IP模型

模型从低到高依次为网络接口层(对应OSI参考模型中的物理层和数据链路层)、网际层、传输层、应用层(对应OSI参考模型中的会话层、表示层和应用层)

网络接口层

TCP/IP本身并未真正描述这一部分,只是指出主机必须使用某种协议与网络连接,以便在其上传递IP分组

网络层

和OSI网络层在功能上非常相似,但他不保证各个分组有序地到达,各个分组的有序交付由高层负责

传输层

和OSI中的传输层类似,主要使用以下两种协议:
1、TCP(Transmission Control Protocol,传输控制协议),它是面向连接的,传输的单位是报文段,提供可靠的交付
2、UDP(User Datagram Protocol,用户数据报协议),无连接,传输的单位是用户数据报,不提供可靠的交付

应用层

TCP/IP模型与OSI参考模型的比较

OSI参考模型在网络层支持无连接和面向连接的通信,但在传输层仅有面向连接的通信。而TCP模型认为可靠性是端到端的问题,因此他在网际层仅有一种无连接的通信模式,但传输层支持无连接和面向连接两种模型

TCP/IP协议是实际执行的标准

物理层设备

中继器

中继器又称转发器,主要功能是将信号整形并放大再转发出去,以消除信号经过一长段电缆后,因噪声或其它原因造成的失真和衰减。其原理是信号再生(而非简单地将衰减的信号放大)

中继器没有存储转发功能,使用中继器连接的几个网段仍然是一个局域网,他不能连接两个具有不同速率的局域网,中继器两端的网段一定要使用同一个协议(物理层协议)

在10BASE5以太网规范中,规定互相串联的中继器的个数不能超过4个,而且用4个中继器串联的5段通信介质中只有3段可以挂接计算机

放大器和中继器都起放大作用,只不过放大器放大的是模拟信号,原理是将衰减的信号放大,中继器放大的是数字信号,原理是将衰减的信号整形再生

集线器

集线器(hub)实质上是一个多端口的中继器。一个端口接收到数据信号后,hub将其进行整形放大,紧接着转发到其它所有(除输入端口外)处于工作状态的端口,如果有同时有两个或多个端口输入,那么输出时会发生冲突,致使这些数据都无效,是一个标准的共享式设备

由集线器组成的网络是共享式网络,但逻辑上仍是一个总线网。hub的每个端口连接的网络部分是同一个网络的不同网段,同时hub也只能在半双工状态

基本信息

  • 考试方式:视频真人对话,老师时间15分钟

  • 分为三个part

part 时间长度 互动内容 考察能力 备注
part 1 4-5分钟、10道题左右 简介及问答:自我介绍及核对身份;日常问题(朋友、兴趣爱好、或者食物) 问答交流能力;日常交流的观点信息;常见的生活方式 屏幕上只有考官的脸,每道题需要回答30s左右,part1 的回答会给考官第一印象,需要非常重视
part 2 3-4分钟 个人陈述:答题任务卡,就一个观点进行阐述 语言运用能力:是否能恰当运用语言;是否能连贯组织自己的观点;需要联系自身经历完成 屏幕会显示题目,一分钟笔记,话题2分钟
part 3 4-5分钟 双向讨论:考官与考生就第二部分话题进行双向讨论 思考问题能力:表达论述看法、分析、讨论 每次回答45s到一分钟左右,考官问的问题的难度会根据part1+part2的回答情况定,如果part1+part2考官给了7分,那么考官问的问题也相应的会增加难度
  • 评分标准

    • 流利度及连贯性:回答时间、每一偶明显的停顿或中断、逻辑清晰、没有过于简单重复

    • 词汇多样性:灵活运用同义词、没有明显错词、地道表达

    • 语法范围和准确度:复杂结构语法、包含从句、简单句、允许少量错误

    • 发音:清晰标准(连接词)、语调、关键词、常用词和音准,口音不是关键

  • 口语考试有题库,1-4月一个题库、5-8月一个题库、9-12题库。题库组成:50%上季度题库+30%的三年内出过的题+20%新题

口语练习方法

  • 语音纠错法

    • 第一轮流利得回答出问题并录音,听录音的时候注意语调、是否有使用连接词、复杂、复杂句、词汇。第二轮语音有意识得加上第一轮缺失的部分
  • 跟读模仿练习

    • 利用听力材料继续跟读模仿练习,可以再精听完一道听力题后进行跟读模仿练习

    • 或者看英剧的时候进行跟读

英语思维

口语考试最重视英语思维,英语是一门比较实际的语音,英语思维关注的更多是what、when、where、why、how、who,所以在回答口语问题的时候也更加需要关注到这些点。

Part 1

part 1的一般用三到四句话来回答

回答的逻辑一般有以下几种:

  • 举例子:第一句话回答问题,第二句话给出原因,第三句话给出示例

  • 对比:

    • 在一种情况下会xxx,在另外情况下会xxx

    • 喜欢xxx,但是在xxx情况下也会去去做

    • 喜欢xxx,但是不喜欢xxx

part one一般的体型有以下几种:

  • 描述类(describe, what)

    • What subjects are you studying ?

    • What is your daily study routine ?

      • 时间

      • 地点

      • 内容

      • 好的句型:when it comes to xxx(时间), doing sth is my daily task

    • what do you always do when you stay up late ?

      • 好的句型:can’t help doing, mystery serials(悬疑剧), next episode
    • what made you happy when you were little ?

      • 考试重点:过去时
  • 喜好型(love & hate)

    • Do you prefer living a house or flat ?

      • house:一般有花园(backyard,garden),离市中心比较远

      • flat:离市中心比较近

    • Do you like your hometown ?

      • 地点:如果离北京、上海、香港近的话就说离这些地方多远的车程,如果离四川近的话,就是离the home of panda有多远的车程

      • 风景

      • 美食

    • Do you like science fiction movies ?

      • 好的短语:envision future、special effects 特效
    • Do you like the area that you live in ?

      • 环境:绿化、安静、安全

      • 交通便利

      • 周围有超市、餐厅

    • Do you like wearing T-Shirts ?

      • T-Shirt的面料:cotton

      • T-Shirt的分类:

        • tank top:背心

        • regular short sleeved:短袖背心

      • plain white T-Shirt(纯白T恤)

    • Do you like to go to the gallery ?

      • 好的表达:too abstract for me to appreciate
    • Do you like eating chocolate ?

      • 巧克力的分类:

        • dark chocolate:bitter

        • milk chocolate:smooth silky texture

      • 巧克力的作用:

        • ease my mind when I feel blue
    • Do you kie modern art or traditional art ?

      • 可以把相机的出现当作traditional art和modern art的分水岭

      • tradition art的主要目的是写实,代表人物是达芬奇、维米尔杨、梵高

      • modern art的创作原材料和风格更加丰富,颜色的使用也更加大胆,代表人物是毕加索

  • 分类(kinds & types)

    • What kind of jewelry do you like ?
      • 珠宝的种类:necklece,bracelet,earring,ring,tiara
      • 珠宝的材质:diamond,gold,silver,jade,pearl,ruby
      • 关于珠宝的形容词:sparkling,shiny,eye-catching,trendy,too extra(太夸张)
      • 好的表达:goes well with her daily outfit
    • What kind of music do you like ?
      • 音乐的分类:pop、jazz、blue、classic、rap、R&B
      • 和音乐相关的词:lyric、melody、rhythm
      • 关于音乐的形容词:fast pace
    • What kind of things are boring to you ?
      • 一般在等待的时候会感到无聊,例如等公车、排队等电梯(waiting in line for the elevator)、堵车(traffic jam)
    • What kind of TV programs do you like ?
      • TV programs分类
        • variety show(综艺)
          • reality show(真人秀)
          • talent show(选秀)
          • talk show(访谈综艺)
        • Drama(电影电视剧)
          • science fiction(科幻)
          • fantasy(魔幻)
          • situation comedy(情景喜剧)
        • documentary(纪录片)
  • 时间或频率型(When ? How often ?)

    • How often do you use social networking application ?
  • 观点类

    • Do you think kids are watching too much television ?
      • 回答是:因为现在越来越多的小孩在戴眼镜,一部分的原因就是看电视
      • 回答不是:因为现在小孩除了上课还要参加许多课外活动,培训班,没有时间看电视
    • Why do you think some people wear a piece of jewelry for a long time ?
      • 可能涉及到的词:family heirloom(传家宝)、wedding band(婚戒)
    • Do you think singing can bring happiness to people ?
      • 回答是:因为一般在一些庆祝的场景例如开party,婚礼(hold a wedding ceremony)
    • Do you think people are happy when buying new things ?
  • 假设类(假设类的重点是注意时态,No, I wouldn’t do; Yes, I would like to)

    • Would you ride bikes to work in the future ?

可以使用的副词

  • definitely

  • honstly

  • especially

  • basically

  • mostly

好的表达

  • Another thing also, which we really like, xxx

  • do A over B:做作B的期间,做A

  • Another reason is that

  • What I love the most is

part one 小套路

如果真的从来没有想过提出的问题,可以先用下面一些话拖点时间

  • well, to be honest, I don’t think I have ever thought about that, but I guess…

  • Actually, this is not something that I have ever considered, but in short, …

  • I’m not really sure how to put this, but I suppose….

部分题目答案参考

  • Do you think it’s good to use chocolate as gifts to others?

    • Yes, for sure. You’ll never go wrong when you choose to give chocolate as a gift. It’s probably one of the best and appropriate gifts you can bestow on someone. In my view, a nice chocolate bar or a box of delicious chocolates is something that can be enjoyed right away and shared with others.
  • Do you want to go into outer space in the future?

    • Yes, of course. I think it’s quite intriguing, as other life forms may exist. It’s fascinating to find out more about the other complex galaxies surrounding us. However, commercial space travel can be invisibly dangerous and I guess it takes time for scientists to develop more advanced technologies for it.
  • Do you think people are happy when buying new things?

    • Yes, definitely, I would say so. Material goods are still what most people are chasing for. That’s just the reality. You can see how excited people are when they have just bought a bit-ticket item such as a a new car, a house, or maybe just a luxury bag. People are so happy because they got what the have worked for, which is quite rewarding.

Part two

part two一般有以下几种题型

  • 人物题:describe a person who open travel by plane, a person you wanted to be similar to when you were growing up

  • 地点类:describe a place you remeber well that is full of colors / an ideal house

  • 事件经历:describe a time that someone didn’t tell you the whole truth about something/ a time when you were sleepy but had to stay awake

  • 物品类:describe an item you brought but do not often use / a leisure activity you do with your family

  • 其他问题:describe something that you do to stay healthy

part two一般需要讲两分钟,分为四个段落

  • 开头点题

  • how to know? when ? where?

  • why?

  • 总结feeling

人物题

做人物题时一般可以列下以下提纲:

  • Background Info (name, identification)

  • Apperence(hair, tall, skin, age)

  • Relationship (friend, teacher, family)

  • How to know (when, where)

  • Personality (easy going, optimistic)

  • What kind of person (hobby, lifestyle, achievement)

部分题目参考答案

Describe an actor or actress whom you admire

you should say:

  • who the person is

  • what he/she looks like

  • what kind of movies he/she appears in

  • explain how you feel about this person

Well, being a movie maniac, there are a verity of actors or actress that I am fond of. I have long idolized the holywood legend - Leonardo DiCaprio. I first got curious about him when I was reading some news on social media.

Leonardo became popular since he is a versatile artist, who established himself as a romantic hero with his outstanding performance. He is famous for his extraordinary role in the movie Titanic, which brought him fame across the world.

As for the reason why I admire him, his career path gives me a lot of inspiration. In fact the beginning of his career was not smooth. He experienced numerous ups and downs in his career, but he kept patience and continued to work with hard effort. Some of his performance are epic and still amuse thi viewers. But the most important thing I have found in him is courage. He is brave enough to take some giant moves even after maintaining a low profile for long days in the movie industry. Now he is one of the highest-paid artists of Hollywood.

Anyway, I really adore him and if I could meet him, I would like to know more about him.

Describe another city you would like to stay for a short time

Speaking of a place where I would like to stay for a while. I want to tell you about Sydney in Australia, which I believe is definety one of the most attractive cites in the world.

As for what I will do there, well, Sydney is a fascinating and an energetic city with amazing historical sites, unique animals and diverse culture. I will be impressed by the remarkable architectures such as the Opera House, the harbor bridge and the Queen victoria Building. If I can visit the Taronga Zoo, there are some unique animals like Kangaroo, Koala and Emu, which are distintive in Australia. Also, it’s a city famous for diverse culture because it’s one of the most livable city in the world with lots of immigrants. I can probably see people from different countries on the street. It’s gonna be relaxing and enjoyable for me to walk around in the city, you know.

Acutally, when I was a child, I learned this place on my textbook and I have looking forward to visit one day. In fact, my family and I planned to go, like 3 years ago. I’d already booked plane tickets and made hotel reservations. However, because of the Covid-19 pandemic, my whole plan just went down to the drain. Luckily, everything is getting better this year. It’s like a dream come true for me if I got a chance to visit. I’m literally getting goose bump right now.

Speaking of the reason why I won’t stay there for a long time, being homesick would be the main factor. All my friends and family mebers are here in China, and I don’t want to live far away from them. May be a short trip occasionally is good enough for me to get to know this place better.

口语俚语总结

  • For the life of me: 无论如何

  • Went down to the drain: 白费了,泡汤了

  • Goose bump: 鸡皮疙瘩

基本信息

听力第一门考,总共30分钟、40道题、4个section,难度随着section依次递增。如果参加的是笔试,听力考试完成后有额外的10分钟进行答题卡的填写,如果参加的是机考,则没有时间填写答题卡。

题型分布

  • section 1: 填空(个人信息)

  • section 2+section 3: 填空、地图、选择、搭配

  • section 4:填空(学术独白)

学习方法 (需打卡)

  • 听写-《语料库》

    • 总共10章,重点章节三、四、五章

    • 先跟读,纠正发音

    • 背生词

    • 听写(不暂停)

    • 检测一遍

    • 错词抄在错词本上

    • 正确率95%以上,再开始下一个test paper

    • 不用重复听写

  • 精听

    • 放完一句话后,暂停,口头复述大意并写下来(使用英语)

    • 看原文,查生词,纠正发音,注意连读

    • 每周精听2-3个section

section 1

  • 注意住址相关的题的答案都是和山水、风景、自然相关的词,例如:xxx hill road

2022年马上就要就离我们而去,和以往的年份一样,这一年世界发生了独属于它的大事件:冬奥会、俄乌战争、英女王离世、世界杯、东航空难、国内互联网大裁员、中国放开新冠疫情管控……,尽管其中的大多数算不上“好事”,但不论是好事坏事,都是时间在2022年留下的独特印记。同样,时间在2022年的我身上也印下了一些独属于这一年的印记:第一次升职、开始独居生活、体重突破120斤、开启第一个side project,确定了下一个人生阶段的计划,以及第一次面对亲人的离世。

练习时长两年半打工人

从2020年6月24号正式踏入职场开始,到今年年底差不多就是两年半的时间。本来今年春节的时候就打算待满两年,到6月份就离职,但是由于种种原因,还是留了下来。这些种种原因包括在今年5月刚好获得一次升职加薪,同时在那个时候还没有想好自己对于下一个阶段的规划,再加上面对互联网公司大裁员的大环境也让我不敢轻举妄动。

我喜欢写代码,但是在这打工的两年半时间里我写的这些代码却很少给我带来过幸福感和成就感。尤其是今年感觉更加明显,如果说之前产生这种感受是因为自己写的代码,最终的产出的应用实际服务的用户很少甚至一段时间后都没有再去用,那么今年即使自己负责的一些特效服务也在抖音中产生过爆款,甚至在朋友圈也看到过有好友使用自己负责的特效服务,工作内容转向tob后,参与的产品也和外部客户签了许多订单,但老实说,这些都没有给我一点兴奋的感觉。一方面是自己对公司的大多数产品无感(目前唯一长期使用的公司的产品就只有办公软件飞书了,抖音也只在21年公司9周年庆的答题活动上用过一次),更多的是对自己写的代码,所创造的产品没有产生太多认同感——虽然对于要创造这款产品所需要的技术还是保持兴趣,但是产品本身却没办法让我产生去写代码的动力,没有办法让我产生想去把这个产品做得更好的意愿。大多数时候自己是不得不去做这件产品,而不是真心的认为这个产品值得去做,认为这个产品可以帮助很多人。尤其是和我写自己的side project时的感觉对比,这种被迫的感觉更加明显。现在支撑我去写业务代码的也就只剩那还算可观的工资了吧。

现在我对于职业的选择的想法就是:尽量还是选择一份可以获得价值认同的职业,如果暂时还找不到,就尽量选一份钱多的,而且最好是多得能让你暂时忽略掉个人价值实现的。两者兼顾当然更好,这也是我目前对未来职业选择的追求,如果两方面都不能满足,还是尽快跳槽为好。

现在对于下一步的规划也想好了,明年预期离职的时间也大致确定了,接下来的问题就是一边打工的同时,如何找时间摸鱼和做好去下一个地方的准备。

我独自生活

从今年7月开始,和一起来深圳搬砖的两个大学同学,也是之前一起租房的小伙伴散伙后,正式开启了独居生活。新租的房子是一个公寓式的大单间,怎么说呢,除了冬冷夏热,房租略贵外,其他方面都还算不错。面积还算比较大,差不多30平;周围很安静,可能最吵的就是每天晚上我练琴发出的据木头声音;离附近的地铁站和商场也不远,去打球的话骑自行车也10分钟就到了。总体来说对当前独居住所的“硬件”配置还是比较满意的。

一个人住最大的感受就是自由。之前和小伙伴一起租房的时候,虽然大家都比较熟,关系也算不错,但是总是会由于一些人情世故、出于礼貌或者由于隐私的原因,而不能去做一些自己想做的事。一个人住就完全没有这些顾虑,我想在房间里放啥就放啥,想什么时候洗衣服就什么时候洗衣服,想什么时候用卫生间就什么时候用,这片空间完全是属于我的。虽然相比于合租,一个人住确实感到孤独的时刻会多一些,但是对于独居带给我的更多的自由和安静来说,我觉得这一点点代价是值得的。

一个人住免不了自己做饭。刚开始独居的时候还对自己的厨艺有一定的幻想,买了好多厨具,想象着自己经过一年做饭的练习能够熟练掌握处理各种食材,蒸炒炸煮手到擒来。结果半年了,到现在只处理过鸡蛋、猪肉、青椒、土豆、豆腐这5种食材,做过的菜不超过6种,做的饭更多是能吃、下饭,算不上什么美味。更多的时候还是靠外卖,但是如果实在不想吃外卖了,自己做饭还是一个很好的解决方案。暂时对厨艺不抱任何幻想了,别食物中毒就行。

突破120,目标140

经过两年多的增重实践,到今年终于有了明显的效果。从年初的不到110斤,到年底的120斤(最重的时候到了125斤)。回想起来,虽然从高中就开始了一些增重的尝试,但是直到正式开始工作后,才开始定期去健身房进行持续的增重训练。去健身房的第一年,虽然自己的力量和耐力都有一定的增长,但是体重却没有太多变化,一直在105斤左右的来回波动。当时一直很焦虑,投入了时间和精力,却几乎没有什么效果。到了去年下半年,体重才开始有了一点点的变化,从105斤到了110斤,但是相对于自己在健身房里做出的付出来说,这点效果好像也不那么振奋人心,而且到了110斤之后体重又好长时间一直维持在这个水平。直到今年,不知道为什么,体重开始逐步稳定增长,肌肉力量也在逐步增加,感觉身体终于开始对我的坚持有了反馈了。

weight

突破120斤之后,整个人虽然体型看起来没有那么明显的改变,但是相比于以前100多斤的时候还是要好不少的,至少不会看起来像一个“吸毒的”了。现在的体型虽然还不是我的理想的体型,但是已经算一个我不会讨厌的体型了。125斤对我来说好像又是一个瓶颈,但是有了以前痛苦的增重经历,也知道怎么自然地去面对这个瓶颈了。

慢慢养成的阅读习惯

我一直不是一个爱好阅读的人,从小到大,语文永远都是拖我后腿的学科。高中三年的课外阅读量加起来应该也只有个位数,大学阅读的书除了专业相关的书之外,很少会去读一些其他方面的书。今年相对于其他年来时,阅读量有了那么一点进步,虽然也不算多,但是对于我来说也算是一个小小的突破。

books

本来一开始读书的原因是我发现睡前阅读十几分钟可以让我更快地入睡,没想到就这样慢慢培养起来了阅读的习惯。现在不仅睡前,想放松的时候就会去看看书。我很喜欢现在阅读的状态,没有任何预设的读书目标,想读就读,不想读就不读,想读什么类型的书就读什么类型的书。慢慢用阅读代替漫无目的地刷B站、油管后,发现自己会有更多的时间处于一种心流状态,一天下来心理的波动也会少那么一些。

对于明年,也不想设啥阅读要超过多少多少本的目标,还是保持这样的阅读状态就好,希望阅读成为一个不会给我带来任何负担的新爱好。

关于爷爷的去世

如果说2022带给了我什么人生转折,那就是从这一年开始我再也见不到我的爷爷了。本来我以来亲人去世这件事还离我很远,毕竟爷爷奶奶、外公外婆虽然都快80了,但是身体却一直都很好,从来没有得过什么大病。可是12月的时候,在毫无任何征兆的情况下,得知爷爷去世的消息一时让我有点无法接受。爷爷走得很突然,下午还在老家的院子里散步遛狗,身体没有任何异常,到了晚上突然就开始咳,然后在不到两个小时的时间里就离开了人世。

从现在能回想起的关于我的最早的记忆开始,就有爷爷的身影。大概是我3、4岁的时候,那时爷爷还没有退休,在老家的小学教书,我和我堂哥就跑到爷爷的学校里玩。之后爷爷退休了,我也到城里去读小学了。小学一二年级的时候都是和爷爷奶奶一起住在我堂哥家,还记得一年级的时候,爷爷带着我爬山从堂哥家去老城区上学,放学后一起爬山回家,当时本来放学就早,加上作业不多,小时候又多动,从来没有觉得爬山有多累,反而觉得很好玩。刚开始来城里读书的时候比较不适应,学习成绩不太好,那时候老师好像也不太喜欢我,所以我有问题也不太愿意找老师。爷爷是数学老师,那时我的数学基本上都是爷爷来辅导的,所以基本上从小开始我的数学成绩就比语文成绩好很多。爷爷性格很温柔,从来没有见过爷爷生气的样子,给我讲题也很耐心,如果我实在听不懂,还会想更简单,我更容易理解的其他解题思路然后讲给我听。小学三年级之后,虽然没有和爷爷奶奶一起住了,但是有时中午家里没人,还是会去堂哥家和爷爷奶奶一起吃饭,如果有数学作业不会做也会去问爷爷。小学期间的暑假也基本上都是在老家和爷爷奶奶一起度过的,晚上和爷爷奶奶一起在老家房顶坐在凉板上乘凉的场景到现在还清晰记得。

小学毕业和爷爷奶奶一起去九寨沟玩

和爷爷一起第一次坐动车

爷爷1943年生,是村里的第一个大学生,去了师范大学学了数学专业,如果不是出生在偏僻的农村,爷爷可能会去更好的大学。大学毕业后爷爷辗转在县里的各个镇的中学,教中学数学,后来终于回到村附近的中学教书,在这里把我爸的哥哥和我爸都送进了县里示范学校。爷爷是个教师,也是一个朴素的农民,那时爷爷白天除了要教书外,还要和乡里的生产队一起干农活——挖土、挑粪、淋菜、……。爷爷就这样一直教书、种田直到退休。

爷爷在照片的左上角

爷爷的个人照

爷爷的一生虽然平淡,但是我觉得也是非常有意义的一生了。爷爷走得没有太多痛苦,这也是关于爷爷的去世这件事唯一的安慰了。

最后

2022年对于我来说可以算是life change的一年了,过完了第二个本命年,离30岁也不算太远了。这一年感觉很累,总是感觉要做的事太多而时间太少,2023年的主题预计也还是“忙”,也希望2023年和2022年一样,没有白忙活一场。