依赖

 <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. python-面向过程、面向对象、类

    目录 python-面向过程.面向对象.类 面向过程编程 面向对象 类和对象 二者区别 定义格式: 定义类发生的事情 定义类的标准格式 属性的查找顺序 python-面向过程.面向对象.类 面向过程编 ...

  2. 7. Minimum Depth of Binary Tree-LeetCode

    难度系数:easy /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; ...

  3. Docker镜像相关操作

    批量导入镜像 ll *.tgz|awk '{print $NF}'|sed -r 's#(.*)#docker load -i \1#' |bash 批量打tag docker images | se ...

  4. Windows cmd 命令行基本操作

    Windows cmd 命令行基本操作 1. 进入到指定根目录 注意:不区分大小写 例如进入到 D 盘 2. 进入到指定的目录 例如 (如果目录文件名太长,可以使用 tab 键来自动补全.重复按可以进 ...

  5. acute

    In Euclidean geometry, an angle is the figure formed by two rays, called the sides of the angle, sha ...

  6. Netty之Channel*

    Netty之Channel* 本文内容主要参考**<<Netty In Action>> ** 和Netty的文档和源码,偏笔记向. 先简略了解一下ChannelPipelin ...

  7. 大数据学习day16------第三阶段-----scala04--------1. 模式匹配和样例类 2 Akka通信框架

    1. 模式匹配和样例类 Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句.类型检查等.并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配 1.1 模式匹 ...

  8. jQuery无限载入瀑布流 【转载】

    转载至 http://wuyuans.com/2013/08/jquery-masonry-infinite-scroll/ jQuery无限载入瀑布流 好久没更新日志了,一来我比较懒,二来最近也比较 ...

  9. 3.6 String 与 切片&str的区别

    The rust String  is a growable, mutable, owned, UTF-8 encoded string type. &str ,切片,是按UTF-8编码对St ...

  10. postman 中get传参数

    mybatis中: @RequestMapping(value = "/detail/{id}", method = RequestMethod.GET, produces = & ...