概述
一、初步了解
网络上讲的很多,看过很多网页进行了解,记住的就几点:
1.Netty是用来做网络通信的
2.Netty做网络通信比JAVA原生NIO好用,做了封装,用起来简单,而且没bug
3.Netty使用Reactor线程模型,至于Reactor线程模型是什么,看了一下,没太记住,等学习一段时间后再回过头来深入了解
二、实例
先从网上找个例子来体验一下,感谢@蚩尤后裔提供的例子(https://blog.csdn.net/wangmx1993328/article/details/83036285)。
这里使用MyEclipse导入二进制jar包的方式,jar包可在Netty 官网下载,下载后直接拷贝netty-all-4.1.36.Final.jar到项目目录,@蚩尤后裔提供的服务端和客户端代码,执行成功。
下面是服务端和客户端源码:
服务端:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Created by Administrator on 2017/5/16.
*/
public class TimeServer {
public static void main(String[] args) {
int port = 9898;
new TimeServer().bind(port);
}
public void bind(int port) {
/**
* interface EventLoopGroup extends EventExecutorGroup extends ScheduledExecutorService extends ExecutorService
* 配置服务端的 NIO 线程池,用于网络事件处理,实质上他们就是 Reactor 线程组
* bossGroup 用于服务端接受客户端连接,workerGroup 用于进行 SocketChannel 网络读写*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
/** ServerBootstrap 是 Netty 用于启动 NIO 服务端的辅助启动类,用于降低开发难度
* */
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
/**服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成*/
ChannelFuture f = b.bind(port).sync();
System.out.println(Thread.currentThread().getName() + ",服务器开始监听端口,等待客户端连接.........");
/**下面会进行阻塞,等待服务器连接关闭之后 main 方法退出,程序结束
*
* */
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**优雅退出,释放线程池资源*/
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new TimeServerHandler());
}
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Created by Administrator on 2017/5/16.
* ChannelInboundHandlerAdapter extends ChannelHandlerAdapter 用于对网络事件进行读写操作
*/
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
/**
* 收到客户端消息,自动触发
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
/**
* 将 msg 转为 Netty 的 ByteBuf 对象,类似 JDK 中的 java.nio.ByteBuffer,不过 ButeBuf 功能更强,更灵活
*/
ByteBuf buf = (ByteBuf) msg;
/**readableBytes:获取缓冲区可读字节数,然后创建字节数组
* 从而避免了像 java.nio.ByteBuffer 时,只能盲目的创建特定大小的字节数组,比如 1024
* */
byte[] reg = new byte[buf.readableBytes()];
/**readBytes:将缓冲区字节数组复制到新建的 byte 数组中
* 然后将字节数组转为字符串
* */
buf.readBytes(reg);
String body = new String(reg, "UTF-8");
System.out.println(Thread.currentThread().getName() + ",The server receive order : " + body);
/**回复消息
* copiedBuffer:创建一个新的缓冲区,内容为里面的参数
* 通过 ChannelHandlerContext 的 write 方法将消息异步发送给客户端
* */
String respMsg = "I am Server,消息接收 success!";
ByteBuf respByteBuf = Unpooled.copiedBuffer(respMsg.getBytes());
ctx.write(respByteBuf);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
/**flush:将消息发送队列中的消息写入到 SocketChannel 中发送给对方,为了频繁的唤醒 Selector 进行消息发送
* Netty 的 write 方法并不直接将消息写如 SocketChannel 中,调用 write 只是把待发送的消息放到发送缓存数组中,再通过调用 flush
* 方法,将发送缓冲区的消息全部写入到 SocketChannel 中
* */
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
/**当发生异常时,关闭 ChannelHandlerContext,释放和它相关联的句柄等资源 */
ctx.close();
}
}
客户端:
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* Created by Administrator on 2017/5/16.
*/
public class TimeClient {
/**
* 使用 3 个线程模拟三个客户端
*
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new MyThread()).start();
}
}
static class MyThread implements Runnable {
@Override
public void run() {
connect("127.0.0.1", 9898);
}
public void connect(String host, int port) {
/**配置客户端 NIO 线程组/池*/
EventLoopGroup group = new NioEventLoopGroup();
try {
/**Bootstrap 与 ServerBootstrap 都继承(extends)于 AbstractBootstrap
* 创建客户端辅助启动类,并对其配置,与服务器稍微不同,这里的 Channel 设置为 NioSocketChannel
* 然后为其添加 Handler,这里直接使用匿名内部类,实现 initChannel 方法
* 作用是当创建 NioSocketChannel 成功后,在进行初始化时,将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件*/
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
/**connect:发起异步连接操作,调用同步方法 sync 等待连接成功*/
ChannelFuture channelFuture = b.connect(host, port).sync();
System.out.println(Thread.currentThread().getName() + ",客户端发起异步连接..........");
/**等待客户端链路关闭*/
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**优雅退出,释放NIO线程组*/
group.shutdownGracefully();
}
}
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.logging.Logger;
/**
* Created by Administrator on 2017/5/17.
* 用于对网络事件进行读写操作
*/
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());
/**
* 当客户端和服务端 TCP 链路建立成功之后,Netty 的 NIO 线程会调用 channelActive 方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String reqMsg = "我是客户端 " + Thread.currentThread().getName();
byte[] reqMsgByte = reqMsg.getBytes("UTF-8");
ByteBuf reqByteBuf = Unpooled.buffer(reqMsgByte.length);
/**
* writeBytes:将指定的源数组的数据传输到缓冲区
* 调用 ChannelHandlerContext 的 writeAndFlush 方法将消息发送给服务器
*/
reqByteBuf.writeBytes(reqMsgByte);
ctx.writeAndFlush(reqByteBuf);
}
/**
* 当服务端返回应答消息时,channelRead 方法被调用,从 Netty 的 ByteBuf 中读取并打印应答消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println(Thread.currentThread().getName() + ",Server return Message:" + body);
ctx.close();
}
/**
* 当发生异常时,打印异常 日志,释放客户端资源
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
/**释放资源*/
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}
再次感谢@蚩尤后裔提供的教程!
最后
以上就是凶狠期待为你收集整理的Netty学习——初体验的全部内容,希望文章能够帮你解决Netty学习——初体验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复