热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

transports(传输)

OIO-NIO如果你曾经使用Java提供的网络接口工作过,你可能已经遇到过想从阻塞传输切换到非阻塞传输的情况,这种切换是比较困难的,因

OIO->NIO

如果你曾经使用Java提供的网络接口工作过,你可能已经遇到过想从阻塞传输切换到非阻塞传输的情况,这种切换是比较困难的,因为阻塞IO和非阻塞IO使用的API有很大的差异;Netty提供了上层的传输实现接口使得这种情况变得简单。我们可以让所写的代码尽可能通用,而不会依赖一些实现相关的APIs。当我们想切换传输方式的时候不需要花很大的精力和时间来重构代码。


使用Java的OIO和NIO

下面代码是使用java实现阻塞OIO的例子

package com.beidao.netty.javaio;import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;/*** Blocking networking without Netty* @author 0200759**/
public class PlainOioServer {public void server(int port) throws Exception {final ServerSocket socket = new ServerSocket(port);try{while(true){//accept connectionfinal Socket clientSocket = socket.accept();System.out.println("Accepted connection from" + clientSocket);//create new thread to handle connectionnew Thread(new Runnable() {public void run() {OutputStream out;try {out = clientSocket.getOutputStream();out.write("Hi\r\n".getBytes(Charset.forName("UTF-8")));out.flush();//close connection once message written and flushedclientSocket.close();} catch (Exception e) {try {clientSocket.close();} catch (Exception e2) {e2.printStackTrace();}}}}).start();}}catch (Exception e) {e.printStackTrace();socket.close();}}
}

下面代码是使用Java NIO实现的例子:

package com.beidao.netty.javaio;import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;public class PlainNioServer {public void server(int port) throws Exception {System.out.println("Listening for connections on port " + port);//open Selector that handles channelsSelector selector = Selector.open();//open ServerSocketChannelServerSocketChannel serverChannel = ServerSocketChannel.open();//get ServerSocketServerSocket serverSocket = serverChannel.socket();//bind server to portserverSocket.bind(new InetSocketAddress(port));//set to non-blockingserverChannel.configureBlocking(false);//register ServerSocket to selector and specify that it is interested in new accepted clientsserverChannel.register(selector, SelectionKey.OP_ACCEPT);final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());while (true) {//Wait for new events that are ready for process. This will block until something happensint n = selector.select();if(n > 0){//Obtain all SelectionKey instances that received eventsIterator iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey key = iter.next();iter.remove();try {//Check if event was because new client ready to get acceptedif (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();System.out.println("Accepted connection from "+ client);client.configureBlocking(false);//Accept client and register it to selectorclient.register(selector, SelectionKey.OP_WRITE,msg.duplicate());}//Check if event was because socket is ready to write dataif (key.isWritable()) {SocketChannel client = (SocketChannel) key.channel();ByteBuffer buff = (ByteBuffer) key.attachment();//write data to connected clientwhile (buff.hasRemaining()) {if (client.write(buff) == 0) {break;}}client.close();}} catch (Exception e) {key.cancel();key.channel().close();}}}}}
}

netty实现OIO和NIO

下面代码是使用Netty作为网络框架编写的一个阻塞IO例子:

package com.beidao.netty.io;import java.net.InetSocketAddress;import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.oio.OioSctpServerChannel;
import io.netty.util.CharsetUtil;public class NettyOioServer {public void server(int port) throws Exception {final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n",CharsetUtil.UTF_8));//事件循环组EventLoopGroup group = new NioEventLoopGroup();try {//用来引导服务器配置ServerBootstrap bootstrap = new ServerBootstrap();//使用OIO阻塞模式bootstrap.group(group).channel(OioSctpServerChannel.class).localAddress(new InetSocketAddress(port))//指定ChannelInitialer初始化handlers.childHandler(new ChannelInitializer() {@Overrideprotected void initChannel(Channel ch) throws Exception {//添加一个“入站”handle到ChannelPiplinech.pipeline().addLast(new ChannelInboundHandlerAdapter() {public void channelActive(ChannelHandlerContext ctx) throws Exception{//连接后,写消息到客户端,写完后关闭连接ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);}});}});//绑定服务器接受连接ChannelFuture future = bootstrap.bind().sync();future.channel().closeFuture().sync();} catch (Exception e) {//释放所有资源group.shutdownGracefully();}}
}

下面代码是使用Netty实现异步,可以看出使用Netty由OIO切换到NIO是非常的方便。

package com.beidao.netty.io;import java.net.InetSocketAddress;import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpServerChannel;
import io.netty.util.CharsetUtil;public class NettyNioServer {public void server(int port) throws Exception {final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n",CharsetUtil.UTF_8));//事件循环组EventLoopGroup group = new NioEventLoopGroup();try {//用来引导服务器配置ServerBootstrap bootstrap = new ServerBootstrap();//使用OIO阻塞模式bootstrap.group(group).channel(NioSctpServerChannel.class).localAddress(new InetSocketAddress(port))//指定ChannelInitialer初始化handlers.childHandler(new ChannelInitializer() {@Overrideprotected void initChannel(Channel ch) throws Exception {//添加一个“入站”handle到ChannelPiplinech.pipeline().addLast(new ChannelInboundHandlerAdapter() {public void channelActive(ChannelHandlerContext ctx) throws Exception{//连接后,写消息到客户端,写完后关闭连接ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);}});}});//绑定服务器接受连接ChannelFuture future = bootstrap.bind().sync();future.channel().closeFuture().sync();} catch (Exception e) {//释放所有资源group.shutdownGracefully();}}
}

Transport API

传输API的核心是Channel接口,它用于所有出站的操作。Channel接口的类层次结构如下:
image
如上图所示,每个Channel都会分配一个ChannelPipeline和ChannelConfig。ChannelConfig负责设置并存储配置,并允许在运行期间更新它们。传输一般有特定的配置设置,只作用于传输,没有其他的实现。ChannelPipeline容纳了使用的ChannelHandler实例,这些ChannelHandler将处理通道传递的“入站”和“出站”数据。ChannelHandler的实现允许你改变数据状态和传输数据。

现在我们可以使用ChannelHandler做下面一些事情:
- 传输数据时,将数据从一种格式转换到另一种格式
- 异常通知
- Channel变为有效或无效时获得通知
- Channel被注册或从EventLoop中注销时获得通知
- 通知用户特定事件

ChannelPipeline实现了拦截过滤器模式,这意味着我们连接不同的ChannelHandler来拦截并处理经过ChannelPipeline的数据或事件。可以把ChannelPipeline想象成UNIX管道,它允许不同的命令链(ChannelHandler相当于命令)。你还可以在运行时根据需要添加ChannelHandler实例到ChannelPipeline或从ChannelPipeline中删除,这能帮助我们构建高度灵活的Netty程序。此外,访问指定的ChannelPipeline和ChannelConfig,你能在Channel自身上进行操作。Channel提供了很多方法,如下列表:
- eventLoop(),返回分配给Channel的EventLoop
- pipeline(),返回分配给Channel的ChannelPipeline
- isActive(),返回Channel是否激活,已激活说明与远程连接对等
- localAddress(),返回已绑定的本地SocketAddress
- remoteAddress(),返回已绑定的远程SocketAddress
- write(),写数据到远程客户端,数据通过ChannelPipeline传输过去

Channel是线程安全(thread-safe),
下面的代码是一个简单的多线程例子:

final Channel channel = ...
//Create ByteBuf that holds data to write
final ByteBuf buf = Unpooled.copiedBuffer("your data",CharsetUtil.UTF_8);
//Create Runnable which writes data to channel
Runnable writer = new Runnable() {@Overridepublic void run() {channel.write(buf.duplicate());}
};
//Obtain reference to the Executor which uses threads to execute tasks
Executor executor = Executors.newChachedThreadPool();
// write in one thread
//Hand over write task to executor for execution in thread
executor.execute(writer);
// write in another thread
//Hand over another write task to executor for execution in thread
executor.execute(writer);

Netty包含的传输实现

Netty自带了一些传输协议的实现,虽然没有支持所有的传输协议,但是其自带的已足够我们来使用。Netty应用程序的传输协议依赖于底层协议,本节我们将学习Netty中的传输协议。

Netty中的传输方式有如下几种:
- NIO,io.netty.channel.socket.nio,基于java.nio.channels的工具包,使用选择器作为基础的方法。
- OIO,io.netty.channel.socket.oio,基于java.net的工具包,使用阻塞流。
- Local,io.netty.channel.local,用来在虚拟机之间本地通信。
- Embedded,io.netty.channel.embedded,嵌入传输,它允许在没有真正网络的运输中使用ChannelHandler,可以非常有用的来测试ChannelHandler的实现。


NIO

NIO传输是目前最常用的方式,它通过使用选择器提供了完全异步的方式操作所有的I/O,NIO从Java1.4才被提供。NIO中,我们可以注册一个通道或获得某个通道的改变的状态,通道状态有下面几种改变:
- 一个新的Channel被接受并已准备好
- Channel连接完成
- Channel中有数据并已准备好读取
- Channel发送数据出去

处理完改变的状态后需重新设置他们的状态,用一个线程来检查是否有已准备好的Channel,如果有则执行相关事件。在这里可能只同时一个注册的事件而忽略其他的。选择器所支持的操作在SelectionKey中定义,具体如下:
- OP_ACCEPT,有新连接时得到通知
- OP_CONNECT,连接完成后得到通知
- OP_READ,准备好读取数据时得到通知
- OP_WRITE,写入数据到通道时得到通知

Netty中的NIO传输就是基于这样的模型来接收和发送数据,通过封装将自己的接口提供给用户使用,这完全隐藏了内部实现。如前面所说,Netty隐藏内部的实现细节,将抽象出来的API暴露出来供使用,下面是处理流程图:
image

Netty中的NIO传输是“zero-file-copy”,也就是零文件复制,这种机制可以让程序速度更快,更高效的从文件系统中传输内容,零复制就是我们的应用程序不会将发送的数据先复制到JVM堆栈在进行处理,而是直接从内核空间操作。


OIO

OIO就是java中提供的Socket接口,java最开始只提供了阻塞的Socket,阻塞会导致程序性能低。
image


Local - In VM transport

Netty包含了本地传输,这个传输实现使用相同的API用于虚拟机之间的通信,传输是完全异步的。每个Channel使用唯一的
SocketAddress,客户端通过使用SocketAddress进行连接,在服务器会被注册为长期运行,一旦通道关闭,它会自动注销,客户端无法再使用它。

连接到本地传输服务器的行为与其他的传输实现几乎是相同的,需要注意的一个重点是只能在本地的服务器和客户端上使用它们。Local未绑定任何Socket,值提供JVM进程之间的通信。


Embedded transport

Embedded transport允许更容易的使用不同的ChannelHandler之间的交互,这也更容易嵌入到其他的ChannelHandler实例并像一个辅助类一样使用它们。它一般用来测试特定的ChannelHandler实现,也可以在ChannelHandler中重新使用一些ChannelHandler来进行扩展,为了实现这样的目的,它自带了一个具体的Channel实现,即:EmbeddedChannel。


推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
author-avatar
手机用户2602879975
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有