Netty(二)TCP粘包、拆包和UDP通信
来源:互联网 发布:安卓手机系统优化软件 编辑:程序博客网 时间:2024/06/02 05:16
Netty基本实现
Netty实现通信的步骤:
1. 创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写。
2. 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。
3. 创建一个实际处理的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、以及实际处理数据的接口。
4. 绑定端口,执行同步阻塞方法等待服务器端启动即可。
public class ServerHandler extends ChannelHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// ((ByteBuf) msg).release(); //do something ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据 buf.readBytes(bytes); String request = new String(bytes,"utf-8"); System.out.println("Server : " + request); //写给客户端 String response = "服务器端响应数据:888"; ctx.write(Unpooled.copiedBuffer(response.getBytes())) //write方法会帮助释放消息,可以不用finally释放消息 .addListener(ChannelFutureListener.CLOSE); //监听,把数据发送后主动断开与客户端的连接 ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}public class Server { public static void main(String[] args) throws InterruptedException { //1.第一个线程组用于接收client端连接 EventLoopGroup bossGroup = new NioEventLoopGroup(); //2.第二个线程组用于实际业务处理 EventLoopGroup workGroup = new NioEventLoopGroup(); //3.创建一个辅助类BootStrap,就是对server进行一系列配置 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(new ServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture future = b.bind(6666).sync(); //阻塞 future.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); }}public class ClientHandler extends ChannelHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据 buf.readBytes(bytes); String request = new String(bytes, "utf-8"); System.out.println("Client : " + request); } finally { ReferenceCountUtil.release(msg);//释放消息 } } @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(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(new ClientHandler()); } }); ChannelFuture cf = b.connect("127.0.0.1",6666).sync(); //buf// cf.channel().write(Unpooled.copiedBuffer("777".getBytes()));// cf.channel().flush(); cf.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes())); //阻塞 cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
TCP粘包、拆包问题
TCP是一个“流”协议,所谓流就是没有界限的遗传数据。TCP底层并不了解上层的业务数据具体的含义,它会跟进TCP缓冲区的实际情况进行包的划分,在业务上,我们一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的TCP粘包、拆包问题。
TCP粘包、拆包问题的产生原因:
1. 应用程序write写入的字节大小大于套接口发送缓冲区的大小
2. 进行MSS大小的TCP分段
3. 以太网帧的payload大于MTU进行IP分片
粘包、拆包主流解决方案:
1. 消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格;
2. 在包尾部增加特殊字符进行分割,例如加回车等;
3. 将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理
Netty解决粘包、拆包问题:
1. 分隔符类 DElimiterBasedFrameDecoder(自定义分隔符)
2. FixedLengthFrameDecoder(定长)
public class ServerHandler1 extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("server channel active..."); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String request = (String) msg; System.out.println("Server: " + request ); String response = "服务器端响应:" + msg + "$_"; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}public class Server1 { public static void main(String[] args) throws InterruptedException { //1.创建线程,接收客户端 EventLoopGroup pGroup = new NioEventLoopGroup(); //2.创建线程,用于处理数据传输 EventLoopGroup wGroup = new NioEventLoopGroup(); //3.创建服务器辅助类 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, wGroup) .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 { //设置特殊分隔符 ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes()); sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf)); //设置字符串形式的编码 sc.pipeline().addLast(new StringDecoder()); sc.pipeline().addLast(new ServerHandler1()); } }); //4.绑定连接 ChannelFuture cf = b.bind(6666).sync(); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); pGroup.shutdownGracefully(); wGroup.shutdownGracefully(); }}public class ClientHandler1 extends ChannelHandlerAdapter{ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client active.."); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { String response = (String) msg; System.out.println("client: " + msg); } finally { ReferenceCountUtil.release(msg);//释放消息 } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }}public class Client1 { public static void main(String[] args) throws InterruptedException { //1.创建线程组 EventLoopGroup group = new NioEventLoopGroup(); //2.创建客户端辅助类 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 ClientHandler1()); } }); //3.连接服务器 ChannelFuture cf = b.connect("127.0.0.1", 6666).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bbbb$_".getBytes())); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccc$_".getBytes())); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
public class ServerHandler1 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 : " + request); String response = request; ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes())); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }}public class Server1 { public static void main(String[] args) throws InterruptedException { //1.创建线程,管理连接 EventLoopGroup pGroup = new NioEventLoopGroup(); //2.创建线程,处理数据传输 EventLoopGroup wGroup = new NioEventLoopGroup(); //3.创建服务端辅助类 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, wGroup) .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 ServerHandler1()); } }); //4.绑定端口 ChannelFuture cf = b.bind(6666).sync(); //等待服务器监听端口关闭 cf.channel().closeFuture().sync(); pGroup.shutdownGracefully(); wGroup.shutdownGracefully(); }}public class ClientHandler1 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: " + msg); } finally { ReferenceCountUtil.release(msg);//释放消息 } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }}public class Client1 { public static void main(String[] args) throws InterruptedException { //1.创建线程 EventLoopGroup group = new NioEventLoopGroup(); //2.创建辅助类 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 ClientHandler1()); } }); //3.连接服务端 ChannelFuture cf = b.connect("127.0.0.1", 6666).sync(); cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaabbbb".getBytes())); cf.channel().writeAndFlush(Unpooled.copiedBuffer("ccccccc".getBytes())); //等待客户端关闭 cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
Netty编解码
可以使用java进行对象序列化,netty去传输,但是java序列化的缺点多,比如java序列化没法跨语言、序列化后码流太大、序列化性能太低等等。
主流的编解码框架:
JBoss的Marshalling包
google的Protobuf
基于Protobuf的Kyro
MessagePack框架
Jboss Marshalling是一个java对象序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特性。
工具类
/** * 解压缩工具类 */public class GzipUtil { private final static Logger logger = Logger.getLogger(GzipUtil.class); public static byte[] gzip(byte[] data) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gzipos = null; byte[] result = null ; try { gzipos = new GZIPOutputStream(baos); gzipos.write(data); gzipos.finish(); result = baos.toByteArray(); } catch (IOException e) { logger.error("压缩文件失败!",e); throw new IOException(); } finally { if (gzipos != null) { try { gzipos.close(); } catch (IOException e) { e.printStackTrace(); } } if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } gzipos = null; baos = null; } return result; } public static byte[] ungzip(byte[] data) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(data); GZIPInputStream gzipis = null; ByteArrayOutputStream baos = null; byte[] result = null; try { gzipis = new GZIPInputStream(bais); byte[] buf = new byte[1024]; int num = -1; baos = new ByteArrayOutputStream(); while ((num = gzipis.read(buf, 0, buf.length)) != -1) { baos.write(buf, 0, num); } result = baos.toByteArray(); baos.flush(); } catch (IOException e) { logger.error("解压文件失败!",e); throw new IOException(); } finally { if (gzipis != null) { try { gzipis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bais != null) { try { bais.close(); } catch (IOException e) { e.printStackTrace(); } } if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } gzipis = null; bais = null; baos = null; } return result; } //测试 public static void main(String[] args) throws Exception { //文件路径 String readPath = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg"; File file = new File(readPath); FileInputStream fis = new FileInputStream(file); byte[] data = new byte[fis.available()]; fis.read(data); fis.close(); System.out.println("文件原始大小:" + data.length); //文件压缩 byte[] result = GzipUtil.gzip(data); System.out.println("文件压缩后大小:" + result.length); //文件解压 byte[] ungzipFile = GzipUtil.ungzip(result); System.out.println("文件解压后大小:" + ungzipFile.length); //写出文件 String writePath = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg"; FileOutputStream fos = new FileOutputStream(writePath); fos.write(ungzipFile); fos.close(); }}/** * Marshalling工厂 */public final class MarshallingCodeCFactory { /** * 创建Jboss Marshalling解码器MarshallingDecoder * @return MarshallingDecoder */ 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 Marshalling编码器MarshallingEncoder * @return MarshallingEncoder */ 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; }}
Netty
public class Request 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 Response 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 ServerHandlerExt extends ChannelHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Req req = (Req) msg; System.out.println("Server:" + req.getId() + "," + req.getName() + "," + req.getRequestMessage()); byte[] attachment = GzipUtil.gzip(req.getAttachment()); String path = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg"; FileOutputStream fos = new FileOutputStream(path); fos.write(attachment); fos.close(); Resp resp = new Resp(); resp.setId(req.getId()); resp.setName("resp" + req.getId()); resp.setResponseMessage("响应内容:" + req.getId()); ctx.writeAndFlush(req);//.addListener(ChannelFutureListener.CLOSE) }}public class ServerExt { public static void main(String[] args) throws InterruptedException { //创建两个现场 EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup wGroup = new NioEventLoopGroup(); //创建辅助类 ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, wGroup) .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(MarshallingFactory.buildMarshallingEncoder()); sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder()); sc.pipeline().addLast(new ServerHandlerExt()); } }); b.bind(6666).channel().closeFuture().sync(); //关闭连接 pGroup.shutdownGracefully(); wGroup.shutdownGracefully(); }}public class ClientHandlerExt extends ChannelHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { Resp resp = (Resp) msg; System.out.println("Client:" + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage()); } finally{ ReferenceCountUtil.release(msg);//释放消息 } }}public class ClientExt { 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(MarshallingFactory.buildMarshallingEncoder()); sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder()); sc.pipeline().addLast(new ClientHandlerExt()); } }); ChannelFuture cf = b.connect("127.0.0.1", 6666).sync(); for (int i = 0; i < 5; i++) { Req req = new Req(); req.setId("" + i); req.setName("pro" + i); req.setRequestMessage("数据信息" + i); String path = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg"; File file = new File(path); FileInputStream fis = new FileInputStream(file); byte[] data = new byte[fis.available()]; fis.read(data); fis.close(); req.setAttachment(GzipUtil.gzip(data)); cf.channel().writeAndFlush(req); } cf.channel().closeFuture().sync(); group.shutdownGracefully(); }}
Netty的UDP协议
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。
public class ServerHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{ // 谚语列表 private static final String[] DICTIONARY = { "只要功夫深,铁棒磨成针。", "旧时王谢堂前燕,飞入寻常百姓家。", "洛阳亲友如相问,一片冰心在玉壶。", "一寸光阴一寸金,寸金难买寸光阴。", "老骥伏枥,志在千里。烈士暮年,壮心不已!" }; private String nextQuote(){ int quoteId = ThreadLocalRandom.current().nextInt(DICTIONARY.length); return DICTIONARY[quoteId]; } protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { String req = packet.content().toString(CharsetUtil.UTF_8); System.out.println(req); if ("谚语字典查询?".equals(req)) { ctx.writeAndFlush( new DatagramPacket(Unpooled.copiedBuffer("谚语查询结果:"+ nextQuote(), CharsetUtil.UTF_8),packet.sender())); } }}public class ServerExt { public void run(int port) throws InterruptedException{ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new ServerHandlerExt()); b.bind(port).sync().channel().closeFuture().await(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new ServerExt().run(6666); new ServerExt().run(8888); }}public class ClientHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{ @Override protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { String response = packet.content().toString(CharsetUtil.UTF_8); if (response.startsWith("谚语查询结果:")) { System.out.println(response); ctx.close(); } }}public class ClientExt { public void run(int port) throws InterruptedException{ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BACKLOG, 1024) .handler(new ClientHandlerExt()); Channel channel = b.bind(0).sync().channel(); // 向网段内的所有机器广播UDP消息 channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("谚语字典查询?", CharsetUtil.UTF_8), new InetSocketAddress("255.255.255.255", port))).sync(); if (!channel.closeFuture().await(15000)) { System.out.println("查询超时!"); } } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new ClientExt().run(6666); }}
Netty的WebSocket
webSocket将网络套接字引入到客户端和服务端,WebSocke特点:
- 单一的tcp连接,双方可通信
- 对代理、防火墙和路由器透明
- 无头部信息、Cookie和身份验证
- 无安全开销
- 通过ping/pong帧保持链路激活
- 服务器可主动传递消息给客户端,不再需要客户端轮询
网络超时设置
public class Server { public static void main(String[] args) throws Exception{ EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); 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>() { protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder()); sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder()); sc.pipeline().addLast(new ReadTimeoutHandler(5)); //网络读写超时设置 sc.pipeline().addLast(new ServerHandler()); } }); ChannelFuture cf = b.bind(8765).sync(); cf.channel().closeFuture().sync(); pGroup.shutdownGracefully(); cGroup.shutdownGracefully(); }}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(5)); sc.pipeline().addLast(new ClientHandler()); } }); } public void connect(){ try { this.cf = b.connect("127.0.0.1", 8765).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++ ){ Request request = new Request(); 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()); //再次发送数据 Request request = new Request(); 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(二)TCP粘包、拆包和UDP通信
- Netty实践(二):TCP拆包、粘包问题
- Netty权威指南之TCP粘包和拆包
- Netty中处理TCP粘包和拆包
- Netty (三) TCP粘包和拆包解决方案
- Netty学习(四)-TCP粘包和拆包
- 关于TCP 和 Netty 拆包 粘包
- Netty学习之---TCP粘包和拆包
- Netty中处理TCP粘包和拆包
- Netty【三】 TCP 粘包和拆包
- Netty -- TCP粘包/拆包
- Netty 权威指南笔记(三):TCP 粘包和拆包
- 网络通信框架Netty的TCP粘包/拆包解决方案
- 【Netty4.x】Netty TCP粘包/拆包问题的解决办法(二)
- netty源码分析(二十四)TCP粘包与拆包实例演示及分析
- Netty解决半包(TCP粘包/拆包导致)读写问题
- Netty解决半包(TCP粘包/拆包导致)读写问题
- Netty解决半包(TCP粘包/拆包导致)读写问题
- mysql怎么完全卸载重装
- 并查集模板
- 递推的专题竞赛
- 关于eclipse中没有js代码提示的解决
- 优先队列模板
- Netty(二)TCP粘包、拆包和UDP通信
- 筛素数模板
- 处理输入成绩异常
- js特效制作页面访问量无IP数量统计累加特效
- ImageNet Classification with Deep Convolutional Neural Networks 论文笔记
- U盘安装 Ubuntu Server 14:04 遇到NO CD-ROM 解决办法
- POJ 3261 Milk Patterns(二分+后缀数组)
- Linux strace命令
- 条件运算符的应用