前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【译】Graphql, gRPC和端对端类型检验

【译】Graphql, gRPC和端对端类型检验

作者头像
腾讯IVWEB团队
发布于 2020-06-28 07:16:39
发布于 2020-06-28 07:16:39
3.2K00
代码可运行
举报
运行总次数:0
代码可运行

原文地址:Graphql, gRPC, and End-to-End Type Coverage 作者:Kaitlyn Barnard 注:本文采用意译

背景介绍

StackPath最近发布了新的门户网站,它让用户可以一站式地配置我们所提供的服务(CDN,WAF, DNS以及Monitoring)。这个项目涉及到整合不同的数据源,以及一些现有和全新的系统。虽然我们认为开发效率的优先级在一个新启动的项目中是最高的,但我们还是希望在保证足够快的开发进度的前提下,尽可能早地做一些能够保证产品长期稳定运行的技术投资,以便我们能够持续不断地在一个健壮的基础设施上添加新的功能特性。最终我们选择了Apollo GraphQL+gRPC+React+TypeScript这样一套技术栈,并对使用它们的结果感到满意。在这篇博客中,我们会解释为何选择这些技术栈,并通过一个简单的示例项目进行论述。

GraphQL

当听到我们需要在这个项目中“整合许多不同的数据源”时,我立即意识到使用GraphQL作为API网关会是一个不错的选择。

我们并不是为了故意揭REST API的短,而是基于我们自己的React应用来看使用GraphQL的主要优势在于:

  • 对前端屏蔽了后端技术的复杂性,让那些后端服务更好地保持了它们的原子性。
  • 由于GraphQL查询可以一次请求多种资源,因此可以减少网络请求的次数。
  • 未来可以很方便地增加服务端接口。只需要在我们的schema中增加查询(queries)和变更(mutations)字段,就可以在应用中使用(consuming)这些数据了。
  • 使用apollo-client和react-apollo简化了前端代码对缓存和数据的管理。
  • 灵活的查询方式方便我们在未来构建移动端以及内部应用。
  • GraphQL schema的自检性让我们可以方便的查询系统中的全部可用数据。

(如果你想更深入的学习GraphQL,我推荐你去看看官方指引)

我们的GraphQL服务主要是干数据透传的活儿。我们所有的解析器(resolvers)都是遵循以下模式:从后端服务请求一些数据,可能会做轻量的数据转化工作,使得返回的数据复合我们的schema。在这些解析器中几乎木有业务逻辑。

结果,静态类型很好的保证了服务端响应和数据转化逻辑能够匹配我们的schema。由于GraphQL schema本身就是一种类型集合,可以根据它很方便地生成TypeScript类型。我们使用graphql-code-generator基于我们的schema来生成对应的Typescript typings, 并且在写解析器的时候使用这些Typescript typings。

GraphQL示例

我们的示例应用会是一个标准的TODO MVC,支持列表展示、创建和删除TODO事项。这三个操作对应的GraphQL schema如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const typeDefs = gql`
 type Query {
   todos: [Todo!]
 }

 type Mutation {
   createTodo(input: CreateTodoInput!): CreateTodoPayload
   deleteTodo(input: DeleteTodoInput!): DeleteTodoPayload
 }

 type Todo {
   id: String!
   title: String
 }

 input CreateTodoInput {
   title: String!
 }

 type CreateTodoPayload {
   todo: Todo
 }

 input DeleteTodoInput {
   id: ID!
 }

 type DeleteTodoPayload {
   success: Boolean
 }
`;

我们的package.json的一些可执行脚本定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 "scripts": {
   "start": "ts-node src/index.ts",
   "genTypes": "graphql get-schema && gql-gen --schema schema.json --template graphql-codegen-typescript-template --out ./src/types.ts"
 },

通过这些schema,我们可以使用yarn genTypes来生成types.ts文件中的types,并在实现解析器时使用它们。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const resolvers: { [key: string]: any } = {
 Query: {
   todos: (): Promise<Array<Todo>> => {
     // Get Todos
   }
 },
 Mutation: {
   createTodo: (
     _obj: object,
     args: CreateTodoMutationArgs
   ): Promise<CreateTodoPayload> => {
    // Create Todo
   },

   deleteTodo: (
     _obj: object,
     args: DeleteTodoMutationArgs
   ): Promise<DeleteTodoPayload> => {
     // Delete Todo
   }
 }
};

每个解析器的主体部分涉及到请求我们的后端服务,我们将在下一个示例中去实现。

gRPC

一开始,我们本来打算使用REST API来集成我们的后端服务。然而我们的后端团队已经使用了gRPC来标准化后端服务之间的通信方式。

(可能有些人还不熟悉这项技术,gRPC是Google基于HTTP/2和protocol buffers开源地一个远程方法调用(RPC)框架。在gPPC中,.proto文件用来描述后端服务的可调用方法名,以及这些方法输入输出的字段类型。通过这些proto文件,protoc(protocol buffer编译器)可以同时生成客户端/服务端的请求/响应代码。你可以在这里阅读到更多有关于gRPC的内容)

通过grpc-gateway我们依然可以选择使用REST API来暴露接口,但是我们依然想通过使用gRPC来探索它能给我们带来什么好处。下面是我们所体会到的gRPC的主要优势:

  • 生成对应我们全部后端服务接口的客户端类型代码是一件灰常简单的事情,我们使用这个插件来生成TypeScript definitions。
  • 通过使用gRPC能够让后端和前端团队之间更加方便的分享知识和互通有无(译注:不太懂这里的意思,难道是指两端的团队共同学习HTTP2和proto buffer?)。

使用类型化的客户端代码是一件令人愉快的事情。每一个服务端所对应的客户端代码都是基于后端接口的请求和响应信息来进行类型化。我们不需要再去查询每个API的接口文档,因为客户端代码里拥有开发者和IDE所需要知道的全部信息。并且我们知道它们一定是正确的,因为它们是基于proto文件自动生成的。

gRPC示例

我们会像在GraphQL schema中所做的那样在proto文件中定义相同的三种操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
syntax = "proto3";

service TodoManager {
 rpc CreateTodo(CreateTodoRequest) returns (CreateTodoResponse) {}

 rpc GetTodos(GetTodosRequest) returns (GetTodosResponse) {}

 rpc DeleteTodo(DeleteTodoRequest) returns (DeleteTodoResponse) {}
}

message CreateTodoRequest {
 string title = 1;
}

message CreateTodoResponse {
 Todo todo = 1;
}

message GetTodosRequest {}

// GetStacksRequest returns stacks
message GetTodosResponse {
 repeated Todo results = 2;
}

message DeleteTodoRequest {
 string todo_id = 1;
}

message DeleteTodoResponse {
 bool success = 1;
}

message Todo {
 string id = 1;
 string title = 2;
}

基于这个todo.proto文件,我们使用protoc来生成客户端代码和服务端的骨架代码(只需要往里面填充每个方法的具体实现就可以了)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/sh

PROTOC_PLUGIN="node_modules/.bin/grpc_tools_node_protoc_plugin"
OUT_DIR="."

./node_modules/.bin/grpc_tools_node_protoc \
 --js_out="import_style=commonjs,binary:${OUT_DIR}" \
 --grpc_out="${OUT_DIR}" \
 --plugin="protoc-gen-grpc=${PROTOC_PLUGIN}" \
 src/todo.proto

protoc \
 --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
 --ts_out="${OUT_DIR}" \
 src/todo.proto

运行这个脚本会生成下面四个文件:

  1. todo_grpc_pb.d.ts
  2. todo_grpc_pb.js
  3. todo_pb.d.ts
  4. todo_pb.js

todo_pb.js 包含了请求体对象(message objects),todo_grpc_pb.js包含了服务端/客户端对象(service/client object)。.d.ts文件是以上每个对象的TypeScript definitions。我们服务端代码的简易实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { TodoManagerService } from "./todo_grpc_pb";
import {
 CreateTodoRequest,
 CreateTodoResponse,
 GetTodosRequest,
 GetTodosResponse,
 DeleteTodoRequest,
 DeleteTodoResponse,
 Todo
} from "./todo_pb";
import grpc, { ServerUnaryCall, sendUnaryData } from "grpc";

let id = 1;
let todos: Array<Todo> = [];

const createTodo = (
 call: ServerUnaryCall<CreateTodoRequest>,
 callback: sendUnaryData<CreateTodoResponse>
) => {
 const title = call.request.getTitle();
 const todo = new Todo();
 todo.setId(String(id++));
 todo.setTitle(title);
 todos = [...todos, todo];

 const response = new CreateTodoResponse();
 response.setTodo(todo);
 callback(null, response);
};

const getTodos = (
 call: ServerUnaryCall<GetTodosRequest>,
 callback: sendUnaryData<GetTodosResponse>
) => {
 const response = new GetTodosResponse();
 response.setResultsList(todos);

 callback(null, response);
};

const deleteTodo = (
 call: ServerUnaryCall<DeleteTodoRequest>,
 callback: sendUnaryData<DeleteTodoResponse>
) => {
 const id = call.request.getTodoId();
 todos = todos.filter(item => item.getId() !== id);
 const response = new DeleteTodoResponse();
 response.setSuccess(true);

 callback(null, response);
};

function main() {
 const server = new grpc.Server();
 server.addService(TodoManagerService, { createTodo, getTodos, deleteTodo });
 server.bind("0.0.0.0:50051", grpc.ServerCredentials.createInsecure());
 server.start();
}

main();

说回我们的GraphQL server,我们现在可以导入gRPC的客户端模块,并完善我们的解析器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import {
 GetTodosRequest,
 CreateTodoRequest,
 DeleteTodoRequest
} from "../../grpc/src/todo_pb";
import { createClient } from "../../grpc/src/client";

const todoClient = createClient();

const resolvers: { [key: string]: any } = {
 Query: {
   todos: (): Promise<Array<Todo>> => {
     const request = new GetTodosRequest();
     return new Promise((resolve, reject) => {
       todoClient.getTodos(request, (error, response) => {
         if (error) {
           reject(error);
         }
         return resolve(response.toObject().resultsList);
       });
     });
   }
 },
 Mutation: {
   createTodo: (
     _obj: object,
     args: CreateTodoMutationArgs
   ): Promise<CreateTodoPayload> => {
     const request = new CreateTodoRequest();
     request.setTitle(args.input.title);
     return new Promise((resolve, reject) => {
       todoClient.createTodo(request, (error, response) => {
         if (error) {
           reject(error);
         }
         return resolve(response.toObject());
       });
     });
   },

   deleteTodo: (
     _obj: object,
     args: DeleteTodoMutationArgs
   ): Promise<DeleteTodoPayload> => {
     const request = new DeleteTodoRequest();
     request.setTodoId(args.input.id);
     return new Promise((resolve, reject) => {
       todoClient.deleteTodo(request, (error, response) => {
         if (error) {
           reject(error);
         }
         return resolve(response.toObject());
       });
     });
   }
 }
};

现在我们拥有了完整的GraphQL server,它可以使用gRPC来与后端服务进行通信。

React

我们并没有花费太多时间来讨论这一选择。我们团队的主要经验都是在构建React应用上,而且我们也没有找到任何令人信服的理由来换到别的选项上。为了保证GraphQL server和前端之间的类型安全,我们使用Apollo CLI的代码生成器:使用命令行来生成我们所有GraphQL查询的类型:

React示例

在我们的应用中需要用到三种查询:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
query GetTodos {
 todos {
   id
   title
 }
}

mutation CreateTodo($input: CreateTodoInput!) {
 createTodo(input: $input) {
   todo {
     id
     title
   }
 }
}

mutation DeleteTodo($input: DeleteTodoInput!) {
 deleteTodo(input: $input) {
   success
 }
}

基于这些GraphQL查询和我们的服务端GraphQL schema,我们可以给这些查询生成typescript types。在此基础上,我们进一步使用apollo-typed-components来给每项操作生成react-apollo组件,也就是ApolloComps.tsx文件中的GetTodosQuery组件、CreateTodoMutation组件、和DeleteTodoMutation组件。

需要注意的是TypeScript使用.ts和.tsx文件扩展名,而不是.js和.jsx。然而,不像.jsx/.js之间那样宽松,当文件中包含任何JSX代码时,你必须使用.tsx扩展名,这样TypeScript才能消除JSX和其他TypeScript语言特性之间的歧义。举个例子,尖括号断言(angle bracket assertions):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const foo = <Foo>bar

这样在.ts文件中是有效的,在.tsx中是无效的。在.tsx中,需要使用as操作符来重写这一表达式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const foo = bar as Foo

我们在ApolloComps.tsx文件中生成出来的Query/Mutation组件看起来像下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* Generated using apollo-typed-components */
import * as React from "react";
import { Mutation } from "react-apollo";
import { CreateTodo } from "./queries.graphql"
import { CreateTodo as CreateTodoType, CreateTodoVariables } from "types";

type GetComponentProps<T> = T extends React.Component<infer P> ? P : never;
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

class CreateTodoMutationClass extends Mutation<CreateTodoType, CreateTodoVariables> {};

export const CreateTodoMutation = (props: Omit<GetComponentProps<CreateTodoMutationClass>, "mutation">) => <CreateTodoMutationClass mutation={CreateTodo} {...props} />;

CreateTodoType和CreateTodoVariables都是通过Apollo的代码生成器用命令行生成的。CreateTodoVariables是GraphQL mutation的入参字段类型,CreateTodoType是GraphQL mutation操作的返回字段类型。CreateTodoMutationClass是继承自react-apollo的Mutation组件的一个子类,它的构造函数两个入参类型就是CreateTodoType和CreateTodoVariables,我们使用它就可以拥有一个variablesdata都是指定类型的组件。我们最终实际暴露的组件是CreateTodoMutation,它是基于CreateTodoMutationClass封装的,并且将之前在queries.graphql中定义的CreateTodo传入组件。我们使用了两个工具类型来定义CreateTodoMutation的属性类型:GetComponentProps和Omit。GetComponentProps接收一个React组件T,然后返回组件T的props所期望的类型。Omit接收一个T类型的对象和K类型的一个键,然后返回T的类型定义,并把K传入的键从返回中移除。结合这两个工具类型,我们可以将CreateTodoMutation的props类型表示为CreateTodoMutationClass中除了mutation相关的props类型,并且由这个封装类来自动提供。

不能否认的是在ApolloComps.tsx文件中,我们不得不在mutation定义、typescript types和React组件之间复制一些代码片段。幸运地是,我们通过自动生成的方式让开发者不用为每个操作准确无误地去整合这些片段,他们只需要关注每个操作所对应的独立的React组件。

整合全部三个自动生成的组件后,我们最终的前端代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React, { Component } from "react";
import { GetTodos } from "./queries.graphql";
import { GetTodos_todos } from "./types";
import {
 GetTodosQuery,
 CreateTodoMutation,
 DeleteTodoMutation
} from "./ApolloComps";

type TodosListProps = {
 onDelete: (id: string) => {};
 todos: Array<GetTodos_todos>;
};
const TodosList = ({ onDelete, todos }: TodosListProps) => {
 return (
   <section className="main">
     <ul className="todo-list">
       {todos.map(todo => (
         <li key={todo.id}>
           <div className="view">
             <label>{todo.title}</label>
             <button className="destroy" onClick={() => onDelete(todo.id)} />
           </div>
         </li>
       ))}
     </ul>
   </section>
 );
};

type AppProps = {
 onCreate: (title: string) => {};
 onDelete: (id: string) => {};
 todos: Array<GetTodos_todos>;
};
type AppState = {
 newTodo: string;
};

class App extends Component<AppProps, AppState> {
 state = {
   newTodo: ""
 };

 render() {
   const { onCreate, onDelete, todos } = this.props;

   return (
     <section className="todoapp">
       <header className="header">
         <h1>{"todos"}</h1>
         <input
           className="new-todo"
           placeholder="What needs to be done?"
           value={this.state.newTodo}
           onKeyDown={(e: React.KeyboardEvent) => {
             if (e.keyCode !== 13) {
               return;
             }
             e.preventDefault();

             var val = this.state.newTodo.trim();

             if (val) {
               onCreate(val);
               this.setState({ newTodo: "" });
             }
           }}
           onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
             this.setState({ newTodo: e.target.value })
           }
           autoFocus={true}
         />
       </header>
       {todos.length ? <TodosList onDelete={onDelete} todos={todos} /> : null}
     </section>
   );
 }
}

const ApolloApp = () => {
 return (
   <GetTodosQuery>
     {({ data }) => (
       <DeleteTodoMutation>
         {deleteTodo => (
           <CreateTodoMutation>
             {createTodo => {
               if (!data || !data.todos) {
                 return null;
               }

               const todos = data.todos;

               return (
                 <App
                   onCreate={(title: string) =>
                     createTodo({
                       variables: {
                         input: {
                           title
                         }
                       },
                       update: (cache, response) => {
                         const createdTodo =
                           response.data &&
                           response.data.createTodo &&
                           response.data.createTodo.todo;
                         if (createdTodo) {
                           cache.writeQuery({
                             query: GetTodos,
                             data: {
                               todos: [...todos, createdTodo]
                             }
                           });
                         }
                       }
                     })
                   }
                   onDelete={(id: string) =>
                     deleteTodo({
                       variables: {
                         input: {
                           id
                         }
                       },
                       update: cache => {
                         cache.writeQuery({
                           query: GetTodos,
                           data: {
                             todos: todos.filter(item => item.id !== id)
                           }
                         });
                       }
                     })
                   }
                   todos={todos}
                 />
               );
             }}
           </CreateTodoMutation>
         )}
       </DeleteTodoMutation>
     )}
   </GetTodosQuery>
 );
};

export default ApolloApp;

结语

通过使用Apollo GraphQL、gRPC、React和TypeScript,我们既享受了查询数据的灵活性,也保证了我们后端服务之间的原子性。此外,由于实现了端对端的类型检验,很难出现数据的错误使用或是引入向前不兼容的变更。如果我们需要引入向前不兼容的变更,也很容易在发生变更之前决定我们系统中的哪些部分是需要进行修改的。

我们这次的主要收获之一,就是意识到了强制性的数据描述的强大之处(在这个例子中就是GraphQL schema和gRPC的proto文件)。通过生成类型文件,并且强制你的实现符合定义,能够确认系统中不同部分的网络数据交换的安全性。无论是采用哪种技术栈,服务端和客户端之间的类型安全的确能够增加对系统整体稳定性的信心。

本文示例代码地址:https://github.com/TLadd/todo-graphql-grpc

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
软考高级架构师:IPv6
IPv4和IPv6是互联网协议(IP)的两个版本,它们是全球互联网的基础。IPv4是目前最广泛使用的版本,而IPv6是为了解决IPv4地址耗尽问题而设计的下一代IP协议。
明明如月学长
2024/05/25
1590
软考高级架构师:IPv6
IPv4地址和IPv6地址的区别
IPv4地址空间仅有32位,因此仅有大约42亿个可能的地址。虽然这在IPv4的早期阶段是足够的,但随着互联网的发展,这个地址空间很快就被用完了。IPv6使用128位地址,可以支持大约340万亿亿亿亿个地址,可以满足未来互联网的需求。
玖叁叁
2023/04/27
4.4K0
IPv6地址
IPv6(Internet Protocol version 6)是互联网协议的第六版,它是用来替代IPv4(互联网协议第四版)的下一代协议。
久绊A
2025/05/05
2380
IPv4和IPv6有什么区别
IPv4 和 IPv6 是互联网使用的两个不同版本的 IP 协议,它们在地址长度、地址表示、地址数量、安全性等方面有着明显的区别。
程序员朱永胜
2023/11/17
7.7K0
计算机网络——网络层(1)
我的计算机网络专栏,是自己在计算机网络学习过程中的学习笔记与心得,在参考相关教材,网络搜素的前提下,结合自己过去一段时间笔记整理,而推出的该专栏,整体架构是根据计算机网络自顶向下方法而整理的,包括各大高校教学都是以此顺序进行的。 面向群体:在学计网的在校大学生,工作后想要提升的各位伙伴,
学编程的小程
2024/05/26
1570
计算机网络——网络层(1)
Linux:配置和使用IPv6的全面指南
IPv6(Internet Protocol Version 6)作为下一代互联网协议,旨在解决IPv4地址耗尽的问题。随着互联网设备的爆炸性增长,IPv6逐渐成为一种必然的选择。本文将详细探讨在Linux系统下如何配置和使用IPv6,包括IPv6地址的划分、配置方法以及常见问题的解决方案。
运维开发王义杰
2024/07/10
2.1K0
Linux:配置和使用IPv6的全面指南
深入解析路由与网络:网络的脉络
路由是指在计算机网络中,将数据包从源地址传递到目标地址的过程。在一个复杂的网络中,数据包需要经过多个中间节点(例如路由器、交换机等)才能到达目标。路由的主要目标是确定最佳路径,以确保数据包能够高效地到达目标地址。
久绊A
2023/12/18
2900
【愚公系列】软考高级-架构设计师 024-IP地址及子网
IP地址(Internet Protocol Address)是指互联网协议地址,是分配给网络中每个设备的一个唯一的标识符。IP地址主要用于网络中的设备间进行识别和通信。在互联网的使用中,每一台连接到网络的设备,例如电脑、手机、服务器等,都需要有一个独一无二的IP地址。
愚公搬代码
2024/06/13
1720
漫话:全球IPv4地址正式耗尽?到底什么是IPv4和IPv6?
导读:近日欧洲网络协调中心(RIPE NCC)宣布,全球所有 43 亿个 IPv4 地址已在11 月 25 日分配完毕,完全耗尽。那么什么是IPv4,耗尽了会怎样?
IT阅读排行榜
2019/12/10
2.2K0
漫话:全球IPv4地址正式耗尽?到底什么是IPv4和IPv6?
网络编程懒人入门(十一):一文读懂什么是IPv6
对于即时通讯技术(尤其是IM应用)的开发者来说,新产品上架苹果的App Store因IPv6问题被拒的情况,很常见。每次也都能根据网上的资料一一解决,并顺利通过审核。
JackJiang
2020/04/17
9720
IPv6,到底是什么?
通知的具体内容我就不贴出来了,主要意思就是:国家要大力推动IPv6的规模化部署,因此,我们提出了一些具体举措,敦促手机终端、承载网络、数据中心等全面支持IPv6,我们要在这方面领先全世界。。。
鲜枣课堂
2019/07/19
1.8K0
IPV6精髓浅析
IPV6关键特性有哪些? IPV6的路由表是如何生成的,与IPV4有何区别?SLAAC工作流程及哪些字段协作生成了IPV6地址?DAD为什么能实现重复地址检测?IPV6包头设计精要有哪些?DHCPv6自动获取IPV6地址工作过程是怎样的?希望本文能给您带来一点帮助!
锅总
2024/10/09
2680
IPV6精髓浅析
运维基础:为什么网络设备既有 IP 地址又有 MAC 地址?
在网络通信中,我们经常会听到 IP 地址和 MAC 地址这两个术语。它们分别是网络设备的两个重要标识,虽然它们都能唯一标识一个设备,但它们的作用和使用场景却有所不同。那么,为什么网络设备既有 IP 地址又有 MAC 地址呢?让我们一起来了解一下。
小明互联网技术分享社区
2024/07/28
9800
运维基础:为什么网络设备既有 IP 地址又有 MAC 地址?
hncloud:TCP IP协议的发展和优势
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/互联网协议)是互联网和现代计算机网络的基础协议集。它定义了数据在网络上如何被组织、传输和路由。TCP/IP协议集包含了许多协议,每个协议负责网络通信过程中的不同方面。下面是对TCP/IP协议的详细介绍,包括其工作原理、组成部分以及实际应用。
用户11163528
2024/08/14
2310
如何在 Linux 中配置 IPv4 和 IPv6 地址?
IPv4和IPv6是Internet上常用的两种IP地址协议。在Linux系统中,您可以通过配置网络接口来设置IPv4和IPv6地址。本文将详细介绍如何在Linux中配置IPv4和IPv6地址。
网络技术联盟站
2023/06/20
3.5K0
如何在 Linux 中配置 IPv4 和 IPv6 地址?
IPv6报头40字节具体怎么分配的?
IPv6协议是为了解决IPv4地址耗尽问题而设计的下一代互联网协议。与IPv4相比,IPv6不仅提供了更大的地址空间,还简化了报头结构,提高了网络设备的处理效率。IPv6报头的长度固定为40字节(320位),其字段分配如下:
用户11396661
2025/02/21
1130
【Linux】Linux中的IP:理解、配置和网络诊断
IP是一种网络层协议,用于在网络上唯一标识和寻址设备。它允许数据在网络中传输,并确保数据能够准确地到达目标设备。IPv4和IPv6是目前两个主要的IP版本,其中IPv6被设计为IPv4的继任者以解决IPv4地址枯竭的问题。
人不走空
2024/02/21
2870
【Linux网络】:网络基础(IP地址和MAC地址对应关系,IPv4,IPv6)
开始我以为只能是一个MAC地址只能对应一个IP地址,一个IP地址能对应多个MAC地址。但是现在好像错了。都可以多对多。
用户11396661
2025/02/20
2750
【Linux网络】:网络基础(IP地址和MAC地址对应关系,IPv4,IPv6)
IP地址的基本概念
IP地址是一个由数字和句点组成的字符串,它用于标识网络中的每个设备。在TCP/IP协议中,数据被分成小的数据包进行传输,每个数据包都包含了目标IP地址和源IP地址。通过IP地址,网络设备可以找到其他设备并与它们进行通信。
玖叁叁
2023/04/27
4630
今天,全球 43 亿个 IPv4 地址正式耗尽,将向 IPv6 过度!
该过程自80年代以来就已预见到,顶级地址实际上已经在2012年耗尽。那时,所有IPv4地址空间已分配给五大区域互联网注册机构,非洲网络信息中心 (AFRINIC)针对非洲,北美网络信息中心(ARIN)针对南极洲、加拿大、部分加勒比海地区和美国,亚太互联网络信息中心(APNIC)针对东亚、大洋洲、南亚和东南亚,拉丁美洲网络信息中心(LACNIC)针对加勒比海的大部分地区和整个拉丁美洲,以及欧洲网络信息中心(RIPE NCC)针对欧洲、中亚、俄罗斯和西亚。
杰哥的IT之旅
2020/06/18
7870
今天,全球 43 亿个 IPv4 地址正式耗尽,将向 IPv6 过度!
推荐阅读
相关推荐
软考高级架构师:IPv6
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档