├── .gitignore ├── client ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── gj │ └── client │ ├── ChatClient.java │ ├── Client1.java │ ├── Client2.java │ └── ClientThread.java ├── pom.xml ├── readme.md └── server ├── .gitignore ├── pom.xml └── src └── main ├── java └── com │ └── gj │ ├── ServerApplication.java │ └── server │ ├── NettyStartListener.java │ ├── SocketHandler.java │ ├── SocketInitializer.java │ └── SocketServer.java └── resources └── application.yml /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.gj 7 | springboot-netty 8 | 0.0.1-SNAPSHOT 9 | 10 | client 11 | 0.0.1-SNAPSHOT 12 | client 13 | netty客户端 14 | 15 | 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-maven-plugin 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /client/src/main/java/com/gj/client/ChatClient.java: -------------------------------------------------------------------------------- 1 | package com.gj.client; 2 | 3 | import org.springframework.util.StringUtils; 4 | 5 | import java.io.IOException; 6 | import java.net.InetSocketAddress; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.Selector; 9 | import java.nio.channels.SocketChannel; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.Scanner; 12 | 13 | /** 14 | * 聊天客户端 15 | * 16 | * @author Gjing 17 | **/ 18 | public class ChatClient { 19 | 20 | public void start(String name) throws IOException { 21 | SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8088)); 22 | socketChannel.configureBlocking(false); 23 | Selector selector = Selector.open(); 24 | socketChannel.register(selector, SelectionKey.OP_READ); 25 | 26 | // 监听服务端发来得消息 27 | new Thread(new ClientThread(selector)).start(); 28 | 29 | Scanner scanner = new Scanner(System.in); 30 | while (scanner.hasNextLine()) { 31 | String message = scanner.nextLine(); 32 | if (StringUtils.hasText(message)) { 33 | socketChannel.write(StandardCharsets.UTF_8.encode(name + ": " + message)); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/main/java/com/gj/client/Client1.java: -------------------------------------------------------------------------------- 1 | package com.gj.client; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @author Gjing 7 | **/ 8 | public class Client1 { 9 | public static void main(String[] args) throws IOException { 10 | new ChatClient().start("李四"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/java/com/gj/client/Client2.java: -------------------------------------------------------------------------------- 1 | package com.gj.client; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @author Gjing 7 | **/ 8 | public class Client2 { 9 | public static void main(String[] args) throws IOException { 10 | new ChatClient().start("张三"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/java/com/gj/client/ClientThread.java: -------------------------------------------------------------------------------- 1 | package com.gj.client; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.Selector; 7 | import java.nio.channels.SocketChannel; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.Iterator; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author Gjing 14 | **/ 15 | public class ClientThread implements Runnable{ 16 | private final Selector selector; 17 | 18 | public ClientThread(Selector selector) { 19 | this.selector = selector; 20 | } 21 | 22 | @Override 23 | public void run() { 24 | try { 25 | for (; ; ) { 26 | int channels = selector.select(); 27 | if (channels == 0) { 28 | continue; 29 | } 30 | Set selectionKeySet = selector.selectedKeys(); 31 | Iterator keyIterator = selectionKeySet.iterator(); 32 | while (keyIterator.hasNext()) { 33 | SelectionKey selectionKey = keyIterator.next(); 34 | 35 | // 移除集合当前得selectionKey 36 | keyIterator.remove(); 37 | if (selectionKey.isReadable()) { 38 | this.handleRead(selector, selectionKey); 39 | } 40 | } 41 | } 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | // 处理可读状态 48 | private void handleRead(Selector selector, SelectionKey selectionKey) throws IOException { 49 | SocketChannel channel = (SocketChannel) selectionKey.channel(); 50 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 51 | StringBuilder message = new StringBuilder(); 52 | if (channel.read(byteBuffer) > 0) { 53 | byteBuffer.flip(); 54 | message.append(StandardCharsets.UTF_8.decode(byteBuffer)); 55 | } 56 | // 再次注册到选择器上,继续监听可读状态 57 | channel.register(selector, SelectionKey.OP_READ); 58 | System.out.println(message); 59 | } 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.6.RELEASE 9 | 10 | 11 | com.gj 12 | springboot-netty 13 | 0.0.1-SNAPSHOT 14 | springboot-netty 15 | springboot集成netty 16 | pom 17 | 18 | 19 | server 20 | client 21 | 22 | 23 | 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter 31 | 32 | 33 | 34 | org.projectlombok 35 | lombok 36 | 37 | 38 | io.netty 39 | netty-all 40 | 4.1.42.Final 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SpringBoot使用netty -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.gj 7 | springboot-netty 8 | 0.0.1-SNAPSHOT 9 | 10 | server 11 | 0.0.1-SNAPSHOT 12 | server 13 | netty服务端 14 | 15 | 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-maven-plugin 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /server/src/main/java/com/gj/ServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.gj; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ServerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ServerApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /server/src/main/java/com/gj/server/NettyStartListener.java: -------------------------------------------------------------------------------- 1 | package com.gj.server; 2 | 3 | import org.springframework.boot.ApplicationArguments; 4 | import org.springframework.boot.ApplicationRunner; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.Resource; 8 | 9 | /** 10 | * 监听Spring容器启动完成,完成后启动Netty服务器 11 | * @author Gjing 12 | **/ 13 | @Component 14 | public class NettyStartListener implements ApplicationRunner { 15 | @Resource 16 | private SocketServer socketServer; 17 | 18 | @Override 19 | public void run(ApplicationArguments args) throws Exception { 20 | this.socketServer.start(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/gj/server/SocketHandler.java: -------------------------------------------------------------------------------- 1 | package com.gj.server; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.channel.group.ChannelGroup; 7 | import io.netty.channel.group.DefaultChannelGroup; 8 | import io.netty.util.concurrent.GlobalEventExecutor; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * Socket拦截器,用于处理客户端的行为 13 | * 14 | * @author Gjing 15 | **/ 16 | @Slf4j 17 | public class SocketHandler extends ChannelInboundHandlerAdapter { 18 | public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 19 | 20 | /** 21 | * 读取到客户端发来的消息 22 | * 23 | * @param ctx ChannelHandlerContext 24 | * @param msg msg 25 | * @throws Exception e 26 | */ 27 | @Override 28 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 29 | byte[] data = (byte[]) msg; 30 | log.info("收到消息: " + new String(data)); 31 | // 给其他人转发消息 32 | for (Channel client : clients) { 33 | if (!client.equals(ctx.channel())) { 34 | client.writeAndFlush(data); 35 | } 36 | } 37 | } 38 | 39 | @Override 40 | public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 41 | log.info("新的客户端链接:" + ctx.channel().id().asShortText()); 42 | clients.add(ctx.channel()); 43 | } 44 | 45 | @Override 46 | public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { 47 | clients.remove(ctx.channel()); 48 | } 49 | 50 | @Override 51 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 52 | cause.printStackTrace(); 53 | ctx.channel().close(); 54 | clients.remove(ctx.channel()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /server/src/main/java/com/gj/server/SocketInitializer.java: -------------------------------------------------------------------------------- 1 | package com.gj.server; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.bytes.ByteArrayDecoder; 7 | import io.netty.handler.codec.bytes.ByteArrayEncoder; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * Socket 初始化器 12 | * @author Gjing 13 | **/ 14 | @Component 15 | public class SocketInitializer extends ChannelInitializer { 16 | @Override 17 | protected void initChannel(SocketChannel socketChannel) throws Exception { 18 | ChannelPipeline pipeline = socketChannel.pipeline(); 19 | // 添加对byte数组的编解码,netty提供了很多编解码器,你们可以根据需要选择 20 | pipeline.addLast(new ByteArrayDecoder()); 21 | pipeline.addLast(new ByteArrayEncoder()); 22 | // 最后添加上自己的处理器 23 | pipeline.addLast(new SocketHandler()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/java/com/gj/server/SocketServer.java: -------------------------------------------------------------------------------- 1 | package com.gj.server; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.nio.NioEventLoopGroup; 5 | import io.netty.channel.socket.nio.NioServerSocketChannel; 6 | import lombok.Getter; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @author Gjing 15 | **/ 16 | @Slf4j 17 | @Component 18 | public class SocketServer { 19 | @Resource 20 | private SocketInitializer socketInitializer; 21 | 22 | @Getter 23 | private ServerBootstrap serverBootstrap; 24 | 25 | /** 26 | * netty服务监听端口 27 | */ 28 | @Value("${netty.port:8088}") 29 | private int port; 30 | /** 31 | * 主线程组数量 32 | */ 33 | @Value("${netty.bossThread:1}") 34 | private int bossThread; 35 | 36 | /** 37 | * 启动netty服务器 38 | */ 39 | public void start() { 40 | this.init(); 41 | this.serverBootstrap.bind(this.port); 42 | log.info("Netty started on port: {} (TCP) with boss thread {}", this.port, this.bossThread); 43 | } 44 | 45 | /** 46 | * 初始化netty配置 47 | */ 48 | private void init() { 49 | // 创建两个线程组,bossGroup为接收请求的线程组,一般1-2个就行 50 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(this.bossThread); 51 | // 实际工作的线程组 52 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 53 | this.serverBootstrap = new ServerBootstrap(); 54 | this.serverBootstrap.group(bossGroup, workerGroup) // 两个线程组加入进来 55 | .channel(NioServerSocketChannel.class) // 配置为nio类型 56 | .childHandler(this.socketInitializer); // 加入自己的初始化器 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | spring: 4 | application: 5 | name: netty-server --------------------------------------------------------------------------------