Netty简单入门
什么是NettyNetty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性Netty应用场景分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。游戏开发中,底层使用netty通讯。为什么选择netty解决了NIO代码的复杂问题,容错机制问题netty-...
·
- 什么是Netty
Netty 是一个基于NIO的客户端、服务器端的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性 - Netty应用场景
分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。
游戏开发中,底层使用netty通讯。 - 为什么选择netty
解决了NIO代码的复杂问题,容错机制问题
- netty---服务端
- 创建maven工程,在pom.xml中引入依赖---开发中尽量用netty4.0以上版本
<dependency> <groupId>io.netty</groupId> <artifactId>netty</artifactId> <version>3.3.0.Final</version> </dependency>
- 创建NettyServer.java
import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.*; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.string.StringDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * netty服务端 * Create by wangxb * 2019-07-27 19:06 */ // 监听类 class ServerHanlder extends SimpleChannelHandler { // 通道被关闭的时候会触发 @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { super.channelClosed(ctx, e); System.out.println("channelClosed......................"); } // 必须要建立连接,关闭通道的时候才会触发 @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { super.channelDisconnected(ctx, e); System.out.println("channelDisconnected......................."); } // 接受出现异常 @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { super.exceptionCaught(ctx, e); System.out.println("exceptionCaught.............................."); } // 接受客户端数据.. @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { super.messageReceived(ctx, e); System.out.println("--------------------接受客户端数据--------------------"); System.out.println(e.getMessage()); System.out.println("-------------------------end---------------------------"); ctx.getChannel().write("客户端, 你好啊!"); } } // netty服务类 public class NettyServer { public static void main(String[] args) { // 1.创建服务对象 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 2.创建两个线程池 第一个 监听端口号 nio监听 ExecutorService boos = Executors.newCachedThreadPool(); ExecutorService wook = Executors.newCachedThreadPool(); // 3.将线程池放入工程 serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, wook)); // 4.设置管道工程 serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() { // 设置管道 public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline(); // 传输数据的时候直接为string类型 pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); // 设置事件监听类 pipeline.addLast("serverHanlder", new ServerHanlder()); return pipeline; } }); // 绑定端口号 serverBootstrap.bind(new InetSocketAddress(8080)); System.out.println("服务器端已经被启动....."); // while (true) { // try { // Thread.sleep(500); // } catch (Exception e) { // // TODO: handle exception // } // System.out.println("说明netty是异步的....."); // // } } }
- 测试:启动NettyServer.java的main方法
浏览器访问地址: http://127.0.0.1:8080/
- netty---客户端
- 创建NettyClient.java
import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.*; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.handler.codec.string.StringDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import java.net.InetSocketAddress; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * netty客户端 * Create by wangxb * 2019-07-27 19:52 */ // 监听类 class ClientHanlder extends SimpleChannelHandler{ // 通道被关闭的时候会触发 @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { super.channelClosed(ctx, e); System.out.println("channelClosed......................."); } // 必须要建立连接,关闭通道的时候才会触发 @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { super.channelDisconnected(ctx, e); System.out.println("channelDisconnected....................."); } // 接受出现异常 @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { super.exceptionCaught(ctx, e); System.out.println("exceptionCaught........................"); } // 接受客户端数据.. @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { super.messageReceived(ctx, e); System.out.println("服务器向客户端回复的内容------------------------------------"); System.out.println( e.getMessage()); } } public class NettyClient { public static void main(String[] args) { // 1.创建服务对象 ClientBootstrap clientBootstrap = new ClientBootstrap(); // 2.创建两个线程池 第一个 监听端口号 nio监听 ExecutorService boos = Executors.newCachedThreadPool(); ExecutorService wook = Executors.newCachedThreadPool(); // 3.将线程池放入工程 clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, wook)); // 4.设置管道工程 clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() { // 设置管道 public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline(); // 传输数据的时候直接为string类型 pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); // 设置事件监听类 pipeline.addLast("clientHanlder", new ClientHanlder()); return pipeline; } }); // 绑定端口号 ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080)); Channel channel = connect.getChannel(); System.out.println("client start"); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请输入内容:"); channel.write(scanner.next()); } } }
- 测试 启动服务端,启动客户端
- TCP粘包、拆包
- 粘包与拆包的概念
粘包: 将多个数据包封装成一个数据包。比如客户端发送了2条消息: a、b,服务端直接当成了一条消息读取: ab
拆包: 将一个数据包拆成了多个数据包。比如客户端发送了1条消息: hello,服务端当成了多条消息: h、e、l、l、0。
- 产生的原因
如图所示: 客户端发送数据到服务端,是先到缓冲区,服务端然后直接从缓冲区读取。
粘包产生原因: 客户端发送的多条数据小于缓存区大小,则服务端直接分1次从缓存区读取。
拆包产生原因: 客户端发送的数据大于缓存区的大小,则服务端直接分多次从缓存区读取。
还有其他的网络延迟等原因.....
- 解决办法:
1). 以固定的消息长度发送数据,到缓冲区
2). 可以在数据之间设置一些特殊字符(\n或者\r\n)
3). 利用编码器LineBaseDFrameDecoder解决tcp粘包的问题sc.pipeline().addLast(new FixedLengthFrameDecoder(10));
- 序列化与反序列化
- 产生原因
因为TCP/IP协议只支持字节数组的传输,不能直接传对象。所以网络传输中,客户端需要将对象转成字节数组进行发送,服务端再将接收的字节数组转成对象。主要用于网络传输、数据持久化等。
- 序列化与反序列化概念
序列化: 就是将对象转换为字节数组,一般也将序列化称为编码(Encode);
反序列化: 就是将网络、磁盘中接收的字节数组还原成对象,一般也将反序列化称为解码(Decode);
- 实现序列化的要求
只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常!
- 序列化ID的作用
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与对象中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。 - 几种常见序列化协议
a) JSON 客户端将对象转成json类型在网络传输,服务端获取json通过key和value再将其转为对象。可实现跨 语言
b) XML
c) Fastjson
d) Thrift.....
e) MessagePack
更多推荐
已为社区贡献1条内容
所有评论(0)