Netty入门一文全解_netty教程-程序员宅基地

技术标签: spring  java  rpc  一文全解  

你如果,缓缓把手举起来,举到顶,再突然张开五指,那恭喜你,你刚刚给自己放了个烟花。
模块介绍
  1. netty-bio: 阻塞型网络通信demo。

  1. netty-nio: 引入channel(通道)、buffer(缓冲区)、selector(选择器)的概念,采用事件驱动的方式,使用单个线程就可以监听多个客户端通道,改进bio模式下线程阻塞等待造成的资源浪费。

  1. netty-demo: Netty小demo,认识Netty初体验。

  1. netty-groupchat: 使用Netty编写一个群聊系统。

  1. netty-http: Netty的HTTP调用demo。

  1. netty-bytebuf: Netty缓冲区使用demo。

  1. netty-decoder: Netty编解码,handler调用链使用示例。

  1. netty-idlestate: Netty心跳包使用示例。

  1. netty-sticking: 自定义协议与handler,解决TCP传输粘包与拆包问题。

  1. netty-rpc: 使用Netty自定义实现RPC通信。

netty-bio模块

模拟测试采用socket的bio方式进行网络通信。

blocking io:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情就会进入阻塞等待状态,造成不必要的线程开销。

适用于连接数据小且连接固定的系统架构。

架构示意图:

netty-nio模块

non-blocking io:同步非阻塞,在bio的架构上进行改进,引入channel(通道)、buffer(缓冲区)、selector(选择器)的概念,采用事件驱动的方式,使用单个线程就可以监听多个客户端通道,改进bio模式下线程阻塞等待造成的资源浪费。

架构示意图:

关键:select会根据不同的事件,在各个channel通道上进行切换

缓冲区buffer

本质上是一个可以读写数据(关键)的内存块,nio的读取与写入数据都必须是经过buffer的。

通道channel

把通道看做流、把通道看做流、把通道看做流,重要的事情说三遍,会很好理解。 nio引入的通道类似bio中流的概念,不同之处在于:

  • 通道可以同时进行读写操作,而流只能读或者写

  • 通道可以实现异步读写数据

  • 通道可以从缓冲区读数据,也可以写数据到缓冲区(双向的概念)

NIOFileOper01: 本地文件写数据

使用ByteBuffer与FileChannel,将“hello,李嘉图”NIOFileOper01.txt文件中。

NIOFileOper02: 本地文件读数据

使用ByteBuffer(缓冲) 和 FileChannel(通道), 将 NIOFileOper01.txt中的数据读入到程序,并显示在控制台屏幕

NIOFileOper03: 使用一个Buffer完成文件读取

使用 FileChannel(通道) 和 方法 read , write,完成文件的拷贝

NIOFileCopy:拷贝文件 transferFrom 方法

使用 FileChannel(通道) 和 方法 transferFrom ,完成文件的拷贝

选择器Selector

核心:selector能够检测多个注册的通道上是否有事件发生(多个channel以事件的方式可以注册到同一个selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理。 这样就可以做到只使用一个单线程去管理多个通道。

只有在连接/通道真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程。

原理图:

说明:

  1. 当客户端连接时,会通过ServerSocketChannel得到SocketChannel。

  1. Selector进行监听select方法,返回有事件发生的通道的个数。

  1. 将socketChannel注册到Selector上,register(),一个selector上可以注册多个SocketChannel。

  1. 注册后返回一个selectionKey,会和该selector关联。

  1. 进一步得到各个selectionKey(有事件发生)。

  1. 再通过selectionKey反向获取socketChannel,方法channel()。

  1. 可以通过得到的channel,完成业务逻辑。

Netty概述

异步的基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠的网络IO程序。

有了NIO为什么还需要Netty?

不需要过于关注底层的逻辑,对下面的sdk等进行封装,相当于简化和流程化了NIO的开发过程。spring和springboot的关系差不多。

因为 Netty 5出现重大bug,已经被官网废弃了,目前推荐使用的是Netty 4.x的稳定版本。

Netty高性能架构设计

线程模型基本介绍

传统阻塞 I/O 服务模型

模型特点:

  • 采用阻塞IO模式获取输入的数据

  • 每个连接都需要独立的线程完成数据的输入,业务处理,数据返回

问题分析:

  • 当并发数很大,就会创建大量的线程,占用很大系统资源

  • 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作,造成线程资源浪费

Reactor 模式

I/O 复用结合线程池,就是 Reactor 模式基本设计思想。

Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件作出反应。它像公司的电话接线员,接听来自客户的电话并将线路转译到适当的联系人。

单 Reactor 单线程
  • 优点:模型简单,没有多线程、进程通信、竞争问题,全部都在一个线程中完成。

  • 缺点:性能问题,只有一个线程,无法完全发挥多核CPU性能。Handler在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。

单 Reactor 多线程

在上一代的问题上进行修改,Reactor主线程只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池的某个线程处理业务。

  • 优点:充分利用多核CPU的处理能力。

  • 缺点:多线程数据共享和访问比较复杂,Reactor处理所有的事件监听与响应,在单线程运行,在高并发场景容易出现性能瓶颈。

主从 Reactor 多线程

针对单 Reactor 多线程模型中,Reactor 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 Reactor 在多线程中运行。

Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件。当Acceptor处理连接事件后,MainReactor将连接分配给 SubReactor,SubReactor将连接加入到连接队列进行监听,并创建Handler进行各种事件处理。

  • 优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理,无需返回数据给主线程。

  • 缺点:编程复杂度较高。

Reactor模式小结
  1. 单Reactor单线程,前台接待员和服务员是同一个人,全程为客户服务。

  1. 单Reactor多线程,1个前台接待员,多个服务员,接待员只负责接待。

  1. 主从Reactor多线程,多个前台接待员,多个服务生。

Netty 模型

  • Netty抽象出两组线程池,BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写。

  • 每个worker nioEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应通道,管道中维护了很多的处理器。

异步模型

基本介绍

  • 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的组件完成后,通过状态、通知和回调来通知调用者。

  • Netty中的I/O操作是异步的,包括Bind、Write、Connect等操作会简单的返回一个 ChannelFuture。

  • 调用者不能立刻获得结果,而是通过 Future-Listener机制,用户可以方便地主动获取或者通过通知机制获得I/O操作结果。

  • Netty的异步模型是建立在future和callback(回调)之上的。重点是future,它的核心思想:假设一个方法func,计算过程可能非常耗时,等待func返回显然不合适。那么在 调用func的时候,立刻返回一个future,后续可以

通过future去监控方法func的处理过程(即:Future-Listener机制)

  • ChannelFuture是一个接口:Public interface ChannelFuture extends Future

  • 可以添加监听器,当监听的事件发生时,就会通知到监听器。

  • 在使用Netty进行编程时,拦截操作和转换出入站数据只需要你提供callback或利用future即可。这使得链式操作简单、高效、并有利于编写可重用、通用的代码。

Future-Listener机制

当Future对象刚刚创建好时,处于非完成状态,调用者可以通过返回的channelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作。

常见的操作:

- 通过 isDone 方法来判断当前操作是否完成。

- 通过 isSuccess 方法来判断已完成的当前操作是否成功。

- 通过 getCause 方法来获取已完成的当前操作失败的原因。

- 通过 isCancelled 方法来判断已完成的当前操作是否被取消。

- 通过 addListener 方法来注册监听器,当操作已完成(isDone),将会通知指定的监听器。

小结:相比于传统阻塞I/O,执行I/O操作后线程会被阻塞住,直到操作完成。异步处理的好处是不会造成线程阻塞,线程在I/O操作期间可以执行别的程序,在高并发情形下会 更稳定和更高的吞吐量。

Netty 核心模块组件

ServerBootstrap、Bootstrap

Bootstrap意思是引导,一个Netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类, ServerBootstrap是服务器启动引导类。

常用方法:

- public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup):用于服务器端,用来设置两个EventLoop

- public B group(EventLoopGroup group):该方法用于客户端,用来设置一个EventLoop

- public B channel(Class<? extends C> channelClass):该方法用来设置一个服务器端的通道实现

- public B option(ChannelOption option, T value):用来给ServerChannel添加配置

- public ServerBootstrap childOption(ChannelOption childOption, T value):用来给接收的通道添加配置

- public ServerBootstrap childHandler(ChannelHandler childHandler):业务处理类,自定义handler

- public ChannelFuture bind(int inetPort):用于服务器端,用来设置占用的端口号

- public ChannelFuture connect(String inetHost, int inetPort):用于客户端,用来连接服务器端

Future、ChannelFuture

Netty中所有的IO操作都是异步的,不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过Future和ChannelFuture, 他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件。

常用的方法:

- Channel channel():返回当前正在进行IO操作的通道

- ChannelFuture sync():等待异步操作执行完毕

Channel

Netty网络通信的组件,能够用于执行网络 I/O 操作。 通过 Channel 可获得当前网络连接的通道的状态。 通过 Channel 可获得 网络连接的配置参数 (例如接收缓冲区大小)。 Channel 提供异步的网络 I/O 操作(如建立连接,读写,绑定端口),异步调用意味着任何 I/O 调用都将立即返回,并且不保证在调用结束时所请求的 I/O 操作已完成 调用立即返回一个 ChannelFuture实例,通过注册监听器到ChannelFuture上,可以 I/O 操作成功、失败或取消时回调通知调用方。 不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应,常用的 Channel 类型:

- NioSocketChannel,异步的客户端 TCP Socket 连接。

- NioServerSocketChannel,异步的服务器端 TCP Socket 连接。

- NioDatagramChannel,异步的 UDP 连接。

- NioSctpChannel,异步的客户端 Sctp 连接。

- NioSctpServerChannel,异步的 Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。

实际开发过程中,在拿到channel之后,做一个判断,看是什么连接,如(channel instanceof SocketChannel/DatagramChannel),就可以做不同的业务处理。

Selector

Netty基于Selector对象实现I/O多路复用,通过Selector一个线程可以监听多个连接的Channel事件。当向一个Selector中注册Channel后, Selector内部的机制就可以自动不断地查询(Select)这些注册的Channel是否有已就绪的I/O事件(例如可读,可写,网络连接完成等), 这样程序就可以很简单地使用一个线程高效地管理多个Channel。

ChannelHandler 及其实现类

ChannelHandler是一个接口,处理 I/O 事件或拦截 I/O 操作,并将其转发到其 ChannelPipeline(业务处理链)中的下一个处理程序。

ChannelHandler及其实现类一览图:

- ChannelInboundHandler 用于处理入站 I/O 事件。

- ChannelOutboundHandler 用于处理出站 I/O 操作。

- ChannelInboundHandlerAdapter 用于处理入站 I/O 事件。

- ChannelOutboundHandlerAdapter 用于处理出站 I/O 操作。

- ChannelDuplexHandler 用于处理入站和出站事件。

Pipeline 和 ChannelPipeline

ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的链。(也可以这样理解:ChannelPipeline 是 保存 ChannelHandler 的 List,用于处理或拦截 Channel 的入站事件和出站操作)。

ChannelPipeline 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。

在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应,它们的组成关系如下:

一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个ChannelHandlerContext中又关联着一个 ChannelHandler。

入站事件和出站事件在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。

常用方法:

- ChannelPipeline addFirst(ChannelHandler... handlers),把一个业务处理类(handler)添加到链中的第一个位置。

- ChannelPipeline addLast(ChannelHandler... handlers),把一个业务处理类(handler)添加到链中的最后一个位置。

ChannelHandlerContext

保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。ChannelHandlerContext中包含一个具体的事件处理器ChannelHandler,同时ChannelHandlerContext 中也绑定了对应的pipeline和Channel的信息,方便对ChannelHandler进行调用。

常用方法:

- ChannelFuture close(): 关闭通道

- ChannelOutboundInvoker flush(): 刷新

- ChannelFuture writeAndFlush(Object msg): 将数据写到ChannelPipeline中当前ChannelHandler的下一个ChannelHandler开始处理。

ChannelOption

  • ChannelOption.SO_BACKLOG

  • 对应TCP/IP协议listen函数中的backlog参数,用来初始化服务器可连接队列大小。服务端处理客户端连接请求时顺序处理的,所以同一时间只能处理一个客户端连接。 多个客户端来的时候,服务器将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。

  • ChannelOption.SO_KEEPALIVE

  • 一直保持连接活动状态。

EventLoopGroup 和其实现类 NioEventLoopGroup

  • BoosEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,BossEventLoop不断轮询将连接事件分离出来。

  • 通常是OP_ACCEPT事件,然后将接收到的SocketChannel交给WorkerEventLoopGroup。

  • WorkerEventLoopGroup会由next选择其中一个EventLoop来将这个SocketChannel注册到其维护的Selector并对其后续的IO事件进行处理。

常用方法:

- public NioEventLoopGroup(): 构造方法

- public Future<?> shutdownGracefully(): 断开连接,关闭线程

Unpooled类

Netty提供一个专门用来操作缓冲区(即Netty的数据容器)的工具类。

常用方法如下:

public static ByteBuf copiedBuffer(CharSequence String, Charset charset):通过给定的数据和字符编码返回一个ByteBuf对象(类似于NIO中的ByteBuffer)

Google Protobuf

Netty本身自带的 ObjectDecoder 和ObjectEncoder可以用来实现POJO对象或各种业务对象的编码和解码,底层使用的仍然是Java序列化技术,而Java序列化技术本身效率就不高,存在如下问题:

  • 无法跨语言

  • 序列化后的体积太大,是二进制的5倍多

  • 序列化性能太低 引出新的解决方案:Google的Protobuf。

Netty编解码器和handler的调用机制

代码示例:netty-decoder模块

使用自定义的编码器和解码器来说明Netty的handler调用机制

  • 客户端发送long -> 服务器

  • 服务器发送long -> 客户端

结论:

  • 不论解码器handler还是编码器handler接收的消息类型必须与待处理的消息类型一致,否则该handler不会被执行。

  • 在解码器进行数据解码时,需要判断缓存区(ByteBuf)的数据是否足够,否则接收到的结果会与期望的结果可能不一致。

  • ReplayingDecoder扩展了ByteToMessageDecoder类,使用这个类,我们不必调用readableBytes()方法。参数S指定了用户状态管理的类型,其中Void代表不需要状态管理。

  • ReplayingDecoder使用方便,但它也有一些局限性:

  • 并不是所有的 ByteBuf操作都被支持,如果调用了一个不被支持的方法,将会抛出一个 UnsupportedOperationException。

  • ReplayingDecoder 在某些情况下可能稍慢于 ByteToMessageDecoder,例如网络缓慢并且消息格式复杂时,消息会被拆成了多个碎片,速度变慢。

TCP粘包与拆包及解决方案

  • TCP是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有——成对的socket,因此,发送端为了将多个发送给接收端的包,更有效的发送给对方, 使用了优化算法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端就难于分辨出完整的数据包了, 因为面向流的通信是无消息保护边界的

TCP粘包与拆包解决方案

  • 使用 自定义协议 + 编解码器 来解决

  • 关键就是要解决 服务器端每次读取数据长度的问题,这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免TCP粘包、拆包。

代码示例:

  • 要求客户端发送5个Message对象,客户端每次发送一个Message对象

  • 服务器端每次接收一个Message,分5次进行解码,每读取到一个Message,会回复一个Message对象给客户端

Netty 核心源码剖析

只有看过Netty源码,才能说是真的掌握了Netty框架。

判断是否为 2 的 n 次方

private static boolean isPowerOfTwo(int val) {

return (val & -val) == val;

}

源码解析:

  • Netty启动过程源码剖析

  • Netty接受请求过程源码剖析

  • Pipeline Handler HandlerContext创建源码剖析

  • ChannelPipeline是如何调度handler的

  • Netty心跳(heartbeat)服务源码剖析

  • Netty核心组件EventLoop源码剖析

  • handler中加入线程池和Context中添加线程池的源码剖析

用Netty 自己 实现 dubbo RPC

  • RPC(Remote Procedure call) - 远程程序调用,是一个计算机通信协议。该协议允许运行与一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。

  • 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样。

  • 常见的PRC框架有:阿里的Dubbo、Google的gRPC、Go语言的rpcx,spring的Spring cloud。

  • RPC的目标就是将 2-8 这些步骤都封装起来,用户无需关心这些细节,可以像调用本地方法一样即可完成远程服务调用。

自己实现 Dubbo RPC(基于Netty)

需求说明:

  • Dubbo底层使用了Netty作为网络通信框架,要求用Netty实现一个简单的RPC框架

  • 模仿Dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者的服务,提供者返回一个字符串,消费者打印提供者返回的数据

设计说明:

  • 创建一个接口,定义抽象方法,用于消费者和提供者之间的约定。

  • 创建一个提供者,该类需要监听消费者请求,并按照约定返回数据。

  • 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用Netty请求提供者返回数据。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2301_76354366/article/details/128816560

智能推荐

Opencv3安装踩坑(SLAM十四讲)_/usr/include/opencv-程序员宅基地

文章浏览阅读5.5k次,点赞3次,收藏57次。因为之前玩ros,使用了sudo apt-get install libopencv-dev 安装了opencv2,而slam十四讲的opencv版本为3。因此需要重新安装opencv3(在与opencv2共存的条件下)首先检查已有的opencv版本:pkg-config opencv --modversion如果是2版本,则需要安装3版本1. 安装过程1.安装依赖(如果有依赖则不必..._/usr/include/opencv

【Spring6】| Bean的生命周期(五步、七步、十步法剖析)-程序员宅基地

文章浏览阅读8.4k次,点赞34次,收藏148次。【Spring6】| Bean的生命周期(五步、七步、十步法剖析)_bean的生命周期

Appium(六)Toast 定位 + Android版本问题的解决以及 Selenium 的安装+屏幕截图_you could still use other supported backends in or-程序员宅基地

文章浏览阅读2.8k次。页面的错误提示 Toast,比如:网络加载问题出现的错误、用户名或者密码输入错误、银行卡密码输入次数提示等错误,这些错误的内容一般都会以浮动的方式显示,而且他们显示的时间非常的有限,可能是几秒钟的时间就会消失掉,不会被点击,无法获取到他们的焦点,但是在 Android 中有一个叫做 Appium Toast 的工具,在 Android 1.6.x 多的时候就已经开始支持识别 Toas..._you could still use other supported backends in order to automate older andr

关于idea将模块修改为Resources报错:Two modules in a project cannot share the same content root.-程序员宅基地

文章浏览阅读1.8k次。点击: file-->Project Structure-->选择:Models----> 右击文件点击Delete最后修改即可_two modules in a project cannot share the same content root

异步FIFO设计-程序员宅基地

文章浏览阅读590次,点赞26次,收藏26次。同步后的写指针与读指针进行比较,如果它们相等或满足其他预定的条件,就表明FIFO为空,从而产生空逻辑信号。产生空状态信号时,实际FIFO中有数据,相当于提前判断了空状态信号,此时不再进行读FIFO数据操作也是安全的。此时经常使用多余的1bit分别当做读写地址的拓展位,来区分读写地址相同的时候,FIFO的状态是空还是满状态。(格雷码是一种二进制编码方式,其相邻的两个数值只有一个位的差异,这种特性使得格雷码在变化时只涉及到一个位的翻转,从而减少了由于多位同时变化可能带来的不稳定性和错误。

Cannot load configuration class_cannot load configuration class: com.jsh.erp.erpap-程序员宅基地

文章浏览阅读3.0k次。将SDK从16设置为1.8,如下图_cannot load configuration class: com.jsh.erp.erpapplication

随便推点

MatconvNet:尝试将 SCRIPT vl_nnconv 作为函数执行_尝试将 script vl_nnconv 作为函数执行:-程序员宅基地

文章浏览阅读5k次,点赞7次,收藏7次。运行matconvnet遇到下面问题:尝试将 SCRIPT vl_nnconv 作为函数执行:L:\Fine-tuning-CNN\matlab\cnnimageretrieval\matlab\vl_nnconv.m出错 dagnn.Conv/forward (line 11)outputs{1} = vl_nnconv(...出错 dagnn.Layer/forwardAdvanced (line 85)outputs = obj.forward(inputs, {net.params(_尝试将 script vl_nnconv 作为函数执行:

Thymeleaf怎么显示request中绑定的数据?_thyme 显示request的值-程序员宅基地

文章浏览阅读1.1k次。Thymeleaf怎么显示request中绑定的数据?后端代码:@Controllerpublic class test { @RequestMapping({"/test"}) public String test(HttpServletRequest httpServletRequest){ //使用setAttribute存入数据 httpServletRequest.setAttribute("zhang" , "zhangjiahong"); _thyme 显示request的值

VSCode之Markdown自动生成目录#TOC#解决目录不整齐问题_vscode markdown 表格显示不全-程序员宅基地

文章浏览阅读3.1k次,点赞8次,收藏3次。一 、下载插件(1)在扩展里,搜索“Markdown”,在列表里选择Markdown的插件。(2)例如“Markdown TOC”,这是一个专门生产目录的插件。点击安装二、生成目录在你想添加目录的地方右击选择“Markdown TOC:Insert/Update”三、可能出现的问题及解决VSCode中Markdown目录显示异常TOC标签格式异常出现如下auto的文字原因:默..._vscode markdown 表格显示不全

MediaServerStudioEssentials2017R2版本安装_media server studio community 2017-程序员宅基地

文章浏览阅读1.1k次。安装依赖sudo apt-get install -y preload libpciaccess-dev libpthread-stubs0-devsudo apt-get install -y compizconfig-settings-managersudo apt-get install -y subversion git git-svn gcc g++ make cmake nasms_media server studio community 2017

第9讲:使用ajax技术实现增删改查及分页显示功能(jQuery)_ajax实现修改功能-程序员宅基地

文章浏览阅读1.2k次。本讲内容首先讲解jQuery对ajax的支持,分别讲解$.post,$.get,$.ajax等方法,这些方法的参数,使用方法及区别。最后对ajax的综合应用举例:在同一个页面实现新增,修改,删除学校资料,分页列表等功能,前端使用html静态页面,使用MySQL数据库,后台使用servlet技术实现。_ajax实现修改功能

找回word文件的两种密码_word文档保护默认密码是多少-程序员宅基地

文章浏览阅读773次。Word文档的密码也有两种:一种是打开密码,一种是编辑限制两种密码加密后的效果也是不一样的:设置了打开密码的Word文档,是在打开文件的时候需要输入密码,保护文件内容不被其他人看到。当我们输入了正确的word密码,进入到文件之后,就一些都正常了,可以正常阅读、正常编辑word文件。设置了编辑限制的Word文档,打开文件的时候不需要输入密码,打开之后能够查看Word文档内容,但是想要编辑WORD文件的时候,保护文件内容不被修改,需要输入正确的Word密码,将限制编辑取消才能够正常编辑Word文档。两种密码如果_word文档保护默认密码是多少