让我们,从头到尾,通透I/O模型

news2024/11/16 20:57:21

什么是IO

一句话总结
IO就是内存和硬盘的输入输出

I/O 其实就是 input 和 output 的缩写,即输入/输出。

那输入输出啥呢?

比如我们用键盘来敲代码其实就是输入,那显示器显示图案就是输出,这其实就是 I/O。

而我们时常关心的磁盘 I/O 指的是硬盘和内存之间的输入输出。

读取本地文件的时候,要将磁盘的数据拷贝到内存中,修改本地文件的时候,需要把修改后的数据拷贝到磁盘中。

网络 I/O 指的是网卡与内存之间的输入输出。

当网络上的数据到来时,网卡需要将数据拷贝到内存中。当要发送数据给网络上的其他人时,需要将数据从内存拷贝到网卡里。

那为什么都要跟内存交互呢?

我们的指令最终是由 CPU 执行的,究其原因是 CPU 与内存交互的速度远高于 CPU 和这些外部设备直接交互的速度。

因此都是和内存交互,当然假设没有内存,让 CPU 直接和外部设备交互,那也算 I/O。

总结下:I/O 就是指内存与外部设备之间的交互(数据拷贝)。

好了,明确什么是 I/O 之后,让我们来揭一揭 socket 通信内幕~

如何通信

socket

socket 创建

首先服务端需要先创建一个 socket。在 Linux 中一切都是文件,那么创建的 socket 也是文件,每个文件都有一个整型的文件描述符(fd)来指代这个文件。

int socket(int domain, int type, int protocol);
  • domain:这个参数用于选择通信的协议族,比如选择 IPv4 通信,还是 IPv6 通信等等
  • type:选择套接字类型,可选字节流套接字、数据报套接字等等。
  • protocol:指定使用的协议。这个 protocol 通常可以设为 0 ,因为由前面两个参数可以推断出所要使用的协议。

比如socket(AF_INET, SOCK_STREAM, 0);,表明使用 IPv4 ,且使用字节流套接字,可以判断使用的协议为 TCP 协议。

这个方法的返回值为创建的 socket 的 fd。

bind 绑定

现在我们已经创建了一个 socket,但现在还没有地址指向这个 socket。

众所周知,服务器应用需要指明 IP 和端口,这样客户端才好找上门来要服务,所以此时我们需要指定一个地址和端口来与这个 socket 绑定一下。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数里的 sockfd 就是我们创建的 socket 的文件描述符,执行了 bind 参数之后我们的 socket 距离可以被访问又更近了一步。

listen 监听

执行了 socket、bind 之后,此时的 socket 还处于 closed 的状态,也就是不对外监听的,然后我们需要调用 listen 方法,让 socket 进入被动监听状态,这样的 socket 才能够监听到客户端的连接请求。

int listen(int sockfd, int backlog);

传入创建的 socket 的 fd,并且指明一下 backlog 的大小。

这个 backlog 我查阅资料的时候,看到了三种解释:

  1. socket 有一个队列,同时存放已完成的连接和半连接,backlog为这个队列的大小。
  2. socket 有两个队列,分别为已完成的连接队列和半连接队列,backlog为这个两个队列的大小之和。
  3. socket 有两个队列,分别为已完成的连接队列和半连接队列,backlog仅为已完成的连接队列大小。

解释下什么叫半连接

我们都知道 TCP 建立连接需要三次握手,当接收方收到请求方的建连请求后会返回 ack,此时这个连接在接收方就处于半连接状态,当接收方再收到请求方的 ack 时,这个连接就处于已完成状态:
在这里插入图片描述
所以上面讨论的就是这两种状态的连接的存放问题。

我查阅资料看到,基于 BSD 派生的系统的实现是使用的一个队列来同时存放这两种状态的连接, backlog 参数即为这个队列的大小。

而 Linux 则使用两个队列分别存储已完成连接和半连接,且 backlog 仅为已完成连接的队列大小

accept 服务端连接

现在我们已经初始化好监听套接字了,此时会有客户端连上来,然后我们需要处理这些已经完成建连的连接。

从上面的分析我们可以得知,三次握手完成后的连接会被加入到已完成连接队列中去。

在这里插入图片描述
这时候,我们就需要从已完成连接队列中拿到连接进行处理,这个拿取动作就由 accpet 来完成。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

这个方法返回的 int 值就是拿到的已完成连接的 socket 的文件描述符,之后操作这个 socket 就可以进行通信了。

如果已完成连接队列没有连接可以取,那么调用 accept 的线程会阻塞等待。

至此服务端的通信流程暂告一段落,我们再看看客户端的操作。

connect 客户端连接

客户端也需要创建一个 socket,也就是调用 socket(),这里就不赘述了,我们直接开始建连操作。

客户端需要与服务端建立连接,在 TCP 协议下开始经典的三次握手操作,再看一下上面画的图:
在这里插入图片描述
客户端创建完 socket 并调用 connect 之后,连接就处于 SYN_SEND 状态,当收到服务端的 SYN+ACK 之后,连接就变为 ESTABLISHED 状态,此时就代表三次握手完毕。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

调用connect需要指定远程的地址和端口进行建连,三次握手完毕之后就可以开始通信了。

客户端这边不需要调用 bind 操作,默认会选择源 IP 和随机端口。

建立连接操作总结

用一幅图来小结一下建连的操作:
在这里插入图片描述
可以看到这里的两个阻塞点:

  • connect:需要阻塞等待三次握手的完成。
  • accept:需要等待可用的已完成的连接,如果已完成连接队列为空,则被阻塞。

read、write

连接建立成功之后,就能开始发送和接收消息了,我们来看一下
在这里插入图片描述
read 为读数据,从服务端来看就是等待客户端的请求,如果客户端不发请求,那么调用 read 会处于阻塞等待状态,没有数据可以读,这个应该很好理解。

write 为写数据,一般而言服务端接受客户端的请求之后,会进行一些逻辑处理,然后再把结果返回给客户端,这个写入也可能会被阻塞。

这里可能有人就会问 read 读不到数据阻塞等待可以理解,write 为什么还要阻塞,有数据不就直接发了吗?

因为我们用的是 TCP 协议,TCP 协议需要保证数据可靠地、有序地传输,并且给予端与端之间的流量控制。

所以说发送不是直接发出去,它有个发送缓冲区,我们需要把数据先拷贝到 TCP 的发送缓冲区,由 TCP 自行控制发送的时间和逻辑,有可能还有重传什么的。

如果我们发的过快,导致接收方处理不过来,那么接收方就会通过 TCP 协议告知:别发了!忙不过来了。发送缓存区是有大小限制的,由于无法发送,还不断调用 write 那么缓存区就满了,满了就不然你 write 了,所以 write 也会发生阻塞。

综上,read 和 write 都会发生阻塞。

总结:为什么网络 I/O 会被阻塞?——io模型

因为建连和通信涉及到的 accept、connect、read、write 这几个方法都可能会发生阻塞。

阻塞会占用当前执行的线程,使之不能进行其他操作,并且频繁阻塞唤醒切换上下文也会导致性能的下降。

由于阻塞的缘故,起初的解决的方案就是建立多个线程,但是随着互联网的发展,用户激增,连接数也随着激增,需要建立的线程数也随着一起增加,到后来就产生了 C10K 问题。

服务端顶不住了呀,咋办?

优化呗!

所以后来就弄了个非阻塞套接字,然后 I/O多路复用、信号驱动I/O、异步I/O。

下篇我们就来好好盘盘,这几种 I/O 模型!

基础知识介绍——内核态与用户态

上篇我们已经搞懂了 socket 的通信内幕,也明白了网络 I/O 确实会有很多阻塞点,阻塞 I/O 随着用户数的增长只能利用增加线程的方式来处理更多的请求,而线程不仅会占用内存资源且太多的线程竞争会导致频繁地上下文切换产生巨大的开销。

因此,阻塞 I/O 已经不能满足需求,所以后面大佬们不断地优化和演进,提出了多种 I/O 模型。

在 UNIX 系统下,一共有五种 I/O 模型,今天我们就来盘一盘它!

不过在介绍 I/O 模型之前,我们需要先了解一下前置知识。

内核态与用户态

我们的电脑可能同时运行着非常多的程序,这些程序分别来自不同公司。

谁也不知道在电脑上跑着的某个程序会不会发疯似得做一些奇怪的操作,比如定时把内存清空了。

因此 CPU 划分了非特权指令和特权指令,做了权限控制,一些危险的指令不会开放给普通程序,只会开放给操作系统等特权程序。

你可以理解为我们的代码调用不了那些可能会产生“危险”操作,而操作系统的内核代码可以调用。

这些“危险”的操作指:内存的分配回收,磁盘文件读写,网络数据读写等等。

如果我们想要执行这些操作,只能调用操作系统开放出来的 API ,也称为系统调用。

这就好比我们去行政大厅办事,那些敏感的操作都由官方人员帮我们处理(系统调用),所以道理都是一样的,目的都是为了防止我们(普通程序)乱来。

这里又有两个名词:

  • 用户空间
  • 内核空间。

我们普通程序的代码是跑在用户空间上的,而操作系统的代码跑在内核空间上,用户空间无法直接访问内核空间的。当一个进程运行在用户空间时就处于用户态,运行在内核空间时就处于内核态

当处于用户空间的程序进行系统调用,也就是调用操作系统内核提供的 API 时,就会进行上下文的切换,切换到内核态中,也时常称之为陷入内核态。

那为什么开头要先介绍这个知识点呢?

因为当程序请求获取网络数据的时候,需要经历两次拷贝:

  • 程序需要等待数据从网卡拷贝到内核空间。
  • 因为用户程序无法访问内核空间,所以内核又得把数据拷贝到用户空间,这样处于用户空间的程序才能访问这个数据。

介绍这么多就是让你理解为什么会有两次拷贝,且系统调用是有开销的,因此最好不要频繁调用。

然后我们今天说的 I/O 模型之间的差距就是这拷贝的实现有所不同!

今天我们就以 read 调用,即读取网络数据为例子来展开 I/O 模型。

发车!

IO模型

同步阻塞模型

假如A在河边钓鱼的时候,非常的专心,生怕鱼儿溜掉,故此,A就一直盯着鱼竿,一直等着鱼儿上钩,专心的做这一件事情,直到鱼儿上钩,才结束这个动作,这就是阻塞IO。在内核把数据准备好之前,系统调用会一直处于阻塞状态。

在这里插入图片描述
当用户程序的线程调用 read 获取网络数据的时候,首先这个数据得有,也就是网卡得先收到客户端的数据,然后这个数据有了之后需要拷贝到内核中,然后再被拷贝到用户空间内,这整一个过程用户线程都是被阻塞的。

假设没有客户端发数据过来,那么这个用户线程就会一直阻塞等着,直到有数据。即使有数据,那么两次拷贝的过程也得阻塞等着。

所以这称为同步阻塞 I/O 模型。

它的优点很明显,简单。调用 read 之后就不管了,直到数据来了且准备好了进行处理即可。

缺点也很明显,一个线程对应一个连接,一直被霸占着,即使网卡没有数据到来,也同步阻塞等着。

我们都知道线程是属于比较重资源,这就有点浪费了。

所以我们不想让它这样傻等着。

于是就有了同步非阻塞 I/O。

同步非阻塞 I/O

假如B也在河边钓鱼,B不想像A一样把所有的时间都花在等鱼儿上钩这件事情上,所以他的做法就是在等待鱼儿上钩的同时,自己也可以看看书,刷刷小编的博客,聊天等等。但是B也不是就不管鱼儿了,他会每隔一段固定时间都来看一下,有没有鱼儿上钩,如果有鱼儿上钩,他就结束这个动作,这就是非阻塞IO。
非阻塞IO往往需要程序员循环的方式反复尝试读取文件描述符,这个过程称为轮询,这对于cpu来说的话是较大的浪费,一般只有特定的场景下才能使用。
在这里插入图片描述
从图中我们可以很清晰的看到,同步非阻塞I/O 基于同步阻塞I/O 进行了优化:

在没数据的时候可以不再傻傻地阻塞等着,而是直接返回错误,告知暂无准备就绪的数据!

这里要注意,从内核拷贝到用户空间这一步,用户线程还是会被阻塞的。

这个模型相比于同步阻塞 I/O 而言比较灵活,比如调用 read 如果暂无数据,则线程可以先去干干别的事情,然后再来继续调用 read 看看有没有数据。

但是如果你的线程就是取数据然后处理数据,不干别的逻辑,那这个模型又有点问题了。

等于你不断地进行系统调用,如果你的服务器需要处理海量的连接,那么就需要有海量的线程不断调用,上下文切换频繁,CPU 也会忙死,做无用功而忙死。

那怎么办?

于是就有了I/O 多路复用。

多路复用

假如D也在河边钓鱼,但是D是一个土豪,他一个人就拿了好多鱼竿摆在哪里,这样很明显就增加了鱼儿上钩的机会。他只需要不断地查看每个鱼竿是否有鱼儿上钩就行了,提高了效率。 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
在这里插入图片描述
从图上来看,好像和上面的同步非阻塞 I/O 差不多啊,其实不太一样,线程模型不一样。

既然同步非阻塞 I/O 在太多的连接下频繁调用太浪费了, 那就招个专员吧。

这个专员工作就是管理多个连接,帮忙查看连接上是否有数据已准备就绪。

也就是说,可以只用一个线程查看多个连接是否有数据已准备就绪。

具体到代码上,这个专员就是 select ,我们可以往 select 注册需要被监听的连接,由 select 来监控它所管理的连接是否有数据已就绪,如果有则可以通知别的线程来 read 读取数据,这个 read 和之前的一样,还是会阻塞用户线程。

这样一来就可以用少量的线程去监控多条连接,减少了线程的数量,降低了内存的消耗且减少了上下文切换的次数,很舒服。

想必到此你已经理解了什么叫 I/O 多路复用。

所谓的多路指的是多条连接,复用指的是用一个线程就可以监控这么多条连接。

看到这,你再想想,还有什么地方可以优化的?

信号驱动式IO

假如C也在河边钓鱼,他认为A、B不够聪明,故此,他想了一种办法,就是在鱼竿上挂上了一个铃铛,当有鱼儿上钩的时候,铃铛就会被触发,发出响声,他就可以过去将鱼儿钓上来了。信号驱动IO模型,应用进程告诉内核:当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,并且调用我的信号处理函数来获取数据报。
在这里插入图片描述
上面的 select 虽然不阻塞了,但是他得时刻去查询看看是否有数据已经准备就绪,那是不是可以让内核告诉我们数据到了而不是我们去轮询呢?

信号驱动 I/O 就能实现这个功能,由内核告知数据已准备就绪,然后用户线程再去 read(还是会阻塞)。

听起来是不是比 I/O 多路复用好呀?那为什么好像很少听到信号驱动 I/O?
为什么市面上用的都是 I/O 多路复用而不是信号驱动?

因为我们的应用通常用的都是 TCP 协议,而 TCP 协议的 socket 可以产生信号事件有七种。

也就是说不仅仅只有数据准备就绪才会发信号,其他事件也会发信号,而这个信号又是同一个信号,所以我们的应用程序无从区分到底是什么事件产生的这个信号。

那就麻了呀!

所以我们的应用基本上用不了信号驱动 I/O,但如果你的应用程序用的是 UDP 协议,那是可以的,因为 UDP 没这么多事件。

因此,这么一看对我们而言信号驱动 I/O 也不太行。

异步 I/O

假如E也想钓鱼,但是他又有点忙,所以他雇佣了一个人专门帮他看着鱼竿,一旦有鱼儿上钩,就让这个人通知他,他过来将鱼儿钓上来。由内核在数据拷贝完成时, 通知应用程序(信号驱动是告诉应用程序何时可以开始拷贝数据).
这一次我们雇了一个钓鱼高手。他不仅会钓鱼,还会在鱼上钩之后给我们发短信,通知我们鱼已经准备好了。我们只要委托他去抛竿,然后就能跑去干别的事情了,直到他的短信。我们再回来处理已经上岸的鱼。

在这里插入图片描述
信号驱动 I/O 虽然对 TCP 不太友好,但是这个思路对的:往异步发展,但是它并没有完全异步,因为其后面那段 read 还是会阻塞用户线程,所以它算是半异步。

因此,我们得想下如何弄成全异步的,也就是把 read 那步阻塞也省了。

其实思路很清晰:让内核直接把数据拷贝到用户空间之后再告知用户线程,来实现真正的非阻塞I/O!

所以异步 I/O 其实就是用户线程调用 aio_read ,然后包括将数据从内核拷贝到用户空间那步,所有操作都由内核完成,当内核操作完毕之后,再调用之前设置的回调,此时用户线程就拿着已经拷贝到用户控件的数据可以继续执行后续操作。

在整个过程中,用户线程没有任何阻塞点,这才是真正的非阻塞I/O。

那么问题又来了:

为什么常用的还是I/O多路复用,而不是异步I/O?
因为 Linux 对异步 I/O 的支持不足,你可以认为还未完全实现,所以用不了异步 I/O。

这里可能有人会说不对呀,像 Tomcat 都实现了 AIO的实现类,其实像这些组件或者你使用的一些类库看起来支持了 AIO(异步I/O),实际上底层实现是用 epoll 模拟实现的。

而 Windows 是实现了真正的 AIO,不过我们的服务器一般都是部署在 Linux 上的,所以主流还是 I/O 多路复用。

至此,想必你已经清晰五种 I/O 模型是如何演进的了。

下篇,我将讲讲谈到网络 I/O 经常会伴随的几个容易令人混淆的概念:同步、异步、阻塞、非阻塞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络层重点协议之IP协议(IPv4)

网络层的作用就是来路由的选择,规划传输的路径,其中网络层的重点协议就是IP协议。4位版本号版本号的取值只有4和64位首部长度描述了IP报头有多长,报头中有一个选项部分,是变长的,是可有可无的部分,所以IP报…

【python百炼成魔】python之列表详解

文章目录一. 列表的概念1.1 列表是什么?1.2 为什么要使用列表?1.3 列表的定义二. 列表的增删改查操作2.1 列表的读取2.2 列表的切片2.3 列表的查询操作2.3.1 not in ,in 表达式2.3.2 列表元素遍历2.4 列表元素的增加操作2.4.1 append()的相关用法2.4.2 e…

Webpack(应用一:基本使用,只需六步骤)

前言 上一篇文章已经说明了webpack的定义以及需求 本偏文章主要讲解webpack的基本使用 tips:现在以vscode编辑器来展示,只需要几个步骤就可以实现webpack的基本使用。 一、首先要安装node.js 1、不会安装node.js的,可以在网上自己找教程来…

Java:博客系统,实现加盐加密,分页,草稿箱,定时发布

文章目录1. 项目概述2. 准备工作2.1 数据库表格代码2.2 前端代码2.3 配置文件3. 准备项目结构3.1 拷贝前端模板3.2 定义实体类3.3 定义mapper接口和 xml 文件3.4 创建其他包4. 统一数据返回4.1 Result 类4.2 统一数据格式5. 注册5.1 逻辑5.2 验证数据规范性5.3 实现注册5.4 前端…

Teradata与中国市场官宣“分手”!国产数据库准备好了吗?

2月15日,西方的情人节刚刚过去一天,国内IT行业就爆出一个大瓜。 继Adobe、甲骨文、Tableau、Salesforce之后,又一个IT巨头要撤离中国市场。 Teradata天睿公司官宣与中国市场“分手”,结束在中国的直接运营。目前,多家…

记录--TS类型写不好?一起来训练提升吧!

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前期准备 本篇文章的编写目的是为了提升TS类型的书写质量,高质量的类型可以提高项目的可维护性并避免一些潜在的漏洞; 在学习本篇之前需要有一定的TS基础知识,在此基础上可以更…

wpgarlic:一款功能强大的WordPress插件模糊测试工具

关于wpgarlic wpgarlic是一款功能强大的WordPress插件模糊测试工具,该工具可以帮助广大研究人员查找WordPress插件漏洞。目前为止,该工具已经成功在大约1500万个网站上找出了140多个WordPress插件漏洞。 考虑到模糊测试工具的运行机制,即模…

mtb10_Presentations_tableau Animation(pages) to pdf to png or Animation

All Tableau authors are essentially storytellers. Analyzing data is more than just puzzle- solving; it is a search for a story that will make a difference. Topics can range from Airbnb爱彼迎(美国短租平台) to the Zika virus[ˈziːkə ˈvaɪrəs]寨卡病毒, an…

Java之滑动窗口详解

目录 一.滑动窗口 1.什么滑动窗口 2.滑动窗口的三要素 二.找到字符串中所有字母异位词 1.题目描述 2.问题分析 3.代码实现 三.字符串的排列 1.题目描述 2.问题分析 3.代码实现 四.考试的最大困扰度 1.题目描述 2.问题分析 3.代码实现 五.替换后的最长重复字符 …

git命令行推送本地分支到远程仓库

之前说过Git与IDEA强强联合(HTTPS协议连接)那么如何使用命令行来推送代码呢? 如下图所示为一个基于layui的前端代码: 目录工作区文件: 本地内容就是将这些内容推送到远程仓库 首先使用git命令初始化git本地仓库&…

阿里巴巴测试岗,3面都过了,到头来却因为这个原因被刷了...

说在前面 面试时最好不要虚报工资。本来阿里巴巴是很想去的,几轮面试也通过了,最后没offer,自己只想到下面几个原因 虚报工资,比实际高30%;有更好的人选,这个可能性不大,我看还在招聘。我是面…

【Redis】Redis持久化之AOF详解(Redis专栏启动)

📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。文章内容兼具广度深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公…

Windows11安装OpenSSH客户端,实现ssh远程连接云服务器

一、打开设置 - 应用 - 添加可选功能,选择安装OpenSSH客户端 二、安装完之后,验证 ssh 命令 1、启动ssh服务之后,打开 cmd 窗口已支持连接 三、如果 ssh 命令仍然不生效,需要配置 ssh 环境变量 1、新建 SSH_HOME 环境变量&#…

2023 年前端十大 Web 发展趋势

很长一段时间,Web 开发的前景似乎没有什么进展(2016 年至 2021 年),但在刚刚过去的 2022 年中确实又猛窜了一波。今天主要想跟大家聊聊最新 Web 开发趋势。相信这波浪潮会继续激发 Web 开发者的关注,对万象更新的 2023…

视频连载11 - 这个为生信学习和生信作图打造的开源R教程真香!!!

点击阅读原文跳转完整教案。1 思考题2 R基础2.1 R安装2.2 Rstudio基础2.2.1 Rstudio版本2.2.2 Rstudio安装2.2.3 Rstudio 使用2.3 R基本语法2.3.1 获取帮助文档,查看命令或函数的使用方法、事例或适用范围2.3.2 R中的变量及其初始化2.3.3 变量类型和转换2.3.4 R中矩…

MYSQL安装部署--Linux 仓库安装

声明 :# 此次我们安装的 MYSQL 版本是 8.0.32 版本 我们本次安装 MYSQL 总共要介绍 四种方式 # 仓库安装# 本地安装# 容器安装# 源码安装我们本篇介绍的是 仓库安装 仓库安装 下载 MYSQL 安装包 # MYSQL 安装,我们都是基于 MYSQL 官方网站里进行下载~&a…

计数系统架构设计(转)

本文主要节选和总结自沈剑大佬的 计数系统架构实践一次搞定 | 架构师之路和文章的评论,略有删改 一、问题描述 很多业务都有“计数”需求,以微博为例: 微博首页的个人中心部分,有三个重要的计数:关注了多少人的计数、粉…

如何编写一个基本的 Verilog Module(模块)

1、概述这篇文章主要介绍了 Verilog 在 FPGA 设计中的概念和使用方法。首先讨论使用模块(module)关键字构造 Verilog 设计的方式,以及这与所描述的硬件的关系。这包括对参数、端口(port)和例化(instantiato…

JSON Web Token (JWT)

1,什么是JWT JWT是JSON Web Token是简称,是一个行业开发标准(RFC 7519)定义了一种简介的 自包含的协议格式,用于在通信双方传递JSON对象,传递的信息经过数字签名可以被验证和信任。它可以使用HMAC算法或使…

4.如何靠IT逆袭大学?

学习的动力不止于此: IT逆袭 这两天利用工作空余时间读了贺利坚老师的《逆袭大学——传给 IT 学子的正能量》,感触很多,有些后悔没有好好利用大学时光。 不过人都是撞了南墙再回头的,吃一堑长一智。 这本书无论你是工作了还是…