依赖

 <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. Atcoder Grand Contest 001 F - Wide Swap(拓扑排序)

    Atcoder 题面传送门 & 洛谷题面传送门 咦?鸽子 tzc 来补题解了?奇迹奇迹( 首先考虑什么样的排列可以得到.我们考虑 \(p\) 的逆排列 \(q\),那么每次操作的过程从逆排列的 ...

  2. BZOJ 3238 差异

    BZOJ 3238 差异 看这个式子其实就是求任意两个后缀的 $ LCP $ 长度和.前面的 $ len(T_i)+len(T_j) $ 求和其实就是 $ n(n-1)(n+1)/2 $ ,这个是很好 ...

  3. 混合(Pooling)样本测序研究

    目录 1.混合测序基础 2. 点突变检测 3. BSA 4. BSR 5. 混合样本GWAS分析 6. 混合样本驯化研究 7. 小结 1.混合测序基础 测序成本虽然下降了,但对于植物育种应用研究来说还 ...

  4. C/C++ Qt 数据库与ComBox多级联动

    Qt中的SQL数据库组件可以与ComBox组件形成多级联动效果,在日常开发中多级联动效果应用非常广泛,例如当我们选择指定用户时,我们让其在另一个ComBox组件中列举出该用户所维护的主机列表,又或者当 ...

  5. SpringBoot Logback 日志配置

    目录 前言 日志格式 日志输出 日志轮替 日志级别 日志分组 小结 前言 之前使用 SpringBoot 的时候,总是习惯于将日志框架切换为 Log4j2,可能是觉得比较靠谱,也可能年龄大了比较排斥新 ...

  6. Docker学习(五)——Docker仓库管理

    Docker仓库管理     仓库(Repository)是集中存放镜像的地方. 1.Docker Hub       目前Docker官方维护了一个公共仓库Docker Hub.大部分需求都可以通过 ...

  7. iOS 客户端获取七牛上传token

    一.官方参考文档: 1.上传策略http://developer.qiniu.com/article/developer/security/put-policy.html 2.上传凭证(即uptoke ...

  8. 队列——Java实现

    1 package struct; 2 3 interface IQueue{ 4 //入队列 5 void add(Object obj); 6 //出队列 7 Object remove(); 8 ...

  9. go recover让崩溃的程序继续执行

    package main import ( "fmt" "log" ) func de() { //recover() //可以打印panic的错误信息 //f ...

  10. ActiveRecord教程

    (一.ActiveRecord基础) ActiveRecord是Rails提供的一个对象关系映射(ORM)层,从这篇开始,我们来了解Active Record的一些基础内容,连接数据库,映射表,访问数 ...