依赖

 <dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>

还用到了

   <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
ChatDto.java
import lombok.Data;
import lombok.experimental.Accessors; /**
* 传输实体类
*/
@Data
@Accessors(chain = true)
public class ChatDto { /**
* 客户端ID 唯一
*/
private String clientId; /**
* 发送的消息
*/
private String msg;
}

NettyChannelMap.java

import io.netty.channel.Channel;
import io.netty.channel.socket.SocketChannel; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* 存放连接的channel对象
*/
public class NettyChannelMap {
private static Map<String, SocketChannel> map = new ConcurrentHashMap<String, SocketChannel>(); public static void add(String clientId, SocketChannel socketChannel) {
map.put(clientId, socketChannel);
} public static Channel get(String clientId) {
return map.get(clientId);
} public static void remove(SocketChannel socketChannel) {
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() == socketChannel) {
map.remove(entry.getKey());
}
}
}
}

NettyTcpServerBootstrap.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; /**
* 服务端启动类
*/
public class NettyTcpServerBootstrap {
private int port;
private SocketChannel socketChannel; public NettyTcpServerBootstrap(int port) throws InterruptedException {
this.port = port;
} public void start() throws InterruptedException {
/**
* 创建两个线程组 bossGroup 和 workerGroup
* bossGroup 只是处理连接请求,真正的和客户端业务处理,会交给 workerGroup 完成
* 两个都是无线循环
*/
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
//创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//设置两个线程组
bootstrap.group(bossGroup, workerGroup)
//使用NioServerSocketChannel 作为服务器的通道实现
.channel(NioServerSocketChannel.class)
//设置线程队列得到连接个数
.option(ChannelOption.SO_BACKLOG, 128)
//设置保持活动连接状态
.childOption(ChannelOption.SO_KEEPALIVE, true)
//通过NoDelay禁用Nagle,使消息立即发出去,不用等待到一定的数据量才发出去
.childOption(ChannelOption.TCP_NODELAY, true)
//可以给 bossGroup 加个日志处理器
.handler(new LoggingHandler(LogLevel.INFO))
//给workerGroup 的 EventLoop 对应的管道设置处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
//给pipeline 设置处理器
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline p = socketChannel.pipeline();
p.addLast(new ObjectEncoder());
p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
p.addLast(new NettyServerHandler());
}
}); //启动服务器并绑定一个端口并且同步生成一个 ChannelFuture 对象
ChannelFuture cf = bootstrap.bind(port).sync();
if (cf.isSuccess()) {
System.out.println("socket server start---------------");
} //对关闭通道进行监听
cf.channel().closeFuture().sync();
} finally {
//发送异常关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully(); }
} }

NettyServerHandler.java

import com.alibaba.fastjson.JSON;
import com.example.netty.dto.ChatDto;
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.channel.socket.SocketChannel;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j; @Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<Object> { /**
* 定义一个channel组管理所有channel
* GlobalEventExecutor.INSTANCE 是一个全局事件执行器 是一个单例
*/
private static ChannelGroup channelGroup=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); /**
* 标识 channel处于活动状态
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { } /**
* 表示连接建立 第一个被执行
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
channelGroup.add(ctx.channel()); /**
* 该方法会将 channelGroup 中所有的channel 遍历一遍然后发送消息 不用我们自己遍历
* 这里只是做个说明 不用
*/
// channelGroup.writeAndFlush("发送所有给所有channel");
} /**
* 断开连接
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } /**
* 标识channel处于非活动状态
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
NettyChannelMap.remove((SocketChannel) ctx.channel());
} /**
* 服务端 接收到 客户端 发的数据
* @param context
* @param obj
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext context, Object obj) throws Exception { log.info(">>>>>>>>>>>服务端接收到客户端的消息:{}",obj); SocketChannel socketChannel = (SocketChannel) context.channel();
ChatDto dto = JSON.parseObject(obj.toString(), ChatDto.class);
/**
* 客户端ID
*/
String clientId = dto.getClientId(); if (clientId == null) {
/**
* 心跳包处理
*/
ChatDto pingDto=new ChatDto();
pingDto.setMsg("服务端收到心跳包,返回响应");
socketChannel.writeAndFlush(JSON.toJSONString(pingDto));
return;
} Channel channel = NettyChannelMap.get(clientId); if (channel==null){
/**
* 存放所有连接客户端
*/
NettyChannelMap.add(clientId, socketChannel);
channel=socketChannel;
} /**
* 服务器返回客户端消息
*/
ChatDto returnDto=new ChatDto();
returnDto.setClientId(clientId).setMsg("我是服务端,收到你的消息了"); channel.writeAndFlush(JSON.toJSONString(returnDto)); /**
* 在这里可以设置异步执行 提交任务到该channel的taskQueue 中
*/ context.channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10*1000);
log.info(">>>>>>>>>休眠十秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); /**
* 可以设置多个异步任务
* 但是这个会在上面异步任务执行完之后才执行
*/
context.channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10*1000);
log.info(">>>>>>>>>休眠二十秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); ReferenceCountUtil.release(obj);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}

客户端

NettyClientBootstrap.java

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;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup; /**
* 客户端启动类
*/
public class NettyClientBootstrap {
private int port;
private String host;
public SocketChannel socketChannel;
private static final EventExecutorGroup group = new DefaultEventExecutorGroup(20); public NettyClientBootstrap(int port, String host) throws InterruptedException {
this.port = port;
this.host = host;
start();
} private void start() throws InterruptedException {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(host, port)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 0 表示禁用
* readerIdleTime读空闲超时时间设定,如果channelRead()方法超过readerIdleTime时间未被调用则会触发超时事件调用userEventTrigger()方法;
*
* writerIdleTime写空闲超时时间设定,如果write()方法超过writerIdleTime时间未被调用则会触发超时事件调用userEventTrigger()方法;
*
* allIdleTime所有类型的空闲超时时间设定,包括读空闲和写空闲;
*/
socketChannel.pipeline().addLast(new IdleStateHandler(20, 10, 0));
socketChannel.pipeline().addLast(new ObjectEncoder());
socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
ChannelFuture future = bootstrap.connect(host, port).sync();
if (future.isSuccess()) {
socketChannel = (SocketChannel) future.channel();
System.out.println("connect server 成功---------");
}
//给关闭通道进行监听
future.channel().closeFuture().sync();
} finally {
eventLoopGroup.shutdownGracefully();
}
} }

NettyClientHandler.java

import com.alibaba.fastjson.JSON;
import com.example.netty.dto.ChatDto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j; @Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler<Object> { @Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
log.info(">>>>>>>>连接");
} @Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
log.info(">>>>>>>>退出");
} @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
switch (e.state()) {
case WRITER_IDLE:
/**
* 利用写空闲发送心跳检测消息
*/
ChatDto pingDto=new ChatDto();
pingDto.setMsg("我是心跳包");
ctx.writeAndFlush(JSON.toJSONString(pingDto));
log.info("send ping to server----------");
break;
default:
break;
}
}
} /**
* 客户端接收到服务端发的数据
* @param channelHandlerContext
* @param obj
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object obj) { log.info(">>>>>>>>>>>>>客户端接收到消息:{}", obj); ReferenceCountUtil.release(obj);
} /**
* socket通道处于活动状态
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info(">>>>>>>>>>socket建立了");
super.channelActive(ctx);
} /**
* socket通道不活动了
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info(">>>>>>>>>>socket关闭了");
super.channelInactive(ctx);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}

SprigBoot启动类添加服务端启动代码

@SpringBootApplication
public class NettyApplication { public static void main(String[] args) {
SpringApplication.run(NettyApplication.class, args); try {
NettyTcpServerBootstrap bootstrap = new NettyTcpServerBootstrap(9999);
bootstrap.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("server socket 启动失败");
}
} }

ChatController.java

import com.alibaba.fastjson.JSON;
import com.example.netty.dto.ChatDto;
import com.example.netty.socket.NettyClientBootstrap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.UUID; /**
* 客户端消息发送控制器
*/
@RestController
@Slf4j
public class ChatController { private static String clientId=UUID.randomUUID().toString(); public static NettyClientBootstrap bootstrap; /**
* 发送消息demo
* @param msg
*/
@PostMapping(value = "/send")
public void send(String msg) {
if (bootstrap == null) {
try {
/**
* 连接 输入服务器的端口和ip
*/
bootstrap = new NettyClientBootstrap(9999, "localhost");
} catch (InterruptedException e) {
e.printStackTrace();
log.error(">>>>>>>>> server socket 连接失败");
}
}
/**
* 发送消息
*/
ChatDto dto=new ChatDto();
dto.setClientId(clientId).setMsg(msg);
/**
* json字符串发送
*/
bootstrap.socketChannel.writeAndFlush(JSON.toJSONString(dto)); }
}

访问

SpringBoot整合Netty实现socket通讯简单demo的更多相关文章

  1. springboot整合netty(二)

    目录 前言 正文 代码 1. 新建一个springboot项目,在pom文件中添加netty依赖: 2.新建netty服务 3.netty调用所需的服务类 4 springboot启动类 5.测试 我 ...

  2. SpringBoot整合Netty并使用Protobuf进行数据传输(附工程)

    前言 本篇文章主要介绍的是SpringBoot整合Netty以及使用Protobuf进行数据传输的相关内容.Protobuf会简单的介绍下用法,至于Netty在之前的文章中已经简单的介绍过了,这里就不 ...

  3. springboot整合netty的多种方式

    netty作为一个高性能的io框架,是非好用的一个技术框架, Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户. ...

  4. springboot整合netty,多种启动netty的方式,展现bean得多种启动方法

    首先讲解下,spring中初始化加载问题: 很多时候,我们自己写的线程池,还有bean对象,还有其他的服务类,都可以通过,相关注解进行交给spring去管理,那么我们如何让nettyserver初始化 ...

  5. SpringBoot整合Netty

    总体来说只需要三个步骤,下面详细介绍 第一步 创建SpringBoot工程snetty 第二步 创建子模块也就是ModuleClient,Server,均为SpringBoot工程 第三步 将服务器端 ...

  6. 第6章 使用springboot整合netty搭建后台

    我们不会去使用自增长的id,在现阶段的互联网开发过程中,自增长的id是已经不适用了.在未来随着系统版本的迭代,用户数量的递增,肯定会做分库分表,去做一些相应的切分.在这个时候我们就需要有一个唯一的id ...

  7. Node中的Socket.IO 简单Demo及说明

    注:下面Demo的Server和Client都是纯后端. 并没有web页面. Server端代码: var express = require('express'); var app = expres ...

  8. SpringBoot整合Guacamole教程

    前言 本文主要介绍的是SpringBoot如何整合Guacamole在浏览器是远程桌面的访问. Guacamole 介绍 Apache Guacamole 是一个无客户端远程桌面网关.它支持标准协议, ...

  9. springboot整合mybatis出现的一些问题

    springboot整合mybatis非常非常的简单,简直简单到发指.但是也有一些坑,这里我会详细的指出会遇到什么问题,并且这些配置的作用 整合mybatis,无疑需要mapper文件,实体类,dao ...

随机推荐

  1. [Noip 2018][标题统计 龙湖斗 摆渡车 对称二叉树]普及组题解

    啊喂,都已经9102年了,你还在想去年? 这里是一个Noip2018年PJ第二题打爆的OIer,错失省一 但经过了一年,我学到了很多,也有了很多朋友,水平也提高了很多,现在回看当时: 今年的Noip ...

  2. 洛谷 P7620 - CF1431J Zero-XOR Array(状压 dp)

    洛谷题面传送门 首先显然题目等价于求有多少个长度 \(n-1\) 的序列 \(b\) 满足 \(a_i\le b_i\le a_{i+1}\),满足 \(b_1\oplus b_2\oplus\cdo ...

  3. python判断字符串是否为空和null

    1.使用字符串长度判断 len(s==0)则字符串为空 test1 = '' if len(test1) == 0: print('test1为空串') else: print('test非空串,te ...

  4. 【玩具】获取B站视频的音频片段

    事情是这样的,我有个和社畜的社会地位不太相符的小爱好--听音乐剧. 基本上是在B站上点开视频听,不是不想在网易云或者QQ音乐听,只是在这些音乐软件上面,我想听的片段要不就收费,要不版本不是我喜欢的,要 ...

  5. A Child's History of England.13

    Then came the boy-king, Edgar, called the Peaceful, fifteen years old. Dunstan, being still the real ...

  6. acquaint

    Interpersonal relationships are dynamic systems that change continuously during their existence. Lik ...

  7. [学习总结]6、Android异步消息处理机制完全解析,带你从源码的角度彻底理解

    开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...

  8. jenkins之邮箱设置

  9. VFL

    VFL 1. 概念 VFL全称是Visual Format Language,翻译过来是"可视化格式语言" VFL是苹果公司为了简化Autolayout的编码而推出的抽象语言 2. ...

  10. 【Linux】【Services】【SaaS】Docker+kubernetes(11. 构建复杂的高可用网络)

    1. 简介 flannel在实战阶段貌似不能胜任在灾难恢复时候异地的网络,打算用openvswith试试