├── .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
--------------------------------------------------------------------------------