SpringBoot使⽤Netty实现客户端与服务器通信⼀、服务端
1、添加Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
springboot结构<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>ioty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.59</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2、l
server:
port: 8001
3、启动类
@SpringBootApplication
public class NettyServerApplication implements CommandLineRunner {
@Autowired
private NettyServerBootStrap serverBootStrap;
public static void main(String[] args) {
SpringApplication.run(NettyServerApplication.class, args);
}
@Override
public void args) throws Exception {
serverBootStrap.start();
}
}
4、NettyServerBootStrap
@Component
@Slf4j
public class NettyServerBootStrap {
@Autowired
private NettyServerHandler nettyServerHandler;
public void start() throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
try {
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
// 使消息⽴即发出去,不⽤等待到⼀定的数据量才发出去
.option(ChannelOption.TCP_NODELAY, true)
// 保持长连接状态
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {                            ChannelPipeline p = socketChannel.pipeline();
p.addLast(new StringDecoder(CharsetUtil.UTF_8));
p.addLast(new StringEncoder(CharsetUtil.UTF_8));
p.addLast(nettyServerHandler);
}
});
// 绑定端⼝,同步等待成功
ChannelFuture f = bootstrap.bind(5678).sync();
if (f.isSuccess()) {
log.info("Netty Start successful");
} else {
<("Netty Start failed");
}
/
/ 等待服务监听端⼝关闭
f.channel().closeFuture().sync();
} finally {
// 退出,释放线程资源
worker.shutdownGracefully();
boss.shutdownGracefully();
}
}
}
5、NettyServerHandler
@Component
@ChannelHandler.Sharable
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
/**
* @Description 客户端断开连接时执⾏,将客户端信息从Map中移除
* @param ctx
* @Date 2019/8/28 14:22
* @Author wuyong
* @return
**/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端断开连接:{}", getClientIp(ctx.channel()));
}
/**
* @Description 客户端连接时执⾏,将客户端信息保存到Map中
* @param ctx
* @Date 2019/8/28 14:22
* @Author wuyong
* @return
**/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("有新的客户端连接:{}", getClientIp(ctx.channel()));
String clientIp = getClientIp(ctx.channel());
NettyClient client = new NettyClient((SocketChannel) ctx.channel(), getClientIp(ctx.channel()));        NettyChannelMap.add(clientIp, client);
}
/**
* @Description 收到消息时执⾏,根据消息类型做不同的处理
* @param ctx
* @param msg
* @Date 2019/8/28 14:33
* @Author wuyong
* @return
**/
@Override
public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
log.info("收到客户端消息:" + msg);
// 这个消息⼀般是结构化的数据,⽐如JSON字符串,解析这个JSON字符串,做相应的逻辑处理        JSONObject msgObj = JSON.parseObject(msg);
String msgType = String("msgType");
switch (msgType) {
// 回复客户端请求
case "req":
doReply(ctx);
break;
default:
break;
}
}
/**
* @description: TODO
* @param ctx
* @param cause
* @Author: wuyong
* @Date: 2019/08/30 13:41:51
* @return: void
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        log.info("抛出异常执⾏,包括客户端断开连接时,会抛出IO异常");
}
/**
* @description: 当收到客户端的消息后,进⾏处理
* @param ctx
* @Author: wuyong
* @Date: 2019/08/30 14:10:59
* @return: void
*/
private void doReply(ChannelHandlerContext ctx) {
String reply = "{\"msgType\":\"reply\",\"data\":\"回复的数据\"}";
ctx.channel().writeAndFlush(reply);
}
/**
* @Description 获取客户端IP
* @param channel
* @Date 2019/8/28 14:32
* @Author wuyong
* @return
**/
private String getClientIp(Channel channel) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) Address();        String clientIP = Address().getHostAddress();
return clientIP;
}
/**
* @Description 当有新的客户端连接的时候,⽤于保存客户端信息
* @Date 2019/8/28 14:20
* @Author wuyong
* @return
**/
public static class NettyChannelMap {
public static Map<String, NettyClient> map = new ConcurrentHashMap<>();
public static void add(String clientId, NettyClient client) {
map.put(clientId, client);
}
public static NettyClient get(String clientId) {
(clientId);
}
public static void remove(SocketChannel socketChannel) {
for (Map.Entry entry : Set()) {
if (((NettyClient) Value()).getChannel() == socketChannel) {
}
}
}
}
/**
* @Description 封装客户端的信息
* @Date 2019/8/28 14:21
* @Author wuyong
* @return
**/
@Data
public static class NettyClient {
/**客户端与服务器的连接*/
private SocketChannel channel;
/**ip地址*/
private String clientIp;
// ......
public NettyClient(SocketChannel channel, String clientIp) {
this.channel = channel;
this.clientIp = clientIp;
}
}
}
⾄此,⼀个简单的Netty服务端就完成了。接下来写⼀个Controller,⽤于获取当前在线的客户端列表:
6、NettyServerController
@RestController
@RequestMapping("/server")
public class NettyServerController {
@GetMapping("/clientList")
public Map<String, NettyServerHandler.NettyClient> clientList() {
return NettyServerHandler.NettyChannelMap.map;
}
}
项⽬结构如下:
⼆、客户端
客户端添加的依赖、配置⽂件以及启动类和服务端类似。
1、NettyClientBootStrap

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。