一、客户端代码及关键类说明
/**
* netty5的客户端
* @author -zhengzx-
*
*/
public class ClientSocket {
public static void main(String[] args) {
//服务类
Bootstrap bootstrap = new Bootstrap();
//worker
EventLoopGroup worker = new NioEventLoopGroup();
try {
//设置线程池
bootstrap.group(worker);
//设置socket工厂
bootstrap.channel(NioSocketChannel.class);
//设置管道
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ClientSocketHandler());
}
});
ChannelFuture connect = bootstrap.connect("127.0.0.1", 10101);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.println("请输入:");
String msg = bufferedReader.readLine();
connect.channel().writeAndFlush(msg);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
worker.shutdownGracefully();
}
}
}
【1】EventLoopGroup
: 客服端需要指定EvnetLoopGroup
,Netty5
中实例为NioEventLoopGroup
:表示一个NIO
的EvnetLoopGroup
。
【2】ChannelType
: 指定 Channel 的类型,客户端为NioSocketChannel
。在Netty
中,Channel
是一个Socket
的抽象,它为用户提供了关于Socket
状态(是否连接还是断开) 以及对Socket
的读写等操作。每当 Netty 建立了一个连接后, 都会有一个对应的 Channel
实例。
【3】Handler
: 设置数据的处理类。
public class ClientSocketHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("客户端接受消息:"+msg);
}
}
【4】ChannelPipeline
: 在实例化一个Channel
时,必然伴随着实例化一个ChannelPipeline
。
二、服务端代码及说明
【1】EventLoopGroup
: 不论是服务器端还是客户端,都必须指定EventLoopGroup
. 在这个例子中, 指定了NioEventLoopGroup
, 表示一个NIO
的EventLoopGroup
, 不过服务器端需要指定两个EventLoopGroup
, 一个是bossGroup
, 用于处理客户端的连接请求; 另一个是workerGroup
, 用于处理与各个客户端连接的IO
操作。
【2】ChannelType
: 指定Channel
的类型. 因为是服务器端, 因此使用了NioServerSocketChannel
。
【3】Handler
: 设置数据的处理器。
public class ServerSocketHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
//返回字符串
ctx.writeAndFlush("hi");
}
}
三、Netty5 与 Netty4/Netty3的区别
Netty4
和Netty5
的主要区别在于它们的版本更新以及一些新特性的添加。Netty5
是对Netty3
的后续版本,因此在架构上进行了一些重大调整,增加了许多新的功能和特性,使得其复杂性相较于Netty3
有所增加。然而,Netty4
与Netty5
的设计方式相似,因此它们之间的差异并不是特别大。简而言之,Netty5
提供了比Netty3
更多的高级功能和新特性,而Netty4
不再作为单独的版本维护,因为它与Netty5
的设计和实现相类似。
netty5
的复杂性相对于netty3
要多一些。架构基本被重构了。所以这里主要是介绍一些属性和用法。
核心的变化主要有:
【1】支持Android
,使得移动设备变的更加强大;
【2】通过Ice Cream Sandwich
解决了在ADK
中最著名的与NIO
和SSLEngine
相关的问题,且用户显然想要重用他们应用中的的编解码和处理器代码;
【3】我们决定官方支持Android
(4.0
及以上版本)
简化处理器层次:
【1】ChannelInboundHandler
和ChannelOutboundHandler
整合为ChannelHandler
。ChannelHandler
现在包含输入和输出的处理方法。
【2】ChannelInboundHandlerAdapter
,ChannelOutboundHandlerAdapter
和ChannelDuplexHandlerAdapter
已被废弃,由ChannelHandlerAdapter
代替。
【3】由于现在无法区分处理器handler)
是输入还是输出的处理器,CombinedChannelDuplexHandler
现在由ChannelHandlerAppender
代替。
Channel.deregister()
已被移除。不再生效和被使用。取而代之的,我们将允许Channel
被充注册到不同的事件循环。
ChannelHandlerContext.attr(..) == Channel.attr(..)
Channel和ChannelHandlerContext
类都实现了AttributeMap
接口,使用户可以在其上关联一个或多个属性。有时会让用户感到困惑的是Channel
和ChannelHandlerContext
都有其自己的存储用户定义属性的容器。例如,即使你通过Channel.attr(KEY_X).set(valueX)
给属性'KEY_X’
赋值,你却无法通过ChannelHandlerContext.attr(KEY_X).get()
方法获取到值。反之亦是如此。这种行为不仅仅令人不解而且还浪费内存。
为了解决这个问题,我们决定每个Channel
内部仅保留一个map
。AttributeMap
总是用AttributeKey
作为它的key
。AttributeKey
确保键的唯一性,因此每个Channel
中如果存在一个以上的属性容易是多余的。只要用户把他自己的AttributeKey
定义成ChannelHandler
的private static final
变量,就不会有出现重复key
的风险。
更简单更精确的缓冲区泄漏追踪: 之前,查找缓冲区泄漏是很困难的,并且泄漏的警告信息也不是很有帮助。现在我们有了增强的泄漏报告机制,该机制会在增长超过上限时触发。
PooledByteBufAllocator
成为默认的allocator
: 在4.x
版本中UnpooledByteBufAllocator
是默认的allocator
,尽管其存在某些限制。现在PooledByteBufAllocator
已经广泛使用一段时间,并且我们有了增强的缓冲区泄漏追踪机制,所以是时候让PooledByteBufAllocator
成为默认了。
全局唯一的Channel ID
: 每个Channel
现在有了全局唯一的ID
,其生成的依据是:
MAC
地址(EUI-48
或是EUI-64
),最好是全局唯一的进程ID
;System#currentTimeMillis()
;System#nanoTime()
;- 随机的
32
位整数,以及系列递增的32
位整数;
可通过Channel.id()
方法获取Channel
的ID
。
更灵活的线程模型: 增加了新的ChannelHandlerInvoker
接口,用于使用户可以选择使用哪个线程调用事件处理方法。替代之前的在向ChannelPipeline
添加ChannelHandler
时指定一个EventExecutor
的方式,使用该特性需要指定一个用户自定义的ChannelHandlerInvoker
实现。
EmbeddedChannel
的易用性: EmbeddedChannel
中的readInbound()
和readOutbound()
方法返回专门类型的参数,因此你不必在转换他们的返回值。这可以简化你的测试用例代码。
EmbeddedChannel ch = ...;
// BEFORE:
FullHttpRequest req = (FullHttpRequest) ch.readInbound();
// AFTER:
FullHttpRequest req = ch.readInbound();
使用Executor
代替ThreadFactory
: 有些应用要求用户使用Executor
运行他们的任务。4.x
版本要求用户在创建事件循环event loop
时指定ThreadFacotry
,现在不再是这样了。
Class loader
友好化: 一些类型,如AttributeKey
对于在容器环境下运行的应用是不友好的,现在不是了。
编解码和处理器handlers
:
XmlFrameDecoder
支持流式的XML
文档;- 二进制的
memcache
协议编解码; - 支持
SPDY/3.1
(也移植到了4.x
版本); - 重构了
HTTP
多部分的编解码;