需要注意的是:
writeAndFlush(Object msg)
msg虽然是Object类型,却并不是什么类型都可以直接传,仅支持ByteBuf 和 FileRegion 类型
以下为demo源码:
引入依赖:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> </dependency>
服务端 Server类:
package com.example.learn.nio.netty; 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; /** * Netty Demo服务端 * */ public class NettyServer { private static String host = "localhost"; private static int port = 5555; public static void main(String[] args) { startNetty(); } public static void startNetty() { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { //服务端启动器 ServerBootstrap bootstrap = new ServerBootstrap(); //指定接收连接的和接收客户端数据的EventLoopGroup //boss用于连接,worker用于客户端数据接收 bootstrap.group(boss, worker); /* 设置参数说明: ChannelOption.SO_BACKLOG:对应的是tcp/ip协议listen函数中的backlog参数,函数listen(int socketfd,int backlog) 用来初始化服务端可连接队列,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候, 服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小 ChannelOption.SO_REUSEADDR:表示允许重复使用本地地址和端口 ChannelOption.SO_KEEPALIVE:对应于套接字选项中的SO_KEEPALIVE,该参数用于设置TCP连接,当设置该选项以后, 连接会测试链接的状态,这个选项用于可能长时间没有数据交流的连接。当设置该选项以后,如果在两小时内没有数据的通信时, TCP会自动发送一个活动探测数据报文。 ChannelOption.SO_SNDBUF:设置发送缓冲区的大小 ChannelOption.SO_RCVBUF:设置接收缓冲区的大小 ChannelOption.SO_LINGER:对应于套接字选项中的SO_LINGER,Linux内核默认的处理方式是当用户调用close()方法的时候,函数返回, 在可能的情况下,尽量发送数据,不一定保证会发生剩余的数据,造成了数据的不确定性,使用SO_LINGER可以阻塞close()的调用时间, 直到数据完全发送 ChannelOption.TCP_NODELAY:禁止使用Nagle算法,使用于小数据即时传输 ChannelOption.TCP_CORK:与NODELAY作用相反,该选项是需要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。 IP_TOS:IP参数,设置IP头部的Type-of-Service字段,用于描述IP包的优先级和QoS选项。 ALLOW_HALF_CLOSURE:Netty参数,一个连接的远端关闭时本地端是否关闭,默认值为False。值为False时,连接自动关闭;为True时, 触发ChannelInboundHandler的userEventTriggered()方法,事件为ChannelInputShutdownEvent。 */ bootstrap.option(ChannelOption.SO_KEEPALIVE, true); //指定通道类型 bootstrap.channel(NioServerSocketChannel.class); // 指定处理器 bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //把处理器放入管道 socketChannel.pipeline().addLast(new NettyServerHandler()); } }); // 绑定地址/端口,启动netty服务 ChannelFuture channelFuture = bootstrap.bind(host, port); try { //等待 server socket关闭 channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } finally { // 关闭所有Event loops以终止所有线程运行。 boss.shutdownGracefully(); worker.shutdownGracefully(); } } }
服务端Handler处理器类,用于处理客户端请求:
package com.example.learn.nio.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.nio.ByteBuffer; /** * 服务端处理器 * */ public class NettyServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; byte[] byteMsg = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(byteMsg); System.out.println(ctx.channel().remoteAddress()+"->Server :"+new String(byteMsg)); byteBuf.release(); ByteBuf b = ctx.alloc().buffer(1024); b.writeBytes(("Server write " + new String(byteMsg)).getBytes("UTF-8")); /* writeAndFlush(Object msg)方法参数msg仅支持 ByteBuf 和 FileRegion 类型 */ ctx.writeAndFlush(b); // super.channelRead(ctx, msg); } }
客户端Netty类:
package com.example.learn.nio.netty; import io.netty.bootstrap.Bootstrap; 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.NioSocketChannel; /** * netty demo客户端 * */ public class NettyClient { public static void main(String[] args) { String host = "localhost"; int port = 5555; EventLoopGroup workergroup = new NioEventLoopGroup(); try { //启动器 Bootstrap bootstrap = new Bootstrap(); // 设置用于连接服务端的 EventLoopGroup bootstrap.group(workergroup); //指定通道类型 bootstrap.channel(NioSocketChannel.class); //设置参数,具体参考服务端代码 bootstrap.option(ChannelOption.SO_KEEPALIVE, true); //设置处理器 bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //把处理器放入管道 socketChannel.pipeline().addLast(new NettyClientHandler()); } }); // 连接 ChannelFuture f = bootstrap.connect("localhost", 5555); try { // 等待 直到socket关闭 f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } finally { // 关闭所有event loops,以终止所有线程 workergroup.shutdownGracefully(); } } }
客户端处理器类,用于向服务端发送和接收消息:
package com.example.learn.nio.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * 客户端处理器 */ public class NettyClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("连接成功"); // ctx.fireChannelActive();// 若把这一句注释掉将无法将event传递给下一个ClientHandler String msg = "hello Server!"; ByteBuf b = ctx.alloc().buffer(4 * msg.length()); b.writeBytes(msg.getBytes()); ctx.write(b); ctx.flush(); b.release(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf b = (ByteBuf) msg; byte[] msgByte = new byte[b.readableBytes()]; b.readBytes(msgByte); System.out.println("收到信息:" + new String(msgByte)); b.release(); } }