Netty(一)核心概念
来源:互联网 发布:值机软件 编辑:程序博客网 时间:2024/05/20 02:29
Netty介绍
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
Netty框架的组成
第一个Netty程序
服务器端
/** * 服务端 */ public class Server { public static void main(String[] args) throws Exception { //1.创建两个线程组 //用于处理服务器端接收客户端连接 EventLoopGroup pGroup = new NioEventLoopGroup(); //用于进行网络通信 EventLoopGroup cGroup = new NioEventLoopGroup(); try { //2.创建辅助工具类,用于服务器通道的配置 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, cGroup) //绑定两个线程组 .channel(NioServerSocketChannel.class) //指定NIO模式 .option(ChannelOption.SO_BACKLOG, 1024) //设置tcp缓冲区 .option(ChannelOption.SO_SNDBUF, 32*1024) //设置发送缓冲大小 .option(ChannelOption.SO_RCVBUF, 32*1024) //设置接收缓冲大小 .option(ChannelOption.SO_KEEPALIVE, true) //保持连接 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //3.配置接收方法的处理 sc.pipeline().addLast(new ServerHandler()); } }); //4.进行绑定,调用sync()方法会阻塞直到服务器完成绑定 ChannelFuture cf = b.bind(9000).sync(); //5.等待关闭 cf.channel().closeFuture().sync(); } finally { //6.释放资源 pGroup.close(); cGroup.close(); } } }
服务器业务逻辑
/** * 服务器处理方法 */ public class ServerHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("服务器通道激活..."); } @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("Server: " + body); String response = "返回给客户端的响应: " + body; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("读取数据完毕"); ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
Handler很简单,它的每个方法都可以被重写,它的所有的方法中只有channelRead方法是必须要重写的。
实现服务器步骤:
- 配置服务器功能,如线程、端口
- 实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么
客户端
/** * 客户端 */ public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(new ClientHandler()); } }); ChannelFuture cf = b.connect("127.0.0.1",9000).sync(); //发送信息 cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello server".getBytes())); cf.channel().closeFuture().sync(); } finally{ group.shutdownGracefully(); } } }
客户端的业务逻辑
/** * 客户端处理方法 */ public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req,"utf-8"); System.out.println("Client: " + body); String response = "服务器端返回信息: " + body; } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { } }
- channelActive():客户端连接服务器后被调用
- channelRead():从服务器接收到数据后调用
- exceptionCaught():发生异常时被调用
Netty核心概念
- Bootstrap or ServerBootstrap
- EventLoop
- EventLoopGroup
- ChannelPipeline
- Channel
- Future or ChannelFuture
- ChannelInitializer
- ChannelHandler
ChannelInitializer
ChannelInitializer类用来配置Handlers。ChannelInitializer是通过ChannelPipeline来添加ChannelHandler的,如发送和接收消息,这些Handlers将确定发的是什么消息。ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己。
Future or ChannelFuture
Future注册一个监听,当操作成功或失败时会通知。ChannelFuture封装的是一个操作的相关信息,操作被执行时会立刻返回ChannelFuture。
Channel
EventLoop就是一个Channel执行实际工作的线程。EventLoop总是绑定一个单一的线程,在其生命周期内不会改变。当注册一个Channel后,Netty将这个Channel绑定到一个EventLoop,在Channel的生命周期内总是被绑定到一个EventLoop。
Bootstrap or ServerBootstrap
“引导”是Netty中配置程序的过程,当你需要连接客户端或服务器绑定指定端口时需要使用bootstrap。如前面所述,“引导”有两种类型,一种是用于客户端的Bootstrap(也适用于DatagramChannel),一种是用于服务端的ServerBootstrap。
差异:
- Bootstrap用来连接远程主机,有1个EventLoopGroup
- ServerBootstrap用来绑定本地端口,有2个EventLoopGroup
第一个差异很明显,“ServerBootstrap”监听在服务器监听一个端口轮询客户端的“Bootstrap”或DatagramChannel是否连接服务器。通常需要调用“Bootstrap”类的connect()方法,但是也可以先调用bind()再调用connect()进行连接,之后使用的Channel包含在bind()返回的ChannelFuture中。
第二个差别也许是最重要的。客户端bootstraps/applications使用一个单例EventLoopGroup,而ServerBootstrap使用2个EventLoopGroup(实际上使用的是相同的实例),它可能不是显而易见的,但是它是个好的方案。一个ServerBootstrap可以认为有2个channels组,第一组包含一个单例ServerChannel,代表持有一个绑定了本地端口的socket;第二组包含所有的Channel,代表服务器已接受了的连接。
EventLoop与EventLoopGroup
EventLoopGroup可以包含很多个EventLoop,每个Channel绑定一个EventLoop不会被改变,因为EventLoopGroup包含少量的EventLoop的Channels,很多Channel会共享同一个EventLoop。这意味着在一个Channel保持EventLoop繁忙会禁止其他Channel绑定到相同的EventLoop。我们可以理解为EventLoop是一个事件循环线程,而EventLoopGroup是一个事件循环集合。
ChannelPipeline与 ChannelHandler
ChannelPipeline的作用我们可以理解为用来管理ChannelHandler的一个容器,每个ChannelHandler处理各自的数据(例如入站数据只能由ChannelInboundHandler处理),处理完成后将转换的数据放到ChannelPipeline中交给下一个ChannelHandler继续处理,直到最后一个ChannelHandler处理完成。
ChannelHandler是一段执行业务逻辑处理数据的代码,它们来来往往的通过ChannelPipeline。
当一个ChannelHandler添加到ChannelPipeline中时获得一个ChannelHandlerContext。通常是安全的获得这个对象的引用,但是当一个数据报协议如UDP时这是不正确的,这个对象可以在之后用来获取底层通道,因为要用它来read/write消息,因此通道会保留。
Netty中发送消息有两种方法:直接写入通道或写入ChannelHandlerContext对象。
这两种方法的主要区别如下:
- 直接写入通道导致处理消息从ChannelPipeline的尾部开始
- 写入ChannelHandlerContext对象导致处理消息从ChannelPipeline的下一个handler开始
拆包、黏包
粘包、拆包的解决方案:
1. 将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑处理。
2. 在包尾部添加特殊字符进行分隔,分隔符类DellmiterBasedFrameDecoder(自定义分隔符)。
3. 消息定长,如果不够,空位补空格,FixedLengthFrameDecoder(定长) 。
消息定长
服务端
/** * 服务端 */ public class Server { public static void main(String[] args) throws Exception { //1.创建两个线程 EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); try { //2.创建服务器辅助类 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, cGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .option(ChannelOption.SO_SNDBUF, 32*1024) .option(ChannelOption.SO_RCVBUF, 32*1024) .option(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //设置定长字符串接收 sc.pipeline().addLast(new FixedLengthFrameDecoder(5)); //设置字符串形式的解码 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ServerHandler()); } }); //绑定连接 ChannelFuture cf = b.bind(9002).sync(); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); } finally { pGroup.shutdownGracefully(); cGroup.shutdownGracefully(); } } }
客户端
/** * 客户端 */ public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(new FixedLengthFrameDecoder(5)); sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ClientHandler()); } }); //绑定端口 ChannelFuture cf = b.connect("127.0.0.1",9002).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaaaabbbbbbb ".getBytes())); cf.channel().writeAndFlush(Unpooled.copiedBuffer("cccccc ".getBytes())); //等待客户端端口关闭 cf.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } }
服务端、客户端业务逻辑不变
尾部添加特殊字符
服务端
/** * 服务端 */ public class Server { public static void main(String[] args) throws Exception { //1.创建两个线程 //负责接收客户端的连接 EventLoopGroup pGroup = new NioEventLoopGroup(); //负责进行数据传输 EventLoopGroup cGroup = new NioEventLoopGroup(); try { //2创建服务辅助类 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, cGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .option(ChannelOption.SO_SNDBUF, 32*1024) .option(ChannelOption.SO_RCVBUF, 32*1024) .option(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //3.设置分隔符,解决拆包粘包问题 ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf)); //设置字符串形式的解码 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ServerHandler()); } }); //4.绑定连接 ChannelFuture cf = b.bind(9001).sync(); //5.等待服务器监听端口关闭 cf.channel().closeFuture().sync(); } finally { //6.释放资源 pGroup.close(); cGroup.close(); } } } /** * 服务器端处理方法 */ public class ServerHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("服务器端通道激活。。。"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String request = (String) msg; System.out.println("Server: " + msg); String response = "服务器响应: " + msg + "$_"; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
客户端
/** * 客户端 */ public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //处理分隔符 ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf)); sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ClientHandler()); } }); //连接服务端 ChannelFuture cf = b.connect("127.0.0.1",9001).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aa$_".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bb$_".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccc$_".getBytes())); //等待客户端端口关闭 cf.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } /** * 客户端处理方法 */ public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("客户端通道激活。。。"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { String response = (String) msg; System.out.println("Client: " + response); } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
编解码器
解码器负责将消息从字节或其他序列形式转成指定的消息对象,编码器则相反;解码器负责处理“入站”数据,编码器负责处理“出站”数据。
压缩解压
/** * 压缩解压工具类 */ public class GzipUtils { /** * 压缩 * @param data * @return * @throws Exception */ public static byte[] gzip(byte[] data) throws Exception{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] ret; try { GZIPOutputStream gzip = new GZIPOutputStream(bos); gzip.write(data); gzip.finish(); gzip.close(); ret = bos.toByteArray(); } finally { if (bos != null) { bos.close(); } } return ret; } /** * 解压 * @param data * @return * @throws Exception */ public static byte[] unzip(byte[] data) throws Exception{ ByteArrayInputStream bis = new ByteArrayInputStream(data); GZIPInputStream gzip = new GZIPInputStream(bis); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] ret; try { byte[] buf = new byte[1024]; int num = -1; while ((num = gzip.read(buf, 0, buf.length)) != -1) { bos.write(buf,0,num); } ret = bos.toByteArray(); bos.flush(); } finally { if (gzip != null) { gzip.close(); } if (bis != null) { bis.close(); } if (bos != null) { bos.close(); } } return ret; } }
Marshalling
Jboss Marshalling 是一个java对象序列化包,对jdk默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口兼容,同时增加了一些可调的参数和附加特性。
/** * Marshalling工厂 */ public class MarshallingCodecFactory { /** * 创建Jboss Marshalling解码器MarshallingDecoder * @return */ public static MarshallingDecoder buildMarshallingDecoder(){ //通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。 final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); //创建了MarshallingConfiguration对象,配置了版本号为5 final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); //根据marshallerFactory和configuration创建provider UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration); //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度 MarshallingDecoder decoder = new MarshallingDecoder(provider,1024 * 1024 * 1); return decoder; } /** * 创建Jboss MarshallingEncoder编码器 * @return */ public static MarshallingEncoder buildMarshallingEncoder(){ final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); final MarshallingConfiguration configuration = new MarshallingConfiguration(); configuration.setVersion(5); MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration); //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组 MarshallingEncoder encoder = new MarshallingEncoder(provider); return encoder; } }
JavaBean
/** * 请求消息 */ public class ReqData implements Serializable { private static final long serialVersionUID = 1L; private String id; private String name; private String requestMessage; private byte[] attachment; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRequestMessage() { return requestMessage; } public void setRequestMessage(String requestMessage) { this.requestMessage = requestMessage; } public byte[] getAttachment() { return attachment; } public void setAttachment(byte[] attachment) { this.attachment = attachment; } } /** * 响应信息 */ public class RespData implements Serializable{ private static final long serialVersionUID = 1L; private String id; private String name; private String responseMessage; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getResponseMessage() { return responseMessage; } public void setResponseMessage(String responseMessage) { this.responseMessage = responseMessage; } }
服务端
/** * 服务端 */ public class Server { public static void main(String[] args) throws Exception { EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(pGroup,cGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .handler(new LoggingHandler(LogLevel.INFO)) //设置日志 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder()); sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder()); sc.pipeline().addLast(new ServerHandler()); } }); //绑定端口 ChannelFuture cf = b.bind(9003).sync(); cf.channel().closeFuture().sync(); } finally { pGroup.close(); cGroup.close(); } } } /** * 服务端处理方法 */ public class ServerHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ReqData req= (ReqData) msg; System.out.println("Server: " + req.getId() + "," + req.getName() + "," + req.getRequestMessage()); byte[] attachment = GzipUtils.unzip(req.getAttachment()); String path = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "001.jpg"; FileOutputStream fos = new FileOutputStream(path); fos.write(attachment); fos.close(); RespData resp = new RespData(); resp.setId(req.getId()); resp.setName("resp" + req.getId()); resp.setResponseMessage("响应内容: " + req.getId()); ctx.writeAndFlush(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
客户端
/** * 客户端 */ public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); try { b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder()); sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder()); sc.pipeline().addLast(new ClientHandler()); } }); ChannelFuture cf = b.connect("127.0.0.1",9003).sync(); for (int i = 0; i < 6; i++) { ReqData req = new ReqData(); req.setId(""+i); req.setName("pro"+i); req.setRequestMessage("数据信息" + i); String path = System.getProperty("user.dir") + File.separatorChar + "sources" + File.separatorChar + "001.jpg"; File file = new File(path); FileInputStream in = new FileInputStream(file); byte[] data = new byte[in.available()]; in.read(data); in.close(); req.setAttachment(GzipUtils.gzip(data)); cf.channel().writeAndFlush(req); } cf.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } /** * 客户端处理方法 */ public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { RespData resp = (RespData) msg; System.out.println("Client:" + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage()); } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
应用场景
断开后重新连接
服务端,加上一行设置超时时间的代码,其它内容不变。
b.group(pGroup,cGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .handler(new LoggingHandler(LogLevel.INFO)) //设置日志 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder()); sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder()); //设置连接超时时间 sc.pipeline().addLast(new ReadTimeoutHandler(3)); sc.pipeline().addLast(new ServerHandler()); } });
客户端
/** * 客户端 */ public class Client { private static class SingletonHolder { static final Client instance = new Client(); } public static Client getInstance(){ return SingletonHolder.instance; } private EventLoopGroup group; private Bootstrap b; private ChannelFuture cf ; private Client(){ group = new NioEventLoopGroup(); b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingDecoder()); sc.pipeline().addLast(MarshallingCodecFactory.buildMarshallingEncoder()); //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用) sc.pipeline().addLast(new ReadTimeoutHandler(3)); sc.pipeline().addLast(new ClientHandler()); } }); } public void connect(){ try { this.cf = b.connect("127.0.0.1", 9003).sync(); System.out.println("远程服务器已经连接, 可以进行数据交换.."); } catch (Exception e) { e.printStackTrace(); } } public ChannelFuture getChannelFuture(){ if(this.cf == null){ this.connect(); } if(!this.cf.channel().isActive()){ this.connect(); } return this.cf; } public static void main(String[] args) throws Exception{ final Client c = Client.getInstance(); //c.connect(); ChannelFuture cf = c.getChannelFuture(); for(int i = 1; i <= 3; i++ ){ ReqData request = new ReqData(); request.setId("" + i); request.setName("pro" + i); request.setRequestMessage("数据信息" + i); cf.channel().writeAndFlush(request); TimeUnit.SECONDS.sleep(4); } cf.channel().closeFuture().sync(); new Thread(new Runnable() { @Override public void run() { try { System.out.println("进入子线程..."); ChannelFuture cf = c.getChannelFuture(); System.out.println(cf.channel().isActive()); System.out.println(cf.channel().isOpen()); //再次发送数据 ReqData request = new ReqData(); request.setId("" + 4); request.setName("pro" + 4); request.setRequestMessage("数据信息" + 4); cf.channel().writeAndFlush(request); cf.channel().closeFuture().sync(); System.out.println("子线程结束."); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); System.out.println("断开连接,主线程结束.."); } }
- Netty(一)核心概念
- 03 Netty核心概念
- Netty核心概念
- 第三篇:Netty核心概念
- Netty In Action中文版 - 第三章:Netty核心概念
- Netty In Action中文版 - 第三章:Netty核心概念
- netty in action第三章-netty的核心概念
- 【Netty in Action学习笔记】Netty核心组件概念
- Netty 学习笔记之二 Netty 核心概念
- Netty in Action (一) netty概念和架构
- netty学习笔记(1)_一些核心概念
- spring学习(一)________核心概念
- Spark入门(一):核心概念简介
- netty学习(一)——概念简介
- Netty(一):基础概念及消息处理流程
- Netty实战学习笔记(一)——Netty的概念及体系结构
- Netty的核心组件
- [netty]--核心源码类
- js控制浏览器后退按钮
- XMLHttpRequest
- Patrick’s blog is online now!
- nfs
- SublimeCodeIntel配置
- Netty(一)核心概念
- Python基础 for循环
- 装载、链接与库——main函数的前世今生
- pxe
- keras系列︱图像多分类训练与利用bottleneck features进行微调(三)
- LeetCodeOJ_001: Two Sum
- normalize.css使用方法
- Sql Server服务远程过程调用失败
- 个人学习总结一机器学习入门(十)