java网络编程之Netty流数据的传输处理(五)
来源:互联网 发布:年均增长率简便算法 编辑:程序博客网 时间:2024/06/08 04:51
Netty流数据的传输处理
Socket Buffer的缺陷
对于例如TCP/IP这种基于流的传输协议实现,接收到的数据会被存储在socket的接受缓冲区内。不幸的是,这种基于流的传输缓冲区并不是一个包队列,而是一个字节队列。这意味着,即使你以两个数据包的形式发送了两条消息,操作系统却不会把它们看成是两条消息,而仅仅是一个批次的字节序列。因此,在这种情况下我们就无法保证收到的数据恰好就是远程节点所发送的数据。例如,让我们假设一个操作系统的TCP/IP堆栈收到了三个数据包:
由于这种流传输协议的普遍性质,在你的应用中有较高的可能会把这些数据读取为另外一种形式:
因此对于数据的接收方,不管是服务端还是客户端,应当重构这些接收到的数据,让其变成一种可让你的应用逻辑易于理解的更有意义的数据结构。在上面所述的这个例子中,接收到的数据应当重构为下面的形式:
第一种解决方案(使用特殊字符分割)
Netty提供了一个分隔符类DelimiterBasedFrameDecoder(自定义分隔符)
下面的开发我是居于我的Netty第一个开发程序来讲的,没看过我的这篇文章可以先看看,想信你在Netty第一个开发程序会捕获很多你想不到的知识。
服务端
public class Server { public static void main(String[] args) throws Exception{ //1 创建2个线程,一个是负责接收客户端的连接。一个是负责进行数据传输的 EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); //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) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //1 设置特殊分隔符 ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); //2 sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf)); //3 设置字符串形式的解码 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ServerHandler()); } }); //4 绑定连接 ChannelFuture cf = b.bind(8765).sync(); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); pGroup.shutdownGracefully(); cGroup.shutdownGracefully(); }}
关于EventLoopGroup、ServerBootstrap等等之类的我都在Netty的第一个程序都讲得很清楚了,需要了解的可以参考我的第一篇文章。
代码说明:
1、 Unpooled.copiedBuffer(“$_”.getBytes()) 这个是设置特殊分隔符返回的是Netty中的ByteBuf类型这里我设置的是 $_
2、DelimiterBasedFrameDecoder()是处理分隔符的类
3、StringDecoder() 设置字符串形式的解码
注意这里使用了StringDecoder()解码成字符串形式,并不像在“Netty的第一个程序”那种方式去转换成字符串。
服务端业务处理
public class ServerHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(" server channel active... "); } @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())); }}
这里没什么可说的!看过我的Netty的第一个程序这篇文章大家都懂。
由于在服务端就使用了StringDecoder()解码成字符串形式,这里不需要用ByteBuf去转换成字符串。
客户端
public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //1 ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); //2 sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf)); //3 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ClientHandler()); } }); ChannelFuture cf = b.connect("127.0.0.1", 8765).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("777$_".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("666$_".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("888$_".getBytes())); //等待客户端端口关闭 cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
由于这里客户端也接收服务端返回的数据所以也采用了与服务端一样的处理方式。
如果你看过我的Netty的第一个程序文章,你会发现当时我是休眠1s再进行发送另一条的。到这目前你应该也知道我什么这样做了吧!
客户端业务处理
public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client channel active... "); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { String response = (String)msg; System.out.println("Client: " + response); } finally { ReferenceCountUtil.release(msg); } }}
这里没什么可说的!看过我的Netty的第一个程序这篇文章大家都懂。
好!到这第一种解决方案就编写结束了,先启动服务端,再启动客户端
客户端打印如下:
客户端签到后服务端的打印如下:
源码地址:https://github.com/hfbin/Thread_Socket/tree/master/Socket/ende–1
第二种解决方案(定长)
Netty提供了一个定长类FixdeLengthFraneDecoder
使用这个定长的有个弊端:如果由多个字段比如可变长度的字段组成时这个时候并解决不了什么问题,建议使用第一个解决方案。
FixdeLengthFraneDecoder的使用跟DelimiterBasedFrameDecoder差不多,由于代码都差不多一样这里我不做太多的说明。
服务端
public class Server { public static void main(String[] args) throws Exception{ //创建2个线程,一个是负责接收客户端的连接。一个是负责进行数据传输的 EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); //创建服务器辅助类 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) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //1 设置定长字符串接收 sc.pipeline().addLast(new FixedLengthFrameDecoder(3)); //2 设置字符串形式的解码 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ServerHandler()); } }); //4 绑定连接 ChannelFuture cf = b.bind(8765).sync(); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); pGroup.shutdownGracefully(); cGroup.shutdownGracefully(); }}
1、FixedLengthFrameDecoder(3) 这里设置定长字符串接收具体设置多长自己定
2、StringDecoder() 设置字符串形式的解码
服务端业务处理
public class ServerHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(" server channel active... "); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String request = (String)msg; System.out.println("Server :" + msg); String response = request ; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); }}
服务端
public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); 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(3)); sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ClientHandler()); } }); ChannelFuture cf = b.connect("127.0.0.1", 8765).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("777".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("666".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("888".getBytes())); //等待客户端端口关闭 cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
客户端业务处理
public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client channel active... "); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { String response = (String)msg; System.out.println("Client: " + response); } finally { ReferenceCountUtil.release(msg); } }}
好!到这第二种解决方案就编写结束了,先启动服务端,再启动客户端
客户端打印如下:
客户端签到后服务端的打印如下:
源码地址:https://github.com/hfbin/Thread_Socket/tree/master/Socket/ende–2
- java网络编程之Netty流数据的传输处理(五)
- Netty5用户手册之五:netty中流数据的传输处理问题
- netty 流数据的传输处理
- java网络编程之Netty第一个程序(四)
- java网络编程之Netty编解码技术(六)
- java网络编程之Netty实战数据通信(七)
- java网络编程之Netty实战心跳检测(八)
- 网络TCp数据的传输设计(黏包处理)
- 浅析Java网络编程之UDP传输(一)
- JAVA 网络编程(7) Netty 处理Http协议 示例
- Java网络编程之传输控制协议
- Java网络编程之传输控制协议
- java网络编程之传输协议
- socket编程解决网络数据的传输
- Netty之网络连接处理
- Netty学习-Java网络编程
- 基于Netty的RPC简单框架实现(四):Netty实现网络传输
- Java网络编程之Netty入门案例-yellowcong
- 中国省市区三级联动实例(AJAX实现)
- 安装新版本Cmake
- 数据分析要点笔记
- 实习阶段性总结
- JSTL标签库之核心标签
- java网络编程之Netty流数据的传输处理(五)
- Linux下添加自定义脚本到开机自启动的方法
- ubuntu修改默认root密码
- Flask 扩展 Flask-Script
- 内存分配粒度vs内存分页大小
- SD-如何增强VF04显示交货单相关的发票金额
- leetcode 147. Insertion Sort List 148. Sort List
- elasticsearch排序(相关性排序score)
- C#SOCKET异步通信