Netty系列2:Springboot+Netty入门案例

要实现的功能

在controller中给netty服务器发送消息

案例组成

1.客户端:客户端的配置,业务处理逻辑,从控制器中给Netty服务器发送消息
2.服务端:服务端的配置和业务处理逻辑

客户端和服务端依赖配置

    
	
		io.netty
		netty-all
		5.0.0.Alpha1
	
	注:其他依赖根据需要添加	

客户端

客户端配置类(读取application.yml中的配置)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class NettyConfig {
	/**
	 * 主机
	 */
	@Value("${netty.host}")
	private String host;
	/**
	 * 端口号
	 */
	@Value("${netty.port}")
	private int port;
	
	@Bean 
	public NettyClient nettyClient() {
		return new NettyClient(host,port);
	}
}

application.yml

server:
  port: 8900
  
netty:
  port : 8899 
  host: 127.0.0.1

客户端配置代码(完成连接netty服务器的功能)

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

@Component
public class NettyClient {
	//主机
	private String host;
	//端口号
	private int port;
	private EventLoopGroup group;
	private Bootstrap b;
	private ChannelFuture cf;

	public NettyClient(String host, int port) {
		this.host = host;
		this.port = port;
	}

	@PostConstruct  //此注解会在类创建时被调用
	public void init() throws InterruptedException {
		group = new NioEventLoopGroup();
		b = new Bootstrap();
		b.group(group)
		.channel(NioSocketChannel.class)
		.handler(new ChannelInitializer() {
			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				ChannelPipeline pipeline = sc.pipeline();
			}
		});
		ChannelFuture cf = b.connect(host, port).sync(); 
	}

	public void connect() {
		try {
			this.cf = this.b.connect(host, port).sync(); // 发送json字符串
			System.out.println("远程服务器已经连接, 可以进行数据交换..");	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public ChannelFuture getChannelFuture() {
		// 如果没有连接先链接
		if (this.cf == null) {
			this.connect();
		} // this.cf.channel().isActive() 这里得到的是链接状态
		if (!this.cf.channel().isActive()) {
			this.connect();
		}
		return this.cf;
	}

	//释放资源
	public void close() {
		try {
			cf.channel().closeFuture().sync();
			group.shutdownGracefully();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

客户端业务逻辑处理器

import java.net.InetSocketAddress;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class NettyClientHandler extends SimpleChannelInboundHandler {

	@Override
	protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
		System.out.println("收到服务端消息: " + msg);
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String clientIp = insocket.getAddress().getHostAddress();
		System.out.println("连接服务器[ip:" + clientIp + ":"+ insocket.getPort()+ "]成功");
		ctx.writeAndFlush("active发送的消息");
	}
}

客户端Controller代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoop;

@RestController
public class TestController {
	@Autowired
	NettyClient nettyClient;

	@RequestMapping("/test/{msg}")
	public String sendMsg(@PathVariable("msg") String msg) {
		//获取建立的channel
		ChannelFuture cf = nettyClient.getChannelFuture();
		Channel channel = cf.channel();
		System.out.println("testcontroller获得的channel---" + channel.toString());
		byte[] req = msg.getBytes();
		ByteBuf  sendMsg=Unpooled.buffer(req.length);
	    sendMsg.writeBytes(req);
		cf.channel().writeAndFlush(sendMsg);
		return "1";
	}
}

服务端

服务端配置类(功能:从application.yml中读取配置)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class NettyConfig {
	/**
	 * 端口号
	 */
	@Value("${netty.port}")
	private int port;
	
	@Bean 
	public NettyServer nettyServer() {
		return new NettyServer(port);
	}
}

application.yml

#内嵌tomcat运行端口
server:
  port: 8090
#netty服务启动端口
netty:
  port : 8899 

服务端配置代码(监听客户端请求,建立连接,发送消息等功能)

import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * netty服务端
 * 
 */
@Component
public class NettyServer {
	private Logger log = LoggerFactory.getLogger(getClass());
	/**
	 * 端口号
	 */
	private int port;
	
	public NettyServer(int port) {
		this.port = port;
	}

	/**
	 * 启动服务器方法
	 * @param port
	 */
	@PostConstruct
	public void run() {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup);
			serverBootstrap.channel(NioServerSocketChannel.class);
			serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
			serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
			serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
			serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
			serverBootstrap.childHandler(new NettyServerInitializer()); // 绑定端口,开始接收进来的连接
			ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
			log.info("netty服务启动: [port:" + port + "]");
			// 等待服务器socket关闭
			channelFuture.channel().closeFuture().sync();
		} catch (Exception e) {
			log.error("netty服务启动异常-" + e.getMessage());
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}

服务端处理器

import java.net.InetSocketAddress;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;

public class NettyServerHandler extends ChannelHandlerAdapter {
	private Logger log = LoggerFactory.getLogger(getClass());

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf buf = (ByteBuf) msg;
		byte[] req = new byte[buf.readableBytes()];
		buf.readBytes(req);
		String body = new String(req, "UTF-8");
		System.out.println("服务端收到消息 : " + body);
		// StringBuilder sb = null;
		// Map result = null;
		// try {
		// // 报文解析处理
		// sb = new StringBuilder();
		//// result = JSON.parseObject(msg.toString());
		//
		// sb.append("服务端返回的数据"+JSON.toJSONString(msg));
		// ctx.writeAndFlush(sb);
		// } catch (Exception e) {
		// String errorCode = "-1\n";
		// ctx.writeAndFlush(errorCode);
		// log.error("报文解析失败: " + e.getMessage());
		// }
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String clientIp = insocket.getAddress().getHostAddress();
		log.info("已上线:客户端[ip:" + clientIp + "]");
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		// 当出现异常就关闭连接
		ctx.close();
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		String clientIp = insocket.getAddress().getHostAddress();
		System.out.println("已下线:客户端[ip:" + clientIp + "]");
		super.channelInactive(ctx);
	}

	@Override
	public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
		super.disconnect(ctx, promise);
	}

}

服务端通道初始化代码

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class NettyServerInitializer extends ChannelInitializer {
	//初始化channel
	@Override
	public void initChannel(SocketChannel ch) throws Exception {
		ChannelPipeline pipeline = ch.pipeline();
		pipeline.addLast(new NettyServerHandler());
	}
}

实现效果图

操作
浏览器访问localhost:8900/test/aa
服务端控制台结果
在这里插入图片描述

参考

https://blog.csdn.net/column/details/17640.html

你可能感兴趣的:(Netty)