java开发中若要实现一个客户端与服务端通信的程序,传统方式是使用IO编程,为了提升性能开始使用NIO,为了更简单的编写网络程序,出现了Netty。 在传统的IO方式中,每个连接创建成功后都需要由一个线程来维护,每个线程中都包含一个while死循环,有多少个连接,则需要多少个线程及对应的while死循环。 在NIO方式中,新的连接不再创建一个新的线程来维护,而是把这个连接直接绑定到某个固定的线程,然后这个连接所有的读性都由这个线程来负责。 IO读写是面向 流 的,一次性只能从流中读取一字节或多字节,并且读完之后流无法再读取,需要自己缓存数据,而NIO的读写是面向 Buffer 的,可以随意读取里面任何字节数据,不需要自己缓存数据,只需要移动读写指针即可。
生活场景(来自闪电侠学Netty) 在一家幼儿园里,小朋友有上厕所的需求,小朋友都太小以至于你要问他要不要上厕所,他才会告诉你。幼儿园一共有100个小朋友,有两种方案可以解决小朋友上厕所的问题。 1.每个小朋友都配一个老师。每个老师都隔段时间询问小朋友是否要上厕所。如果要上,就领他去厕所,100个小朋友就需要100个老师来询问,并且每个小朋友上厕所的时候都需要一个老师领着他去,这就是IO模型,一个连接对应一个线程。 2.所有的小朋友都配同一个老师。这个老师隔段时间询问所有的小朋友是否有人要上厕所,然后每一时刻把所有要上厕所的小朋友批量领到厕所,这就是NIO模型。所有小朋友都注册到同一个老师,对应的就是所有的连接都注册到同一个线程,然后批量轮询。 这就是NIO模型解决线程资源受限问题的方案。在实际开发过程中,我们会开多个线程,每个线程都管理着一批连接,相对于IO模型中一个线程管理一个连接,消耗的线程资源大幅减少。
NIO方式由于复杂,编程不友好,而Netty的出现则封装了NIO,可以简单方便的编程,不用写一大堆复杂代码了。 Netty:是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务端和客户端。 实例 引入maven依赖
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
</dependency>
服务端
public class NettyServer {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
serverBootstrap
.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
.bind(8000);
}
}
客户端
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder());
}
});
Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
while (true) {
channel.writeAndFlush(new Date() + ": hello world!");
Thread.sleep(2000);
}
}
}
先启动服务端,再启动客户端,发现服务端控制台每2秒收到一条客户端发来的消息。
|