netty服务端、客户端简单搭建
maven依赖
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.72.Final</version> </dependency>
服务端
NettyServer.java
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import lombok.extern.slf4j.Slf4j; /** * netty server */ @Slf4j public class NettyServer { public void run(int port) { //EventLoopGroup是用来处理IO操作的多线程事件循环器 //负责接收客户端连接线程 EventLoopGroup bossGroup = new NioEventLoopGroup(); //负责处理客户端i/o事件、task任务、监听任务组 EventLoopGroup workerGroup = new NioEventLoopGroup(); //启动 NIO 服务的辅助启动类 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup); //配置 Channel bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new ServerIniterHandler()); //BACKLOG用于构造服务端套接字ServerSocket对象, // 标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度 bootstrap.option(ChannelOption.SO_BACKLOG, 1024); //是否启用心跳保活机制 bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); try { //绑定服务端口监听 Channel channel = bootstrap.bind(port).sync().channel(); log.info("netty server run is port " + port); //服务器关闭监听 /*channel.closeFuture().sync()实际是如何工作: channel.closeFuture()不做任何操作,只是简单的返回channel对象中的closeFuture对象,对于每个Channel对象,都会有唯一的一个CloseFuture,用来表示关闭的Future, 所有执行channel.closeFuture().sync()就是执行的CloseFuturn的sync方法,从上面的解释可以知道,这步是会将当前线程阻塞在CloseFuture上*/ channel.closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { //关闭事件流组 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) { new NettyServer().run(8899); } }
ServerHandle.java
import com.ithailin.iot.socket.MyWebSocket; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.GlobalEventExecutor; /** * 服务器的处理逻辑 */ public class ServerHandler extends SimpleChannelInboundHandler<String> { /** * 所有的活动用户 */ public static final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); public static MyWebSocket myWebSocket; /** * 读取消息通道 * * @param context * @param msg * @throws Exception */ @Override protected void channelRead0(ChannelHandlerContext context, String msg) throws Exception { Channel channel = context.channel(); //当有用户发送消息的时候,对其他的用户发送消息 for (Channel ch : group) { if (ch == channel) { //此处是当前发送的客户端 ch.writeAndFlush("[you]: " + msg + "\n"); } else { //此处是其他客户端显示 ch.writeAndFlush("[" + channel.remoteAddress() + "]: " + msg + "\n"); } } //此处是服务器显示 System.out.println("[" + channel.remoteAddress() + "]: " + msg + "\n"); } /** * 处理新加的消息通道 * * @param ctx * @throws Exception */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); group.add(channel); for (Channel ch : group) { if (ch == channel) { ch.writeAndFlush("[" + channel.remoteAddress() + "] coming \n"); } } } /** * 处理退出消息通道 * @param ctx * @throws Exception */ @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); for (Channel ch : group) { if (ch == channel) { ch.writeAndFlush("[" + channel.remoteAddress() + "] leaving"); } } group.remove(channel); } /** * 在建立连接时发送消息 * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); boolean active = channel.isActive(); if (active) { System.out.println("[" + channel.remoteAddress() + "] is online"); } else { System.out.println("[" + channel.remoteAddress() + "] is offline"); } ctx.writeAndFlush("[server]: welcome"); } /** * 退出时发送消息 * @param ctx * @throws Exception */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); if (!channel.isActive()) { System.out.println("[" + channel.remoteAddress() + "] is offline"); } else { System.out.println("[" + channel.remoteAddress() + "] is online"); } } /** * 异常捕获 * * @param ctx * @param e * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception { Channel channel = ctx.channel(); System.out.println("[" + channel.remoteAddress() + "] leave the room"); ctx.close().sync(); } }
ServerIniterHandle.java
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 服务器初始化 */ public class ServerIniterHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //管道注册handler ChannelPipeline pipeline = socketChannel.pipeline(); //编码通道处理 pipeline.addLast("decode", new StringDecoder()); //转码通道处理 pipeline.addLast("encode", new StringEncoder()); //聊天服务通道处理 pipeline.addLast("chat", new ServerHandler()); } }
客户端
NettyClient.java
import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * Netty client */ @Slf4j public class NettyClient { private String ip; private int port; private boolean stop = false; public NettyClient(String ip, int port) { this.ip = ip; this.port = port; } public void run() throws IOException { //设置一个多线程循环器 EventLoopGroup workerGroup = new NioEventLoopGroup(); //启动附注类 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(workerGroup); //指定所使用的NIO传输channel bootstrap.channel(NioSocketChannel.class); //指定客户端初始化处理 bootstrap.handler(new ClientIniterHandler()); try { //连接服务 Channel channel = bootstrap.connect(ip, port).sync().channel(); while (true) { //向服务端发送内容 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String content = reader.readLine(); if (!StringUtils.isEmpty(content)) { if (content.equalsIgnoreCase("quit")) { System.exit(1); } channel.writeAndFlush(content); } } } catch (InterruptedException e) { e.printStackTrace(); System.exit(1); } finally { workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { new NettyClient("127.0.0.1", 8899).run(); } }
ClientHandler.java
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 客户端逻辑处理 */ public class ClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { //打印服务端的发送数据 System.out.println(s); } }
ClientIniterHandler.java
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 客户端处理初始化 */ public class ClientIniterHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //注册管道 ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("http", new HttpClientCodec()); pipeline.addLast("chat", new ClientHandler()); } }
先启动服务端 NettyServer
在启动客户端 NettyClient
在客户端控制台输入信息,服务端可以监听到,具体代码参考:
while (true) { //向服务端发送内容 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String content = reader.readLine(); if (!StringUtils.isEmpty(content)) { if (content.equalsIgnoreCase("quit")) { System.exit(1); } channel.writeAndFlush(content); } }