Full Code of marquisXuan/netty for AI

master 4c3c96c00a7e cached
95 files
146.3 KB
45.2k tokens
282 symbols
1 requests
Download .txt
Repository: marquisXuan/netty
Branch: master
Commit: 4c3c96c00a7e
Files: 95
Total size: 146.3 KB

Directory structure:
gitextract_3mkei390/

├── .gitignore
├── README.md
└── spring-boot-netty/
    ├── netty-commons/
    │   ├── pom.xml
    │   └── src/
    │       └── main/
    │           └── java/
    │               └── org/
    │                   └── yyx/
    │                       └── netty/
    │                           ├── entity/
    │                           │   ├── MethodInvokeMeta.java
    │                           │   ├── NullWritable.java
    │                           │   └── User.java
    │                           ├── exception/
    │                           │   ├── ErrorParamsException.java
    │                           │   └── NoUseableChannel.java
    │                           └── util/
    │                               ├── ObjectCodec.java
    │                               └── ObjectSerializerUtils.java
    ├── netty-demo/
    │   ├── netty-client/
    │   │   ├── README.md
    │   │   ├── pom.xml
    │   │   └── src/
    │   │       ├── main/
    │   │       │   ├── java/
    │   │       │   │   └── org/
    │   │       │   │       └── yyx/
    │   │       │   │           └── netty/
    │   │       │   │               ├── NettyClientApplication.java
    │   │       │   │               ├── action/
    │   │       │   │               │   └── MainAction.java
    │   │       │   │               ├── client/
    │   │       │   │               │   ├── NettyClient.java
    │   │       │   │               │   ├── NettyClientHandler.java
    │   │       │   │               │   ├── NettyClientHandlerAdapter.java
    │   │       │   │               │   ├── NettyClientListener.java
    │   │       │   │               │   └── RPCProxyFactoryBean.java
    │   │       │   │               ├── config/
    │   │       │   │               │   ├── NettyConfig.java
    │   │       │   │               │   └── NettyConfiguration.java
    │   │       │   │               └── rpc/
    │   │       │   │                   ├── service/
    │   │       │   │                   │   └── DemoService.java
    │   │       │   │                   └── util/
    │   │       │   │                       ├── ChannelUtil.java
    │   │       │   │                       ├── NettyBeanScanner.java
    │   │       │   │                       ├── PackageClassUtils.java
    │   │       │   │                       ├── RemoteMethodInvokeUtil.java
    │   │       │   │                       └── WrapMethodUtils.java
    │   │       │   └── resources/
    │   │       │       └── application.yml
    │   │       └── test/
    │   │           └── java/
    │   │               └── org/
    │   │                   └── yyx/
    │   │                       └── netty/
    │   │                           └── client/
    │   │                               └── NettyClientApplicationTests.java
    │   ├── netty-server/
    │   │   ├── README.md
    │   │   ├── pom.xml
    │   │   └── src/
    │   │       ├── main/
    │   │       │   ├── java/
    │   │       │   │   └── org/
    │   │       │   │       └── yyx/
    │   │       │   │           └── netty/
    │   │       │   │               ├── NettyServerApplication.java
    │   │       │   │               ├── rpc/
    │   │       │   │               │   └── service/
    │   │       │   │               │       └── DemoService.java
    │   │       │   │               └── server/
    │   │       │   │                   ├── adapter/
    │   │       │   │                   │   └── ServerChannelHandlerAdapter.java
    │   │       │   │                   ├── config/
    │   │       │   │                   │   └── NettyServerConfig.java
    │   │       │   │                   ├── dispatcher/
    │   │       │   │                   │   └── RequestDispatcher.java
    │   │       │   │                   ├── impl/
    │   │       │   │                   │   └── DemoServiceImpl.java
    │   │       │   │                   └── listener/
    │   │       │   │                       └── NettyServerListener.java
    │   │       │   └── resources/
    │   │       │       └── application.yml
    │   │       └── test/
    │   │           └── java/
    │   │               └── org/
    │   │                   └── yyx/
    │   │                       └── netty/
    │   │                           └── NettyServerApplicationTests.java
    │   └── pom.xml
    ├── pom.xml
    └── study-netty/
        ├── pom.xml
        ├── study-client/
        │   ├── pom.xml
        │   └── src/
        │       ├── main/
        │       │   ├── java/
        │       │   │   └── org/
        │       │   │       └── yyx/
        │       │   │           └── netty/
        │       │   │               └── study/
        │       │   │                   ├── StudyClientApplication.java
        │       │   │                   ├── codec/
        │       │   │                   │   └── msgpack/
        │       │   │                   │       ├── MsgPackDecoder.java
        │       │   │                   │       └── MsgPackEncoder.java
        │       │   │                   ├── echo/
        │       │   │                   │   ├── delimiter/
        │       │   │                   │   │   ├── EchoClient.java
        │       │   │                   │   │   └── EchoClientHandler.java
        │       │   │                   │   ├── fixlength/
        │       │   │                   │   │   ├── EchoClient.java
        │       │   │                   │   │   └── EchoClientHandler.java
        │       │   │                   │   └── megpack/
        │       │   │                   │       ├── MessagePackClient.java
        │       │   │                   │       └── MessagePackClientHandler.java
        │       │   │                   ├── time/
        │       │   │                   │   ├── demo1/
        │       │   │                   │   │   ├── TimeClient.java
        │       │   │                   │   │   └── TimeClientHandler.java
        │       │   │                   │   ├── demo2/
        │       │   │                   │   │   ├── TimeClient.java
        │       │   │                   │   │   └── TimeClientHandler.java
        │       │   │                   │   └── demo3/
        │       │   │                   │       ├── TimeClient.java
        │       │   │                   │       └── TimeClientHandler.java
        │       │   │                   └── websocket/
        │       │   │                       ├── WebSocketClient.java
        │       │   │                       ├── WebSocketClientHandler.java
        │       │   │                       ├── WebSocketConstant.java
        │       │   │                       ├── WebSocketHandlerClient.java
        │       │   │                       ├── WebSocketMessage.java
        │       │   │                       └── WebSocketUsers.java
        │       │   └── resources/
        │       │       └── application.properties
        │       └── test/
        │           └── java/
        │               └── org/
        │                   └── yyx/
        │                       └── netty/
        │                           └── study/
        │                               └── StudyClientApplicationTests.java
        └── study-server/
            ├── pom.xml
            └── src/
                ├── main/
                │   ├── java/
                │   │   └── org/
                │   │       └── yyx/
                │   │           └── netty/
                │   │               └── study/
                │   │                   ├── StudyServerApplication.java
                │   │                   ├── codec/
                │   │                   │   └── msgpack/
                │   │                   │       ├── MsgPackDecoder.java
                │   │                   │       └── MsgPackEncoder.java
                │   │                   ├── echo/
                │   │                   │   ├── delimiter/
                │   │                   │   │   ├── EchoServer.java
                │   │                   │   │   └── EchoServerHandler.java
                │   │                   │   ├── fixlength/
                │   │                   │   │   ├── EchoServer.java
                │   │                   │   │   └── EchoServerHandler.java
                │   │                   │   └── megpack/
                │   │                   │       ├── MessagePackServer.java
                │   │                   │       └── MessagePackServerHandler.java
                │   │                   ├── time/
                │   │                   │   ├── demo1/
                │   │                   │   │   ├── ChildChannelHandler.java
                │   │                   │   │   ├── TimeServer.java
                │   │                   │   │   └── TimeServerHandler.java
                │   │                   │   ├── demo2/
                │   │                   │   │   ├── ChildChannelHandler.java
                │   │                   │   │   ├── TimeServer.java
                │   │                   │   │   └── TimeServerHandler.java
                │   │                   │   └── demo3/
                │   │                   │       ├── ChildChannelHandler.java
                │   │                   │       ├── TimeServer.java
                │   │                   │       └── TimeServerHandler.java
                │   │                   └── websocket/
                │   │                       ├── WebSocketChildHandler.java
                │   │                       ├── WebSocketConstant.java
                │   │                       ├── WebSocketMessage.java
                │   │                       ├── WebSocketServer.java
                │   │                       ├── WebSocketServerHandler.java
                │   │                       └── WebSocketUsers.java
                │   └── resources/
                │       └── application.properties
                └── test/
                    └── java/
                        └── org/
                            └── yyx/
                                └── netty/
                                    └── study/
                                        ├── CodeCTest.java
                                        └── StudyServerApplicationTests.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*.DS_Store
*.iml
.idea/
target/



================================================
FILE: README.md
================================================
# spring boot netty

### 资源库说明

首先,不是一个项目.而是一个DEMO性质的代码分享.

**当前资源库中包含了两大部分**

1. netty-demo下的可以在项目中使用的netty与SpringBoot集成的demo
2. 在study-netty下的netty相关的基础学习demo

## netty-demo

#### 简述
这是我之前博客中的集成代码,当然,如今的代码已经不完全是博文中所述那样.这里做了相应的优化与升级.主要包含以下几点:

1. 性能优化,不再循环中与服务端创建连接.
2. 代码优化,客户端代码添加了更多的注释,以便理解这样的做法.
3. 功能优化,添加了保持长连接的心跳检测功能,并添加了测试类模拟测试.

原博文地址: [开源中国](https://my.oschina.net/yzwjyw/blog/1614889) [CSDN](http://blog.csdn.net/yuanzhenwei521/article/details/79194275) [博客园](http://www.cnblogs.com/tdg-yyx/p/8376842.html)

#### PS

具体项目说明详见内部项目的说明文档

## study-netty

#### 简述

当前目录下的项目为netty基础学习demo.

#### PS

具体项目说明详见内部项目的说明文档

## End

#### 作者QQ: 562638362

#### 作者邮箱:marquis_xuan@163.com



================================================
FILE: spring-boot-netty/netty-commons/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-boot-netty</artifactId>
        <groupId>org.yyx.netty</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty-commons</artifactId>


</project>

================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/MethodInvokeMeta.java
================================================
package org.yyx.netty.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

/**
 * 记录调用方法的元信息
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/14 - 22:46
 */
@Data
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class MethodInvokeMeta implements Serializable {
    private static final long serialVersionUID = 5429914235135594820L;
    /**
     * 接口
     */
    private Class<?> interfaceClass;
    /**
     * 方法名
     */
    private String methodName;
    /**
     * 参数
     */
    private Object[] args;
    /**
     * 返回值类型
     */
    private Class<?> returnType;
    /**
     * 参数类型
     */
    private Class<?>[] parameterTypes;

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public Class<?> getInterfaceClass() {
        return interfaceClass;
    }

    public void setInterfaceClass(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    public Class getReturnType() {
        return returnType;
    }

    public void setReturnType(Class returnType) {
        this.returnType = returnType;
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/NullWritable.java
================================================
package org.yyx.netty.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * 服务器可能返回空的处理
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 11:57
 */
@Data
public class NullWritable implements Serializable {
    /**
     * 序列化标识
     */
    private static final long serialVersionUID = 2123827169429254101L;
    /**
     * 单例
     */
    private static NullWritable instance = new NullWritable();

    /**
     * 私有构造器
     */
    private NullWritable() {
    }

    /**
     * 返回代表Null的对象
     *
     * @return {@link NullWritable} 当方法返回值为void时或返回值为null时返回此对象
     */
    public static NullWritable nullWritable() {
        return instance;
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/User.java
================================================
package org.yyx.netty.entity;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.msgpack.annotation.Message;

import java.io.Serializable;
import java.nio.ByteBuffer;

/**
 * 用户实体
 * <p>
 * create by 叶云轩 at 2018/3/3-下午1:48
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 11:55
 */
@Data
@NoArgsConstructor
@Message
public class User implements Serializable {
    /**
     * 序列化标识
     */
    private static final long serialVersionUID = -5462474276911290451L;
    /**
     * 编号
     */
    private int id;
    /**
     * 姓名
     */
    private String name;
    /**
     * 分数
     */
    private double source;
    /**
     * 领导
     */
    private User leader;

    public byte[] codeC() {
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        byte[] bytes = this.name.getBytes();
        allocate.putInt(bytes.length);
        allocate.put(bytes);
        allocate.putInt(this.id);
        allocate.flip();
        bytes = null;
        byte[] res = new byte[allocate.remaining()];
        allocate.get(res);
        return res;
    }

    public byte[] codeC(ByteBuffer buffer) {
        buffer.clear();
        byte[] bytes = this.name.getBytes();
        buffer.putInt(bytes.length);
        buffer.put(bytes);
        buffer.putInt(this.id);
        buffer.flip();
        bytes = null;
        byte[] res = new byte[buffer.remaining()];
        buffer.get(res);
        return res;
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/ErrorParamsException.java
================================================
package org.yyx.netty.exception;

/**
 * 参数错误异常
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-14:41
 */
public class ErrorParamsException extends RuntimeException {
    private static final long serialVersionUID = -623198335011996153L;

    public ErrorParamsException() {
        super();
    }

    public ErrorParamsException(String message) {
        super(message);
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/NoUseableChannel.java
================================================
package org.yyx.netty.exception;

/**
 * 没有可用的通道异常
 * <p>
 *
 * @author 叶云轩 at marquis_xuan@163.com
 * @date 2018/11/2-16:00
 */
public class NoUseableChannel extends RuntimeException{
    private static final long serialVersionUID = 7762465537123947683L;

    public NoUseableChannel() {
        super();
    }

    public NoUseableChannel(String message) {
        super(message);
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectCodec.java
================================================
package org.yyx.netty.util;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.util.List;

/**
 * <p>
 * create by 叶云轩 at 2018/3/3-下午1:42
 * contact by tdg_yyx@foxmail.com
 */
public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) {
        byte[] data = ObjectSerializerUtils.serilizer(msg);
        ByteBuf buf = Unpooled.buffer();
        buf.writeBytes(data);
        out.add(buf);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
        byte[] bytes = new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        Object deSerilizer = ObjectSerializerUtils.deSerilizer(bytes);
        out.add(deSerilizer);
    }
}


================================================
FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectSerializerUtils.java
================================================
package org.yyx.netty.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * 对象序列化工具
 * <p>
 * create by 叶云轩 at 2018/3/3-下午1:43
 * contact by tdg_yyx@foxmail.com
 */
public class ObjectSerializerUtils {
    /**
     * ObjectSerializerUtils 日志控制器
     * Create by 叶云轩 at 2018/3/3 下午1:43
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectSerializerUtils.class);

    /**
     * 反序列化
     *
     * @param data
     * @return
     */
    public static Object deSerilizer(byte[] data) {
        if (data != null && data.length > 0) {
            try {
                ByteArrayInputStream bis = new ByteArrayInputStream(data);
                ObjectInputStream ois = new ObjectInputStream(bis);
                return ois.readObject();
            } catch (Exception e) {
                LOGGER.info("[异常信息] {}", e.getMessage());
                e.printStackTrace();
            }
            return null;
        } else {
            LOGGER.info("[反序列化] 入参为空");
            return null;
        }
    }

    /**
     * 序列化对象
     *
     * @param obj
     * @return
     */
    public static byte[] serilizer(Object obj) {
        if (obj != null) {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(obj);
                oos.flush();
                oos.close();
                return bos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        } else {
            return null;
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/README.md
================================================
# Netty-client

> 该项目源自博主项目中实际应用场景。主要用作RPC通信中的客户端处理。

## 阅读说明

### 阅读顺序:

netty-client阅读顺序参考以下链条

```java
NettyConfiguration -> NettyBeanScanner -> PackageClassUtils(工具) -> RPCProxyFactoryBean -> WrapMethodUtils(工具) -> NettyClient -> CustomChannelInitializerClient -> ClientChannelHandlerAdapter
```

由NettyConfiguration入手,获取配置文件中的相关配置。

### 项目阅读

主要见博客:

[叶云轩的知识库](http://xuan.wp.happyqing.com/2018/09/17/netty%E4%B8%8Espring-boot%E7%9A%84%E6%95%B4%E5%90%88/)、[开源中国](https://my.oschina.net/yzwjyw/blog/1614889)、[CSDN](http://blog.csdn.net/yuanzhenwei521/article/details/79194275)、[博客园](http://www.cnblogs.com/tdg-yyx/p/8376842.html)

**更直接的参见源码注释。**

### 更新说明

------

#### 2018-11-01

1. 添加阅读思路,整理待优化细节

------



================================================
FILE: spring-boot-netty/netty-demo/netty-client/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>netty-client</name>
    <description>netty-client with spring boot</description>

    <parent>
        <groupId>org.yyx.netty</groupId>
        <artifactId>netty-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.yyx.netty</groupId>
            <artifactId>netty-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/NettyClientApplication.java
================================================
package org.yyx.netty;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:29
 */
@SpringBootApplication
public class NettyClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(NettyClientApplication.class, args);
    }
}

================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/action/MainAction.java
================================================
package org.yyx.netty.action;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yyx.netty.entity.User;
import org.yyx.netty.rpc.service.DemoService;

import javax.annotation.Resource;

/**
 * 主要用来进行模拟测试的类.就不用写接口来进行测试了
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:05
 */
@Component
public class MainAction {

    /**
     * MainAction 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MainAction.class);

    /**
     * 测试业务
     */
    @Resource
    private DemoService demoService;

    /**
     * 真正远程调用的方法
     * @throws InterruptedException interruptedException
     */
    public void call() throws InterruptedException {
        // 用于模拟服务器正常启动后,人工调用远程服务代码
        Thread.sleep(10 * 1000);
        LOGGER.warn("[准备进行业务测试]");
        LOGGER.warn("[rpc测试] ");
        int sum = demoService.sum(5, 8);
        LOGGER.warn("[rpc测试结果] {}", sum);
        LOGGER.warn("[字符串测试] ");
        String print = demoService.print();
        LOGGER.warn("[字符串测试结果] {}", print);
        LOGGER.warn("[对象测试] ");
        User userInfo = demoService.getUserInfo();
        LOGGER.warn("[对象测试结果] {}", userInfo);
        LOGGER.warn("[异常测试]");
        try {
            double division = demoService.division(3, 0);
            LOGGER.warn("[异常测试结果] {}", division);
        } catch (Exception e) {
            LOGGER.error("[服务器异常] {}", e.getMessage());
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClient.java
================================================
package org.yyx.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * netty客户端第二个版本
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:08
 */
public class NettyClient {
    /**
     * NettyClient 志控制器
     * Create by 叶云轩 at 2018/3/3 下午2:08
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class);
    private static int retry = 0;
    /**
     * 初始化Bootstrap实例, 此实例是netty客户端应用开发的入口
     */
    private Bootstrap bootstrap;
    /**
     * 工人线程组
     */
    private EventLoopGroup worker;
    /**
     * 远程端口
     */
    private int port;
    /**
     * 远程服务器url
     */
    private String url;
    /**
     * 默认重连机制为10次
     */
    private int MAX_RETRY_TIMES = 10;

    public NettyClient(int port, String url) {
        this.port = port;
        this.url = url;
        bootstrap = new Bootstrap();
        worker = new NioEventLoopGroup();
        bootstrap.group(worker);
        bootstrap.channel(NioSocketChannel.class);
    }

    public void start() {
        LOGGER.info("{} -> [启动连接] {}:{}", this.getClass().getName(), url, port);
        bootstrap.handler(new NettyClientHandler());
        ChannelFuture f = bootstrap.connect(url, port);
        try {
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            retry++;
            if (retry > MAX_RETRY_TIMES) {
                throw new RuntimeException("调用Wrong");
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                LOGGER.info("第{}次尝试....失败", retry);
                start();
            }
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandler.java
================================================
package org.yyx.netty.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.timeout.IdleStateHandler;
import org.yyx.netty.util.ObjectCodec;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:17
 */
public class NettyClientHandler extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 基于定长的方式解决粘包/拆包问题
        pipeline.addLast(new LengthFieldPrepender(2));
        pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, 0, 2));
        // 序列化方式 可使用 MsgPack 或 Protobuf 进行序列化扩展 具体可参考study-netty项目下的相关使用例子
        pipeline.addLast(new ObjectCodec());
        // 心跳机制
        pipeline.addLast(new IdleStateHandler(3, 10, 0, TimeUnit.SECONDS));
        pipeline.addLast(new NettyClientHandlerAdapter());
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandlerAdapter.java
================================================
package org.yyx.netty.client;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.rpc.util.ChannelUtil;

/**
 * 自定义的NettyClientHandler
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:20
 */
public class NettyClientHandlerAdapter extends ChannelHandlerAdapter {

    /**
     * NettyClientHandlerAdapter 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientHandlerAdapter.class);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("{} -> [通道异常] {}", this.getClass().getName(), ctx.channel().id());
        ChannelUtil.remove(ctx.channel());
    }

    @Override
    public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception {
        LOGGER.info("{} -> [连接建立成功] {}", this.getClass().getName(), channelHandlerContext.channel().id());
        // 注册通道
        ChannelUtil.registerChannel(channelHandlerContext.channel());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (!(msg instanceof Exception)) {
            LOGGER.info("{} -> [客户端收到的消息] {}", this.getClass().getName(), msg);
        }
        String longText = ctx.channel().id().asLongText();
        String resultKey = ChannelUtil.getResultKey(longText);
        ChannelUtil.calculateResult(resultKey, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("{} -> [客户端消息接收完毕] {}", this.getClass().getName(), ctx.channel().id());
        super.channelReadComplete(ctx);
        boolean active = ctx.channel().isActive();
        LOGGER.info("{} -> [此时通道状态] {}", this.getClass().getName(), active);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        LOGGER.info("{} -> [客户端心跳监测发送] 通道编号:{}", this.getClass().getName(), ctx.channel().id());
        if (evt instanceof IdleStateEvent) {
            ctx.writeAndFlush("ping-pong-ping-pong");
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        LOGGER.info("{} -> [关闭通道] {}", this.getClass().getName(), ctx.channel().id());
        super.close(ctx, promise);
//        ChannelUtil.remove(ctx.channel());
    }
}

================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientListener.java
================================================
package org.yyx.netty.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.yyx.netty.action.MainAction;
import org.yyx.netty.config.NettyConfig;

import javax.annotation.Resource;

/**
 * netty客户端监听器
 * <p>
 * 主要用于延迟测试RPC和启动NettyClient
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:03
 */
@Order(0)
@Component
public class NettyClientListener implements CommandLineRunner {
    /**
     * NettyClientListener 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientListener.class);

    /**
     * netty客户端配置
     */
    @Resource
    private NettyConfig nettyConfig;
    /**
     * 主要用于测试RPC场景的类。集成到自己的业务中就不需要此依赖
     */
    @Resource
    private MainAction mainAction;

    @Override
    public void run(String... args) throws Exception {
        LOGGER.info("{} -> [准备进行与服务端通信]", this.getClass().getName());
        // region 模拟RPC场景
        Thread t1 = new Thread(() -> {
            try {
                mainAction.call();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 使用一个线程模拟Client启动完毕后RPC的场景
        t1.start();
        // endregion
        // 获取服务器监听的端口
        int port = nettyConfig.getPort();
        // 获取服务器IP地址
        String url = nettyConfig.getUrl();
        // 启动NettyClient
        new NettyClient(port, url).start();
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/RPCProxyFactoryBean.java
================================================
package org.yyx.netty.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.yyx.netty.entity.MethodInvokeMeta;
import org.yyx.netty.exception.ErrorParamsException;
import org.yyx.netty.rpc.util.ChannelUtil;
import org.yyx.netty.rpc.util.WrapMethodUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * JDK动态代理类
 * <p>
 *
 * @author 叶云轩 contact by marquis_xuan@163.com
 * @date 2018/11/1 - 15:49
 */
public class RPCProxyFactoryBean extends AbstractFactoryBean<Object> implements InvocationHandler {
    /**
     * RPCProxyFactoryBean 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RPCProxyFactoryBean.class);
    /**
     * 远程服务接口
     */
    private Class interfaceClass;

    @Override
    public Class<?> getObjectType() {
        return interfaceClass;
    }

    /**
     * 创建实例的方法
     *
     * @return 由工厂创建的实例
     */
    @Override
    protected Object createInstance() {
        LOGGER.info("[代理工厂] 初始化代理Bean : {}", interfaceClass);
        // 返回代理类
        return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
    }

    /**
     * 动态调用方法的方法
     * 该方法不会显示调用
     *
     * @param proxy  被代理的实例
     * @param method 调用的方法
     * @param args   参数列表
     * @return 返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws ErrorParamsException {
        LOGGER.info("{} -> [准备进行远程服务调用] ", this.getClass().getName());
        LOGGER.info("{} -> [封装调用信息] ", this.getClass().getName());
        final MethodInvokeMeta methodInvokeMeta = WrapMethodUtils.readMethod(interfaceClass, method, args);
        LOGGER.info("{} -> [远程服务调用封装完毕] 调用接口 -> {}\n调用方法 -> {}\n参数列表 -> {} \n 参数类型 -> {}" +
                        "\n 返回值类型 -> {}", this.getClass().getName(), methodInvokeMeta.getInterfaceClass(), methodInvokeMeta.getMethodName()
                , methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType());
        // 构造一个时间戳
        String uuid = System.currentTimeMillis() + UUID.randomUUID().toString();
        // 真正开始使用netty进行通信的方法
        ChannelUtil.remoteCall(methodInvokeMeta, uuid);
        Object result;
        do {
            // 接收返回信息
            result = ChannelUtil.getResult(uuid);
        } while (result == null);
        // 服务器有可能返回异常信息,所以在这里可以进行异常处理
        if (result instanceof ErrorParamsException) {
            throw (ErrorParamsException) result;
        }
        return result;
    }

    public void setInterfaceClass(Class interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

}

================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfig.java
================================================
package org.yyx.netty.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * netty客户端配置
 * <p>
 *
 * @author 叶云轩 at tdg_yyx@foxmail.com
 * @date 2018/11/1-17:13
 */
@Component
@ConfigurationProperties(prefix = "netty")
@Data
public class NettyConfig {

    private String url;

    private int port;
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfiguration.java
================================================
package org.yyx.netty.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.yyx.netty.rpc.util.NettyBeanScanner;

/**
 * Netty相关的初始化入口
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:32
 */
@Configuration
public class NettyConfiguration {

    /**
     * 初始化加载Netty相关bean的配置方法
     *
     * @param basePackage 配置的包名
     * @param clientName  配置的Netty实例对象名
     * @return NettyBeanScanner
     */
    @Bean
    public static NettyBeanScanner initNettyBeanScanner(@Value("${netty.basePackage}") String basePackage,
                                                        @Value("${netty.clientName}") String clientName) {
        // 创建对象
        return new NettyBeanScanner(basePackage, clientName);
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/service/DemoService.java
================================================
package org.yyx.netty.rpc.service;

import org.yyx.netty.entity.User;
import org.yyx.netty.exception.ErrorParamsException;

/**
 * 测试Service
 * <p>
 * create by 叶云轩 at 2018/3/3-下午1:46
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:29
 */
public interface DemoService {

    /**
     * 除法运算
     *
     * @param numberA 第一个数
     * @param numberB 第二个数
     * @return 结果
     */
    double division(int numberA, int numberB) throws ErrorParamsException;

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    User getUserInfo();

    /**
     * 打印方法
     *
     * @return 一个字符串
     */
    String print();

    /**
     * 求和方法
     *
     * @param numberA 第一个数
     * @param numberB 第二个数
     * @return 两数之和
     */
    int sum(int numberA, int numberB);
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/ChannelUtil.java
================================================
package org.yyx.netty.rpc.util;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.entity.MethodInvokeMeta;
import org.yyx.netty.exception.NoUseableChannel;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * 通道Util
 * <p>
 *
 * @author 叶云轩 at marquis_xuan@163.com
 * @date 2018/11/2-15:39
 */
public class ChannelUtil {
    /**
     * 用于记录c-s连接后建立的通道
     */
    private static final Set<Channel> CHANNELS = new ConcurrentSkipListSet<>();
    /**
     * ChannelUtil日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ChannelUtil.class);
    /**
     * 用于记录通道响应的结果集
     */
    private static final Map<String, Object> RESULT_MAP = new ConcurrentHashMap<>();

    private ChannelUtil() {
    }

    /**
     * 计算结果集(存储响应结果)
     *
     * @param key    唯一标识
     * @param result 结果集
     */
    public static void calculateResult(String key, Object result) {
        RESULT_MAP.put(key, result);
    }

    /**
     * 获取结果集的key
     *
     * @param key 保存的唯一标识
     * @return 结果集的 key (通道标识)
     */
    public static String getResultKey(String key) {
        return (String) getResult(key);
    }

    /**
     * 根据结果集的 key 获取结果集
     *
     * @param key 结果集的key
     * @return 结果集
     */
    public static Object getResult(String key) {
        return RESULT_MAP.get(key);
    }

    /**
     * 注册通道
     *
     * @param channel 通道
     */
    public static void registerChannel(Channel channel) {
        ChannelId id = channel.id();
        LOGGER.info("{} -> [添加通道] {}", ChannelUtil.class.getName(), id);
        CHANNELS.add(channel);
    }

    /**
     * 获取回调结果
     *
     * @param methodInvokeMeta 远程调用方法信息
     * @param key              用于取结果的key值
     */
    public static void remoteCall(MethodInvokeMeta methodInvokeMeta, String key) {
        LOGGER.info("{} -> [远程调用] ", ChannelUtil.class.getName());
        Iterator<Channel> iterator = CHANNELS.iterator();
        Channel channel;
        if (iterator.hasNext()) {
            channel = iterator.next();
        } else {
            LOGGER.error("{} -> [没有活跃的通道] ", ChannelUtil.class);
            throw new NoUseableChannel("没有活跃的通道");
        }
        // 将用于获取结果的key保存,以通道id为键
        String channelID = channel.id().asLongText();
        LOGGER.info("{} -> [保存获取结果的key] key - {} 通道id - {}", ChannelUtil.class, key, channelID);
        RESULT_MAP.put(channelID, key);
        ChannelFuture channelFuture = channel.writeAndFlush(methodInvokeMeta);
//        channelFuture.addListener(ChannelFutureListener.CLOSE);
    }

    public static void remove(Channel channel) {
        CHANNELS.remove(channel);
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/NettyBeanScanner.java
================================================
package org.yyx.netty.rpc.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.yyx.netty.client.RPCProxyFactoryBean;

import java.util.List;

/**
 * 主要用于Netty框架初始化远程服务类
 * <p>
 * BeanFactoryPostProcessor : Spring初始化bean时对外暴露的扩展点,即可以在Spring工厂初始化的时候做点什么,属于Spring知识点
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:28
 */
public class NettyBeanScanner implements BeanFactoryPostProcessor {

    /**
     * 装载bean的工厂
     */
    private DefaultListableBeanFactory beanFactory;
    /**
     * 包名
     */
    private String basePackage;
    /**
     * bean名(引用名)
     */
    private String clientName;

    /**
     * 有参构造
     *
     * @param basePackage 待扫描包名
     * @param clientName  netty客户端beanName
     */
    public NettyBeanScanner(String basePackage, String clientName) {
        this.basePackage = basePackage;
        this.clientName = clientName;
    }


    /**
     * 注册远程接口Bean到Spring的bean工厂
     *
     * @param beanFactory 装载bean的工厂
     * @throws BeansException bean异常
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
        // 从目录中加载远程服务的接口
        List<String> resolverClass = PackageClassUtils.resolver(basePackage);
        for (String clazz : resolverClass) {
            // 获取接口名
            String simpleName;
            // 接口全限定名
            if (clazz.lastIndexOf('.') != -1) {
                simpleName = clazz.substring(clazz.lastIndexOf('.') + 1);
            } else {
                simpleName = clazz;
            }
            // 使用建造者模式创建一个Bean定义
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RPCProxyFactoryBean.class);
            // 对应 RPCProxyFactoryBean 类的 interfaceClass 属性
            beanDefinitionBuilder.addPropertyValue("interfaceClass", clazz);
            // 对应 RPCProxyFactoryBean 的nettyClient 属性  --  已删
//            beanDefinitionBuilder.addPropertyReference("nettyClient", clientName);
            // 注册对bean的定义
            this.beanFactory.registerBeanDefinition(simpleName, beanDefinitionBuilder.getRawBeanDefinition());
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/PackageClassUtils.java
================================================
package org.yyx.netty.rpc.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:28
 */
public class PackageClassUtils {
    private final static Logger LOGGER = LoggerFactory.getLogger(PackageClassUtils.class);

    /**
     * 获取一个目录下的所有文件
     *
     * @param s
     * @param file
     * @param classStrs
     */
    private static void getAllFile(String s, File file, List<String> classStrs) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null)
                for (File file1 : files) {
                    getAllFile(s, file1, classStrs);
                }
        } else {
            String path = file.getPath();
            String cleanPath = path.replaceAll("/", ".");
            String fileName = cleanPath.substring(cleanPath.indexOf(s), cleanPath.length());
            LOGGER.info("[加载完成] 类文件:{}", fileName);
            classStrs.add(fileName);
        }
    }

    /**
     * 添加全限定类名到集合
     *
     * @param classStrs 集合
     * @return 类名集合
     */
    private static List<String> getClassReferenceList(List<String> classStrs, File file, String s) {
        File[] listFiles = file.listFiles();
        if (listFiles != null && listFiles.length != 0) {
            for (File file2 : listFiles) {
                if (file2.isFile()) {
                    String name = file2.getName();
                    String fileName = s + "." + name.substring(0, name.lastIndexOf('.'));
                    LOGGER.info("[加载完成] 类文件:{}", fileName);
                    classStrs.add(fileName);
                }
            }
        }
        return classStrs;
    }

    /**
     * 解析包参数
     *
     * @param basePackage 包名
     * @return 包名字符串集合
     */
    public static List<String> resolver(String basePackage) {
        // 以";"分割开多个包名
        String[] splitFHs = basePackage.split(";");
        List<String> classStrs = new ArrayList<>();
        // s: com.yyx.util.*
        for (String s : splitFHs) {
            LOGGER.info("[加载类目录] {}", s);
            // 路径中是否存在".*" com.yyx.util.*
            boolean contains = s.contains(".*");
            if (contains) {
                // 截断星号  com.yyx.util
                String filePathStr = s.substring(0, s.lastIndexOf(".*"));
                // 组装路径 com/yyx/util
                String filePath = filePathStr.replaceAll("\\.", "/");
                // 获取路径 xxx/classes/com/yyx/util
                File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);
                // 获取目录下获取文件
                getAllFile(filePathStr, file, classStrs);
            } else {
                String filePath = s.replaceAll("\\.", "/");
                File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);
                classStrs = getClassReferenceList(classStrs, file, s);
            }
        }
        return classStrs;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/RemoteMethodInvokeUtil.java
================================================
package org.yyx.netty.rpc.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.yyx.netty.entity.MethodInvokeMeta;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:28
 */
public class RemoteMethodInvokeUtil implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object processMethod(MethodInvokeMeta methodInvokeMeta) throws InvocationTargetException, IllegalAccessException {
        Class interfaceClass = methodInvokeMeta.getInterfaceClass();
        Object bean = applicationContext.getBean(interfaceClass);
        Method[] declaredMethods = interfaceClass.getDeclaredMethods();
        Method method = null;
        for (Method declaredMethod : declaredMethods) {
            if (methodInvokeMeta.getMethodName().equals(declaredMethod.getName())) {
                method = declaredMethod;
            }
        }
        Object invoke = method.invoke(bean, methodInvokeMeta.getArgs());
        return invoke;
    }

    @Override
    public void setApplicationContext(ApplicationContext app) throws BeansException {
        applicationContext = app;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/WrapMethodUtils.java
================================================
package org.yyx.netty.rpc.util;


import org.yyx.netty.entity.MethodInvokeMeta;

import java.lang.reflect.Method;

/**
 * 封装接口调用的工具
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:27
 */
public class WrapMethodUtils {
    /**
     * 封装 method 的元数据信息
     *
     * @param interfaceClass 接口类
     * @param method         方法
     * @param args           参数列表
     * @return 封装的对象
     */
    public static MethodInvokeMeta readMethod(Class interfaceClass, Method method, Object[] args) {
        MethodInvokeMeta methodInvokeMeta = new MethodInvokeMeta();
        methodInvokeMeta.setInterfaceClass(interfaceClass);
        methodInvokeMeta.setArgs(args);
        methodInvokeMeta.setMethodName(method.getName());
        methodInvokeMeta.setReturnType(method.getReturnType());
        Class<?>[] parameterTypes = method.getParameterTypes();
        methodInvokeMeta.setParameterTypes(parameterTypes);
        return methodInvokeMeta;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/main/resources/application.yml
================================================
netty:
  port: 12345
  basePackage: org.yyx.netty.rpc.service;
  clientName: nettyClient
  url: 127.0.0.1

================================================
FILE: spring-boot-netty/netty-demo/netty-client/src/test/java/org/yyx/netty/client/NettyClientApplicationTests.java
================================================
package org.yyx.netty.client;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class NettyClientApplicationTests {

	@Test
	public void contextLoads() {
	}

}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/README.md
================================================
# netty-server
>博文源码,基于SOA架构的服务端,使用Netty做RPC通信.

这是一个Netty服务端与SpringBoot集成的Demo.

================================================
FILE: spring-boot-netty/netty-demo/netty-server/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>netty-server</name>
    <description>
        基于SpringBoot的Netty服务端整合代码
    </description>

    <parent>
        <groupId>org.yyx.netty</groupId>
        <artifactId>netty-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>


    <!-- region 依赖 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.yyx.netty</groupId>
            <artifactId>netty-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <!-- endregion -->


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/NettyServerApplication.java
================================================
package org.yyx.netty;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.yyx.netty.server.listener.NettyServerListener;

import javax.annotation.Resource;

/**
 * Netty服务器启动类
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:33
 */
@SpringBootApplication
public class NettyServerApplication implements CommandLineRunner {

    @Resource
    private NettyServerListener nettyServerListener;

    public static void main(String[] args) {
        SpringApplication.run(NettyServerApplication.class, args);
    }

    @Override
    public void run(String... args) {
        nettyServerListener.start();
    }
}

================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/rpc/service/DemoService.java
================================================
package org.yyx.netty.rpc.service;


import org.yyx.netty.entity.User;
import org.yyx.netty.exception.ErrorParamsException;

/**
 * 测试Service
 * <p>
 * create by 叶云轩 at 2018/3/3-下午1:46
 * contact by tdg_yyx@foxmail.com
 */
public interface DemoService {

    /**
     * 除法运算
     *
     * @param numberA 第一个数
     * @param numberB 第二个数
     * @return 结果
     */
    double division(int numberA, int numberB) throws ErrorParamsException;

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    User getUserInfo();

    /**
     * 打印方法
     *
     * @return 一个字符串
     */
    String print();

    /**
     * 求和方法
     *
     * @param numberA 第一个数
     * @param numberB 第二个数
     * @return 两数之和
     */
    int sum(int numberA, int numberB);
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/adapter/ServerChannelHandlerAdapter.java
================================================
package org.yyx.netty.server.adapter;

import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yyx.netty.entity.MethodInvokeMeta;
import org.yyx.netty.server.dispatcher.RequestDispatcher;

import javax.annotation.Resource;

/**
 * NettyServer通道适配器
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:39
 */
@Component
@Sharable
public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter {
    /**
     * ServerChannelHandlerAdapter 日志控制器
     * Create by 叶云轩 at 2018/3/3 下午1:25
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ServerChannelHandlerAdapter.class);
    /**
     * 注入请求分排器
     */
    @Resource
    private RequestDispatcher dispatcher;
    private int lossConnectCount = 0;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        LOGGER.error("{} -> [连接异常] {}通道异常,异常原因:{}", this.getClass().getName(),
                ctx.channel().id(), cause.getMessage());
        ctx.close();
    }

    /**
     * 服务器接收到消息时进行进行的处理
     *
     * @param channelHandlerContext channelHandlerContext
     * @param msg                   msg
     */
    @Override
    public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) {
        if (msg instanceof String) {
            if ("ping-pong-ping-pong".equals(msg)) {
                LOGGER.info("{} -> [心跳监测] {}:通道活跃", this.getClass().getName(), channelHandlerContext.channel().id());
                // 心跳消息
                lossConnectCount = 0;
                return;
            }
        }
        // 转换为MethodInvokeMeta
        MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg;
        LOGGER.info("{} -> [客户端信息] \n 方法名  - > {} \n 参数列表  -> {} \n " +
                        "返回值  ->  {} ", this.getClass().getName(), invokeMeta.getMethodName(), invokeMeta.getArgs()
                , invokeMeta.getReturnType());
        // 具体的处理类
        this.dispatcher.dispatcher(channelHandlerContext, invokeMeta);
    }

    /**
     * 触发器
     *
     * @param channelHandlerContext channelHandlerContext
     * @param evt
     * @throws Exception exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object evt) throws Exception {
        LOGGER.info("{} -> [已经有5秒中没有接收到客户端的消息了]", this.getClass().getName());
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                lossConnectCount++;
                if (lossConnectCount > 2) {
                    LOGGER.info("{} -> [释放不活跃通道] {}", this.getClass().getName(), channelHandlerContext.channel().id());
                    channelHandlerContext.channel().close();
                }
            }
        } else {
            super.userEventTriggered(channelHandlerContext, evt);
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/config/NettyServerConfig.java
================================================
package org.yyx.netty.server.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * Netty服务器配置信息
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:39
 */
@Component
@ConfigurationProperties(prefix = "netty")
public class NettyServerConfig {

    /**
     * 端口
     */
    private int port;
    /**
     * 最大线程数
     */
    private int maxThreads;
    /**
     * 最大数据包长度
     */
    private int maxFrameLength;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getMaxThreads() {
        return maxThreads;
    }

    public void setMaxThreads(int maxThreads) {
        this.maxThreads = maxThreads;
    }

    public int getMaxFrameLength() {
        return maxFrameLength;
    }

    public void setMaxFrameLength(int maxFrameLength) {
        this.maxFrameLength = maxFrameLength;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/dispatcher/RequestDispatcher.java
================================================
package org.yyx.netty.server.dispatcher;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.yyx.netty.entity.MethodInvokeMeta;
import org.yyx.netty.entity.NullWritable;
import org.yyx.netty.exception.ErrorParamsException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 请求分排器
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:37
 */
@Component
public class RequestDispatcher implements ApplicationContextAware {

    /**
     * RequestDispatcher 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestDispatcher.class);
    /**
     * Spring上下文
     */
    private ApplicationContext applicationContext;

    /**
     * 发送
     *
     * @param channelHandlerContext channelHandlerContext
     * @param invokeMeta            invokeMeta:用于记录远程调用的服务信息,即调用哪个接口中的哪个方法
     */
    public void dispatcher(final ChannelHandlerContext channelHandlerContext, final MethodInvokeMeta invokeMeta) {
        ChannelFuture channelFuture = null;
        // 指向的接口类
        Class<?> interfaceClass = invokeMeta.getInterfaceClass();
        // 所调用的方法名
        String name = invokeMeta.getMethodName();
        // 方法的参数列表
        Object[] args = invokeMeta.getArgs();
        // 方法的参数列表按顺序对应的类型
        Class<?>[] parameterTypes = invokeMeta.getParameterTypes();
        // 通过Spring获取实际对象
        Object targetObject = this.applicationContext.getBean(interfaceClass);
        // 声明调用的方法对象
        Method method;
        try {
            // 获取调用的方法对象
            method = targetObject.getClass().getMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            // 尚未执行方法调用出现异常
            LOGGER.error("[获取远程方法异常] {}", e.getMessage());
            // 响应给客户端
            channelFuture = channelHandlerContext.writeAndFlush(e);
            return;
        } finally {
            if (channelFuture != null) {
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        }
        try {
            // 执行方法
            Object result = method.invoke(targetObject, args);
            if (result == null) {
                // 方法没有返回值,返回NullWritable对象
                LOGGER.info("{} -> [方法没有返回值,返回NullWritable对象]", this.getClass().getName());
                channelFuture = channelHandlerContext.writeAndFlush(NullWritable.nullWritable());
            } else {
                // 将方法执行结果写入到Channel
                LOGGER.info("{} -> [返回结果] {}", this.getClass().getName(), result);
                channelFuture = channelHandlerContext.writeAndFlush(result);
            }
            /*
            虽然可以通过ChannelFuture的get()方法获取异步操作的结果,但完成时间是无法预测的,若不设置超时时间则有可能导致线程长时间被阻塞;
            若是不能精确的设置超时时间则可能导致I/O操作中断.
            因此,Netty建议通过GenericFutureListener接口执行异步操作结束后的回调.
             */
            /// ChannelFutureListener.CLOSE = new ChannelFutureListener() {
            ///    @Override
            ///    public void operationComplete(ChannelFuture future) {
            ///        future.channel().close();
            ///    }
            /// };
        } catch (Exception e) {
            Throwable targetException = ((InvocationTargetException) e).getTargetException();
            LOGGER.error("{} -> [方法执行异常] {}", this.getClass().getName());
            if (targetException instanceof ErrorParamsException) {
                LOGGER.error("{} -> [异常信息] {}", this.getClass().getName(), targetException.getMessage());
            }
            channelFuture = channelHandlerContext.writeAndFlush(targetException);
        } finally {
            if (channelFuture != null) {
//                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/impl/DemoServiceImpl.java
================================================
package org.yyx.netty.server.impl;

import org.springframework.stereotype.Service;
import org.yyx.netty.entity.User;
import org.yyx.netty.exception.ErrorParamsException;
import org.yyx.netty.rpc.service.DemoService;

/**
 * 测试Service实现类
 * <p>
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/7/9 - 上午9:36
 */
@Service
public class DemoServiceImpl implements DemoService {

    @Override
    public double division(int numberA, int numberB) throws ErrorParamsException {
        if (numberB == 0) {
            throw new ErrorParamsException("除数不能为0");
        }
        return numberA / numberB;
    }

    @Override
    public User getUserInfo() {
        User leader = new User();
        leader.setId(1);
        leader.setName("上级");
        leader.setSource(100);
        User user = new User();
        user.setSource(80);
        user.setId(0);
        user.setName("基层");
        user.setLeader(leader);
        return user;
    }

    @Override
    public String print() {
        return "这是来自服务器中DemoService接口的print方法打印的消息";
    }

    @Override
    public int sum(int numberA, int numberB) {
        return numberA + numberB;
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/listener/NettyServerListener.java
================================================
package org.yyx.netty.server.listener;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yyx.netty.server.adapter.ServerChannelHandlerAdapter;
import org.yyx.netty.server.config.NettyServerConfig;
import org.yyx.netty.util.ObjectCodec;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * Netty服务器监听器
 * <p>
 * create by 叶云轩 at 2018/3/3-下午12:21
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:26
 */
@Component
public class NettyServerListener {
    /**
     * NettyServerListener 日志控制器
     * Create by 叶云轩 at 2018/3/3 下午12:21
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerListener.class);

    /**
     * 创建bootstrap
     */
    private ServerBootstrap serverBootstrap = new ServerBootstrap();
    /**
     * BOSS
     */
    private EventLoopGroup boss = new NioEventLoopGroup();
    /**
     * Worker
     */
    private EventLoopGroup work = new NioEventLoopGroup();
    /**
     * 通道适配器
     */
    @Resource
    private ServerChannelHandlerAdapter channelHandlerAdapter;
    /**
     * NETT服务器配置类
     */
    @Resource
    private NettyServerConfig nettyConfig;

    /**
     * 关闭服务器方法
     */
    @PreDestroy
    public void close() {
        LOGGER.info("关闭服务器....");
        //优雅退出
        boss.shutdownGracefully();
        work.shutdownGracefully();
    }

    /**
     * 开启及服务线程
     */
    public void start() {
        // 从配置文件中(application.yml)获取服务端监听端口号
        int port = nettyConfig.getPort();
        serverBootstrap.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 100)
                .handler(new LoggingHandler(LogLevel.INFO));
        try {
            //设置事件处理
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    // 添加心跳支持
                    pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
                    // 基于定长的方式解决粘包/拆包问题
                    pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength()
                            , 0, 2, 0, 2));
                    pipeline.addLast(new LengthFieldPrepender(2));
                    // 序列化
                    pipeline.addLast(new ObjectCodec());
                    pipeline.addLast(channelHandlerAdapter);
                }
            });
            LOGGER.info("netty服务器在[{}]端口启动监听", port);
            ChannelFuture f = serverBootstrap.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            LOGGER.info("[出现异常] 释放资源");
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/main/resources/application.yml
================================================
# netty配置
netty:
  # 端口号
  port: 12345
  # 最大线程数
  maxThreads: 1024
  # 数据包的最大长度
  max_frame_length: 65535

================================================
FILE: spring-boot-netty/netty-demo/netty-server/src/test/java/org/yyx/netty/NettyServerApplicationTests.java
================================================
package org.yyx.netty;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class NettyServerApplicationTests {

	@Test
	public void contextLoads() {
	}

}


================================================
FILE: spring-boot-netty/netty-demo/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>netty-demo</name>
    <description>可以正常使用的Netty与SpringBoot集成的Demo</description>

    <parent>
        <groupId>org.yyx.netty</groupId>
        <artifactId>spring-boot-netty</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modules>
        <module>netty-server</module>
        <module>netty-client</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>


================================================
FILE: spring-boot-netty/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.yyx.netty</groupId>
    <artifactId>spring-boot-netty</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>spring-boot-netty</name>
    <description>
        Netty 学习之路
        netty-server是基于Netty开发的一款Server端代码
        netty-client是基于Netty开发的一款Client端代码
        study-netty是学习Netty的相关Demo,记录了从0到1的整合过程等
    </description>

    <modules>
        <module>study-netty</module>
        <module>netty-commons</module>
        <module>netty-demo</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <netty>5.0.0.Alpha2</netty>
        <lombok>1.16.20</lombok>
        <msgpack>0.6.12</msgpack>
        <fastjson>1.2.47</fastjson>
        <jboss.marshalling>2.0.5.Final</jboss.marshalling>
    </properties>

    <dependencies>
        <!-- region spring-boot-starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- endregion -->
        <!-- region test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- endregion -->
        <!-- region netty -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>${netty}</version>
        </dependency>
        <!-- endregion -->
        <!-- region lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok}</version>
        </dependency>
        <!-- endregion -->
        <!-- region jboss-marshalling -->
        <!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling -->
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling</artifactId>
            <version>${jboss.marshalling}</version>
        </dependency>
        <!-- endregion -->
        <!-- region messagePack -->
        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>${msgpack}</version>
        </dependency>
        <!-- endregion -->
        <!-- region fast-json -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson}</version>
        </dependency>
        <!-- endregion -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>


================================================
FILE: spring-boot-netty/study-netty/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>study-netty</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>study-netty</name>
    <description>study netty</description>

    <modules>
        <module>study-server</module>
        <module>study-client</module>
    </modules>

    <parent>
        <groupId>org.yyx.netty</groupId>
        <artifactId>spring-boot-netty</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.yyx.netty</groupId>
            <artifactId>netty-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>


================================================
FILE: spring-boot-netty/study-netty/study-client/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>


	<artifactId>study-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>study-client</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.yyx.netty</groupId>
		<artifactId>study-netty</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/StudyClientApplication.java
================================================
package org.yyx.netty.study;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StudyClientApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(StudyClientApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java
================================================
package org.yyx.netty.study.codec.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * MsgPack解码器
 * <p>
 * create by 叶云轩 at 2018/4/12-下午7:31
 * contact by tdg_yyx@foxmail.com
 */
public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {
    /**
     * MsgPackDecoder 日志控制器
     * Create by 叶云轩 at 2018/5/3 下午3:19
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackDecoder.class);

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        final byte[] array;
        final int length = msg.readableBytes();
        array = new byte[length];
        msg.getBytes(msg.readerIndex(), array, 0, length);
        MessagePack messagePack = new MessagePack();
        out.add(messagePack.read(array));
    }

}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java
================================================
package org.yyx.netty.study.codec.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * MsgPack编码器
 * <p>
 * create by 叶云轩 at 2018/4/12-下午7:29
 * contact by tdg_yyx@foxmail.com
 */
public class MsgPackEncoder extends MessageToByteEncoder<Object> {

    /**
     * MsgPackEncoder 日志控制器
     * Create by 叶云轩 at 2018/5/3 下午3:18
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackEncoder.class);

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        MessagePack messagePack = new MessagePack();
        // 序列化
        byte[] write = new byte[0];
        try {
            write = messagePack.write(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
        out.writeBytes(write);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClient.java
================================================
package org.yyx.netty.study.echo.delimiter;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

/**
 * MessagePackClient
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:28
 * contact by tdg_yyx@foxmail.com
 */
public class EchoClient {

    public void connect(int port, String host) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes());
                            ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect(host,port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            group.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClientHandler.java
================================================
package org.yyx.netty.study.echo.delimiter;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:57
 * contact by tdg_yyx@foxmail.com
 */
public class EchoClientHandler extends ChannelHandlerAdapter {
    static final String ECHO_REQ = "Hi,Welcome to Netty World.$_$";
    /**
     * MessagePackClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:57
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoClientHandler.class);
    /**
     * 计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("--- [异常] {}", cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i = 0; i < 10; i++) {
            ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        LOGGER.info("--- [第{}次接收到服务器的消息] {} | [消息] {}", ++counter, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClient.java
================================================
package org.yyx.netty.study.echo.fixlength;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

/**
 * MessagePackClient
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:28
 * contact by tdg_yyx@foxmail.com
 */
public class EchoClient {

    public void connect(int port, String host) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes());
                            ch.pipeline().addLast(new FixedLengthFrameDecoder(20));
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect(host, port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            group.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClientHandler.java
================================================
package org.yyx.netty.study.echo.fixlength;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:57
 * contact by tdg_yyx@foxmail.com
 */
public class EchoClientHandler extends ChannelHandlerAdapter {
    static final String ECHO_REQ = "Hi,Welcome to Netty World.$_$";
    /**
     * MessagePackClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:57
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoClientHandler.class);
    /**
     * 计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("--- [异常] {}", cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i = 0; i < 10; i++) {
            ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        LOGGER.info("--- [第{}次接收到服务器的消息] {} | [消息] {}", ++counter, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClient.java
================================================
package org.yyx.netty.study.echo.megpack;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.yyx.netty.study.codec.msgpack.MsgPackDecoder;
import org.yyx.netty.study.codec.msgpack.MsgPackEncoder;

/**
 * MessagePackClient
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:28
 * contact by tdg_yyx@foxmail.com
 */
public class MessagePackClient {

    /**
     * 客户端连接方法
     *
     * @param port 端口号
     * @param host 主机名
     *
     * @throws Exception 异常
     */
    public void connect(int port, String host, int sendNumber) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
//                            ch.pipeline().addLast(new LengthFieldPrepender(2));
                            ch.pipeline().addLast(new MsgPackEncoder());
//                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            ch.pipeline().addLast(new MsgPackDecoder());
                            ch.pipeline().addLast(new MessagePackClientHandler(sendNumber));
                        }
                    });
            ChannelFuture future = bootstrap.connect(host, port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            group.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClientHandler.java
================================================
package org.yyx.netty.study.echo.megpack;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.entity.User;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:57
 * contact by tdg_yyx@foxmail.com
 */
public class MessagePackClientHandler extends ChannelHandlerAdapter {

    /**
     * MessagePackClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:57
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackClientHandler.class);

    private final int sendNumber;

    public MessagePackClientHandler(int sendNumber) {
        this.sendNumber = sendNumber;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        LOGGER.error("--- [异常] {}", cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        User[] userInfo = UserInfo();
        ctx.writeAndFlush(userInfo);
        for (User info : userInfo) {
            ctx.write(info);
        }
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        LOGGER.info("--- [收到服务器的消息] {}", msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    private User[] UserInfo() {
        User[] users = new User[sendNumber];
        User user;
        for (int i = 0; i < sendNumber; i++) {
            user = new User();
            user.setId(i);
            user.setName("YYX --->" + i);
            users[i] = user;
        }
        return users;
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClient.java
================================================
package org.yyx.netty.study.time.demo1;

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;

/**
 * Netty时间服务器客户端
 * <p>
 * create by 叶云轩 at 2018/4/12-上午9:53
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClient {

    public void connect(int port, String host) throws Exception {
        // 配置客户端的NIO线程组
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        try {
            // Client辅助启动类
            Bootstrap bootstrap = new Bootstrap();
            // 配置bootstrap
            bootstrap.group(clientGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        /**
                         * 创建NioSocketChannel成功之后,进行初始化时,
                         * 将ChannelHandler设置到ChannelPipeline中,
                         * 同样,用于处理网络I/O事件
                         * @param ch
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            // 发起异步连接操作  同步方法待成功
            ChannelFuture future = bootstrap.connect(host, port).sync();
            // 等待客户端链路关闭
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放NIO线程组
            clientGroup.shutdownGracefully();
        }


    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClientHandler.java
================================================
package org.yyx.netty.study.time.demo1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-上午10:16
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClientHandler extends ChannelHandlerAdapter {

    /**
     * TimeClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午10:16
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class);
    /**
     *
     */
    private final ByteBuf firstMessage;
    /**
     *
     */
    private byte[] req;

    public TimeClientHandler() {
        byte[] req = "QUERY TIME ORDER".getBytes();
        firstMessage = Unpooled.buffer(req.length);
        firstMessage.writeBytes(req);
    }

    /**
     * 捕捉异常
     *
     * @param ctx
     * @param cause
     *
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage());
        ctx.close();
    }

    /**
     * 当客户端和服务端TCP链路建立成功之后调用此方法
     * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端
     *
     * @param ctx
     *
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(firstMessage);
    }

    /**
     * 服务端返回应答消息时,调用此方法
     *
     * @param ctx
     * @param msg
     *
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] request = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(request);
        String body = new String(request, "utf-8");
        LOGGER.info("--- [Now is] {}", body);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClient.java
================================================
package org.yyx.netty.study.time.demo2;

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;

/**
 * Netty时间服务器客户端
 * <p>
 * create by 叶云轩 at 2018/4/12-上午9:53
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClient {

    public void connect(int port, String host) throws Exception {
        // 配置客户端的NIO线程组
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        try {
            // Client辅助启动类
            Bootstrap bootstrap = new Bootstrap();
            // 配置bootstrap
            bootstrap.group(clientGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        /**
                         * 创建NioSocketChannel成功之后,进行初始化时,
                         * 将ChannelHandler设置到ChannelPipeline中,
                         * 同样,用于处理网络I/O事件
                         * @param ch
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            // 发起异步连接操作  同步方法待成功
            ChannelFuture future = bootstrap.connect(host, port).sync();
            // 等待客户端链路关闭
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放NIO线程组
            clientGroup.shutdownGracefully();
        }


    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClientHandler.java
================================================
package org.yyx.netty.study.time.demo2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-上午10:16
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClientHandler extends ChannelHandlerAdapter {

    /**
     * TimeClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午10:16
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class);
    /**
     * 模拟粘包/拆包问题计数器
     */
    private int counter;

    /**
     *
     */
    private byte[] req;

    public TimeClientHandler() {
        // region 模拟粘包/拆包问题相关代码
        req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();
        // endregion
    }

    /**
     * 捕捉异常
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage());
        ctx.close();
    }

    /**
     * 当客户端和服务端TCP链路建立成功之后调用此方法
     * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // region 模拟粘包/拆包问题相关代码
        ByteBuf message;
        for (int i = 0; i < 100; i++) {
            message = Unpooled.buffer(req.length);
            message.writeBytes(req);
            ctx.writeAndFlush(message);
        }
        // endregion
    }

    /**
     * 服务端返回应答消息时,调用此方法
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] request = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(request);
        String body = new String(request, "utf-8");
        LOGGER.info("--- [Now is] {} | [counter] {}", body, ++counter);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClient.java
================================================
package org.yyx.netty.study.time.demo3;

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.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

/**
 * Netty时间服务器客户端
 * <p>
 * create by 叶云轩 at 2018/4/12-上午9:53
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClient {

    public void connect(int port, String host) throws Exception {
        // 配置客户端的NIO线程组
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        try {
            // Client辅助启动类
            Bootstrap bootstrap = new Bootstrap();
            // 配置bootstrap
            bootstrap.group(clientGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        /**
                         * 创建NioSocketChannel成功之后,进行初始化时,
                         * 将ChannelHandler设置到ChannelPipeline中,
                         * 同样,用于处理网络I/O事件
                         * @param ch
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // region 解决粘包/拆包问题相关代码
                            ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            ch.pipeline().addLast(new StringDecoder());
                            // endregion
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            // 发起异步连接操作  同步方法待成功
            ChannelFuture future = bootstrap.connect(host, port).sync();
            // 等待客户端链路关闭
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放NIO线程组
            clientGroup.shutdownGracefully();
        }


    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClientHandler.java
================================================
package org.yyx.netty.study.time.demo3;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-上午10:16
 * contact by tdg_yyx@foxmail.com
 */
public class TimeClientHandler extends ChannelHandlerAdapter {

    /**
     * TimeClientHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午10:16
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class);
    /**
     * 模拟粘包/拆包问题计数器
     */
    private int counter;

    /**
     *
     */
    private byte[] req;

    public TimeClientHandler() {
        // region 模拟粘包/拆包问题相关代码
        req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();
        // endregion
    }

    /**
     * 捕捉异常
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage());
        ctx.close();
    }

    /**
     * 当客户端和服务端TCP链路建立成功之后调用此方法
     * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // region 模拟粘包/拆包问题相关代码
        ByteBuf message;
        for (int i = 0; i < 100; i++) {
            message = Unpooled.buffer(req.length);
            message.writeBytes(req);
            ctx.writeAndFlush(message);
        }
        // endregion
    }

    /**
     * 服务端返回应答消息时,调用此方法
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // region 解决粘包/拆包问题相关代码
        String body = (String) msg;
        // endregion
        LOGGER.info("--- [Now is] {} | [counter] {}", body, ++counter);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClient.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;

import java.net.URI;

/**
 * webSocketClient
 * <p>
 * create by 叶云轩 at 2018/5/17-下午6:04
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketClient {

    public void connect(int port, String host, String userName) throws Exception {
        // 配置客户端的NIO线程组
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        try {
            // Client辅助启动类
            Bootstrap bootstrap = new Bootstrap();
            // 配置bootstrap
            WebSocketClientHandler webSocketClientHandler = new WebSocketClientHandler(
                    WebSocketClientHandshakerFactory.newHandshaker(new URI("ws://172.0.0.1:9999/websocket/" + userName)
                            , WebSocketVersion.V13, null, false, new DefaultHttpHeaders())
            );
            bootstrap.group(clientGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new WebSocketHandlerClient(webSocketClientHandler));
            // 发起异步连接操作  同步方法待成功
            Channel channel = bootstrap.connect(host, port).sync().channel();
            // 等待客户端链路关闭
            channel.closeFuture().sync();
        } finally {
            // 优雅退出,释放NIO线程组
            clientGroup.shutdownGracefully();
        }


    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClientHandler.java
================================================
package org.yyx.netty.study.websocket;


import com.alibaba.fastjson.JSONObject;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/5/17-下午6:06
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
    /**
     * WebSocketClientHandler 日志控制器
     * Create by 叶云轩 at 2018/5/17 下午6:10
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketClientHandler.class);

    private final WebSocketClientHandshaker webSocketClientHandshaker;
    private ChannelPromise handshakeFuture;

    WebSocketClientHandler(WebSocketClientHandshaker webSocketClientHandshaker) {
        this.webSocketClientHandshaker = webSocketClientHandshaker;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        handshakeFuture = ctx.newPromise();
    }

    /**
     * 异常
     *
     * @param channelHandlerContext channelHandlerContext
     * @param cause                 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [exception]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", cause.getMessage());
        channelHandlerContext.close();
    }

    /**
     * 当客户端主动链接服务端的链接后,调用此方法
     *
     * @param channelHandlerContext ChannelHandlerContext
     */
    @Override
    public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception {
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [建立连接]\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");

        Channel channel = channelHandlerContext.channel();
        // 握手
        webSocketClientHandshaker.handshake(channel);
    }

    /**
     * 与服务端断开连接时
     *
     * @param channelHandlerContext channelHandlerContext
     */
    @Override
    public void channelInactive(ChannelHandlerContext channelHandlerContext) {
        Channel channel = channelHandlerContext.channel();
        WebSocketUsers.remove(channel);
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [断开连接]:client [{}]\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channel.remoteAddress());

    }

    /**
     * 读完之后调用的方法
     *
     * @param channelHandlerContext ChannelHandlerContext
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {
        channelHandlerContext.flush();
    }

    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        // 获取通道
        Channel channel = channelHandlerContext.channel();
        // 如果没有握手完成进行握手
        if (!webSocketClientHandshaker.isHandshakeComplete()) {
            webSocketClientHandshaker.finishHandshake(channel, (FullHttpResponse) msg);
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [握手成功]\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");
            handshakeFuture.setSuccess();
            // 将当前登陆用户保存起来
            WebSocketUsers.put("client1-" + getUserNameInPath(), channel);
            return;
        }
        channelHandlerContext.flush();

        if (msg instanceof FullHttpResponse) {
            FullHttpResponse response = (FullHttpResponse) msg;
            throw new IllegalStateException(
                    "Unexpected FullHttpResponse (getStatus=" + response.status() +
                            ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
        }

        WebSocketFrame frame = (WebSocketFrame) msg;

        if (frame instanceof TextWebSocketFrame) {
            TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) frame;
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [服务器响应消息]: {}\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", textWebSocketFrame.text());
            WebSocketMessage webSocketMessage = new WebSocketMessage();
            webSocketMessage.setHeader(WebSocketMessage.Type.send_user);
            webSocketMessage.setAccept("yyx");
            webSocketMessage.setContent("Hello I'm YeYunXuan");
            String string = JSONObject.toJSONString(webSocketMessage);
            WebSocketUsers.sendMessageToUser("client1-YeYunXuan", string);
        }
    }

    /**
     * 获取登陆用户
     *
     * @return 用户名
     */
    private String getUserNameInPath() {
        String path = webSocketClientHandshaker.uri().getPath();
        int i = path.lastIndexOf("/");
        return path.substring(i + 1, path.length());
    }
}



================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java
================================================
package org.yyx.netty.study.websocket;

/**
 * WebSocket常量
 * <p>
 * create by 叶云轩 at 2018/5/18-下午1:29
 * contact by tdg_yyx@foxmail.com
 */
public interface WebSocketConstant {

    /**
     * 点对点
     */
    String SEND_TO_USER = "send_to_user";

    /**
     * 群发 广播
     */
    String SEND_TO_USERS = "send_to_users";

    /**
     * 请求成功
     */
    String REQUEST_SUCCESS = "request_success";
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketHandlerClient.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.yyx.netty.study.codec.msgpack.MsgPackDecoder;
import org.yyx.netty.study.codec.msgpack.MsgPackEncoder;

/**
 * webSocketChildHandler
 * <p>
 * create by 叶云轩 at 2018/5/15-下午4:54
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketHandlerClient extends ChannelInitializer<SocketChannel> {

    private WebSocketClientHandler webSocketClientHandler;

    public WebSocketHandlerClient(WebSocketClientHandler webSocketClientHandler) {
        this.webSocketClientHandler = webSocketClientHandler;
    }

    /**
     * 初始化Channel
     *
     * @param socketChannel socketChannel
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 将请求与应答消息编码或者解码为HTTP消息
        pipeline.addLast(new HttpClientCodec());
        // 将http消息的多个部分组合成一条完整的HTTP消息
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
        // 客户端Handler
        pipeline.addLast("handler", webSocketClientHandler);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java
================================================
package org.yyx.netty.study.websocket;

import lombok.Data;

import java.io.Serializable;

/**
 * WebSocketMessage
 * <p>
 * create by 叶云轩 at 2018/5/18-下午3:02
 * contact by tdg_yyx@foxmail.com
 */
@Data
public class WebSocketMessage implements Serializable {
    private static final long serialVersionUID = -4666429837358506065L;

    private String accept;
    private String content;
    private Type header;

    enum Type {
        send_user, send_users, request_success;
    }


}


================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.internal.PlatformDependent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

/**
 * WebSocket用户集
 */
public class WebSocketUsers {

    /**
     * 用户集
     */
    private static final ConcurrentMap<String, Channel> USERS = PlatformDependent.newConcurrentHashMap();
    /**
     * WebSocketUsers 日志控制器
     * Create by 叶云轩 at 2018/5/15 下午5:41
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);

    private static WebSocketUsers ourInstance = new WebSocketUsers();

    private WebSocketUsers() {
    }

    public static WebSocketUsers getInstance() {
        return ourInstance;
    }

    /**
     * 存储通道
     *
     * @param key     唯一键
     * @param channel 通道
     */
    public static void put(String key, Channel channel) {
        USERS.put(key, channel);
    }

    /**
     * 移除通道
     *
     * @param channel 通道
     *
     * @return 移除结果
     */
    public static boolean remove(Channel channel) {
        String key = null;
        boolean b = USERS.containsValue(channel);
        if (b) {
            Set<Map.Entry<String, Channel>> entries = USERS.entrySet();
            for (Map.Entry<String, Channel> entry : entries) {
                Channel value = entry.getValue();
                if (value.equals(channel)) {
                    key = entry.getKey();
                    break;
                }
            }
        } else {
            return true;
        }
        return remove(key);
    }

    /**
     * 移出通道
     *
     * @param key 键
     */
    public static boolean remove(String key) {
        Channel remove = USERS.remove(key);
        boolean containsValue = USERS.containsValue(remove);
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [移出结果]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", containsValue ? "失败" : "成功");
        return containsValue;
    }

    /**
     * 获取在线用户列表
     *
     * @return 返回用户集合
     */
    public static ConcurrentMap<String, Channel> getUSERS() {
        return USERS;
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    public static void sendMessageToUsers(String message) {
        Collection<Channel> values = USERS.values();
        for (Channel value : values) {
            value.write(new TextWebSocketFrame(message));
            value.flush();
        }
    }

    /**
     * 给某个人发送消息
     *
     * @param userName key
     * @param message  消息
     */
    public static void sendMessageToUser(String userName, String message) {
        Channel channel = USERS.get(userName);
        channel.write(new TextWebSocketFrame(message));
        channel.flush();
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-client/src/main/resources/application.properties
================================================


================================================
FILE: spring-boot-netty/study-netty/study-client/src/test/java/org/yyx/netty/study/StudyClientApplicationTests.java
================================================
package org.yyx.netty.study;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.yyx.netty.study.echo.megpack.MessagePackClient;
import org.yyx.netty.study.websocket.WebSocketClient;
import org.yyx.netty.study.websocket.WebSocketUsers;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudyClientApplicationTests {

    /**
     * 测试端口
     */
    private final int port = 8080;
    /**
     * 测试IP
     */
    private final String host = "127.0.0.1";

    // region 启动未解决粘包/拆包问题的netty客户端
    @Test
    public void startNettyClient1() throws Exception {
        new org.yyx.netty.study.time.demo1.TimeClient().connect(port, host);
    }
    // endregion

    // region 启动模拟粘包/拆包问题的netty客户端
    @Test
    public void startNettyClient2() throws Exception {
        new org.yyx.netty.study.time.demo2.TimeClient().connect(port, host);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty客户端 - LineBasedFrameDecoder 实现
    @Test
    public void startNettyClient3() throws Exception {
        new org.yyx.netty.study.time.demo3.TimeClient().connect(port, host);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty客户端 - DelimiterBasedFrameDecoder 实现
    @Test
    public void startNettyClient4() throws Exception {
        new org.yyx.netty.study.echo.delimiter.EchoClient().connect(port, host);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty服务 - FixedLengthFrameDecoder 实现
    @Test
    public void startNettyServer5() throws Exception {
        new org.yyx.netty.study.echo.fixlength.EchoClient().connect(port, host);
    }
    // endregion

    // region 启动不考虑粘包/拆包问题 基于MessagePack编解码的Netty客户端
    @Test
    public void testMessagePackEchoClient() throws Exception {
        new MessagePackClient().connect(port, host, 20);
    }
    // endregion

    // region 启动WebSocketClient
    @Test
    public void startWebSocketClient() throws Exception {
        new WebSocketClient().connect(9999, host, "yyx");
    }

    @Test
    public void startWebSocketClient2() throws Exception {
        new WebSocketClient().connect(9999, host, "YeYunXuan");
    }
    // endregion
}


================================================
FILE: spring-boot-netty/study-netty/study-server/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>study-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>study-server</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.yyx.netty</groupId>
        <artifactId>study-netty</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/StudyServerApplication.java
================================================
package org.yyx.netty.study;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StudyServerApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(StudyServerApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java
================================================
package org.yyx.netty.study.codec.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * MsgPack解码器
 * <p>
 * create by 叶云轩 at 2018/4/12-下午7:31
 * contact by tdg_yyx@foxmail.com
 */
public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {


    /**
     * MsgPackDecoder 日志控制器
     * Create by 叶云轩 at 2018/5/3 下午3:15
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackDecoder.class);

    /**
     * 解码 byte[] -> Object
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext
            , ByteBuf msg, List<Object> out) throws Exception {
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [解码]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg);
        final int length = msg.readableBytes();
        final byte[] array = new byte[length];
        msg.getBytes(msg.readerIndex(), array, 0, length);
        MessagePack messagePack = new MessagePack();
        out.add(messagePack.read(array));
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [out]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", out);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java
================================================
package org.yyx.netty.study.codec.msgpack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * MsgPack编码器
 * <p>
 * create by 叶云轩 at 2018/4/12-下午7:29
 * contact by tdg_yyx@foxmail.com
 */
public class MsgPackEncoder extends MessageToByteEncoder<Object> {
    /**
     * MsgPackEncoder 日志控制器
     * Create by 叶云轩 at 2018/5/3 下午3:15
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackEncoder.class);

    /**
     * 编码 Object -> byte[]
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception {
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [编码]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg);
        MessagePack messagePack = new MessagePack();
        // 序列化
        byte[] write = messagePack.write(msg);
        out.writeBytes(write);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServer.java
================================================
package org.yyx.netty.study.echo.delimiter;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * EchoServer服务端
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:07
 * contact by tdg_yyx@foxmail.com
 */
public class EchoServer {
    /**
     * MessagePackServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:09
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoServer.class);


    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 设置分隔符为 $_$
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes());
                            // 单条消息最大长度不能超过1024
                            ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServerHandler.java
================================================
package org.yyx.netty.study.echo.delimiter;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:12
 * contact by tdg_yyx@foxmail.com
 */
public class EchoServerHandler extends ChannelHandlerAdapter {

    /**
     * MessagePackServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:20
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoServerHandler.class);
    /**
     * 计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.info("--- [发生异常] 释放资源");
        ctx.close();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String body = (String) msg;
        LOGGER.info("--- [第{}次接收客户端消息] {}", ++counter, body);
        body += "$_$";
        ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
        ctx.writeAndFlush(echo);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServer.java
================================================
package org.yyx.netty.study.echo.fixlength;

import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * EchoServer服务端
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:07
 * contact by tdg_yyx@foxmail.com
 */
public class EchoServer {
    /**
     * MessagePackServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:09
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoServer.class);


    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new FixedLengthFrameDecoder(20));
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServerHandler.java
================================================
package org.yyx.netty.study.echo.fixlength;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:12
 * contact by tdg_yyx@foxmail.com
 */
public class EchoServerHandler extends ChannelHandlerAdapter {

    /**
     * MessagePackServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:20
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoServerHandler.class);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.info("--- [发生异常] 释放资源");
        ctx.close();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        LOGGER.info("--- [接收到客户端的数据] {}", msg);
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServer.java
================================================
package org.yyx.netty.study.echo.megpack;

import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.study.codec.msgpack.MsgPackDecoder;
import org.yyx.netty.study.codec.msgpack.MsgPackEncoder;

/**
 * EchoServer服务端
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:07
 * contact by tdg_yyx@foxmail.com
 */
public class MessagePackServer {
    /**
     * MessagePackServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:09
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackServer.class);


    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
//                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            ch.pipeline().addLast(new MsgPackDecoder());
//                            ch.pipeline().addLast(new LengthFieldPrepender(2));
                            ch.pipeline().addLast(new MsgPackEncoder());
                            ch.pipeline().addLast(new MessagePackServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServerHandler.java
================================================
package org.yyx.netty.study.echo.megpack;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.entity.User;

import java.util.List;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午4:12
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:32
 */
public class MessagePackServerHandler extends ChannelHandlerAdapter {

    /**
     * MessagePackServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午4:20
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackServerHandler.class);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.info("--- [发生异常] 释放资源: {}", cause.getMessage());
        // todo
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush("Server connect success");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        List<User> userInfo = (List<User>) msg;
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [接收 ]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", userInfo);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/ChildChannelHandler.java
================================================
package org.yyx.netty.study.time.demo1;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * I/O事件处理类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:34
 * contact by tdg_yyx@foxmail.com
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    /**
     * ChildChannelHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:29
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class);

    /**
     * 创建NioSocketChannel成功之后,进行初始化时,
     * 将ChannelHandler设置到ChannelPipeline中,
     * 同样,用于处理网络I/O事件
     *
     * @param ch
     *
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        LOGGER.info("--- [通道初始化]");
        ch.pipeline().addLast(new TimeServerHandler());
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServer.java
================================================
package org.yyx.netty.study.time.demo1;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:33
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServer {
    /*
     NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理
     一个用于服务端接受客户端的连接
     一个用于进行SocketChannel的网络读写
     */

    /**
     * TimeServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:26
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class);

    /**
     * 绑定端口
     *
     * @param port 端口号
     * @throws Exception 异常
     */
    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类
                    .channel(NioServerSocketChannel.class)
                    // 配置NioServerSocketChannel的TCP参数 backlog设置为1024
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 绑定I/O事件处理类
                    .childHandler(new ChildChannelHandler());
            // 绑定端口,同步等待成功
            // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            // 等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServerHandler.java
================================================
package org.yyx.netty.study.time.demo1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * 针对网络事件进行读写操作的类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:35
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServerHandler extends ChannelHandlerAdapter {
    /**
     * TimeServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:30
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class);
    /**
     * 模拟粘包/拆包问题计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    /**
     * 读事件
     *
     * @param ctx ChannelHandlerContext
     * @param msg 消息
     *
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 将消息转换成ByteBuf
        ByteBuf buf = (ByteBuf) msg;
        // 获取缓冲区中的字节数
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "utf-8");
        LOGGER.info("--- [接收到的数据] {}", body);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() :
                "BAD ORDER";
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());

        // region 模拟粘包/拆包问题相关代码
//        String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length());
//        LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter);
//        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
//        currentTime = currentTime + System.getProperty("line.separator");
//        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        // endregion


        // 异步发送应答消息给Client
        ctx.write(resp); // --> 将消息放到发送缓冲数组中
    }

    /**
     * 读完之后
     *
     * @param ctx
     *
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("--- [服务器写消息] ");
        // 将消息发送队列中的消息写到SocketChannel中
        ctx.flush(); // --> 将消息写到 SocketChannel 中
    }

}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/ChildChannelHandler.java
================================================
package org.yyx.netty.study.time.demo2;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * I/O事件处理类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:34
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:38
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    /**
     * ChildChannelHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:29
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class);

    /**
     * 创建NioSocketChannel成功之后,进行初始化时,
     * 将ChannelHandler设置到ChannelPipeline中,
     * 同样,用于处理网络I/O事件
     *
     * @param ch
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        LOGGER.info("--- [通道初始化]");
        ch.pipeline().addLast(new TimeServerHandler());
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServer.java
================================================
package org.yyx.netty.study.time.demo2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:33
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServer {
    /*
     NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理
     一个用于服务端接受客户端的连接
     一个用于进行SocketChannel的网络读写
     */

    /**
     * TimeServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:26
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class);

    /**
     * 绑定端口
     *
     * @param port 端口号
     * @throws Exception 异常
     */
    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类
                    .channel(NioServerSocketChannel.class)
                    // 配置NioServerSocketChannel的TCP参数 backlog设置为1024
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 绑定I/O事件处理类
                    .childHandler(new ChildChannelHandler());
            // 绑定端口,同步等待成功
            // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            // 等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServerHandler.java
================================================
package org.yyx.netty.study.time.demo2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * 针对网络事件进行读写操作的类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:35
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServerHandler extends ChannelHandlerAdapter {
    /**
     * TimeServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:30
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class);
    /**
     * 模拟粘包/拆包问题计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    /**
     * 读事件
     *
     * @param ctx ChannelHandlerContext
     * @param msg 消息
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 将消息转换成ByteBuf
        ByteBuf buf = (ByteBuf) msg;
        // 获取缓冲区中的字节数
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        // region 模拟粘包/拆包问题相关代码
        String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length());
        LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
        currentTime = currentTime + System.getProperty("line.separator");
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        // endregion
        // 异步发送应答消息给Client
        ctx.writeAndFlush(resp); // --> 将消息放到发送缓冲数组中
    }

    /**
     * 读完之后
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("--- [服务器写消息] ");
        // 将消息发送队列中的消息写到SocketChannel中
        ctx.flush(); // --> 将消息写到 SocketChannel 中
    }

}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/ChildChannelHandler.java
================================================
package org.yyx.netty.study.time.demo3;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * I/O事件处理类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:34
 * contact by tdg_yyx@foxmail.com
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    /**
     * ChildChannelHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:29
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class);


    /**
     * LineBasedFrameDecoder: 以换行符为结束标志的解码器
     *  依次遍历ByteBuf中的可读字节,
     *  如果有"\n" 或者 "\r\n" -> 就以此位置为结束位置 之后将可读索引到结束位置区间的字节组成一行
     */

    /**
     * 创建NioSocketChannel成功之后,进行初始化时,
     * 将ChannelHandler设置到ChannelPipeline中,
     * 同样,用于处理网络I/O事件
     *
     * @param ch
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        LOGGER.info("--- [通道初始化]");
        // region 解决粘包/拆包问题相关代码
        ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
        // 将接收到的对象转成字符串
        ch.pipeline().addLast(new StringDecoder());
        // endregion

        ch.pipeline().addLast(new TimeServerHandler());
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServer.java
================================================
package org.yyx.netty.study.time.demo3;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:33
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServer {
    /*
     NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理
     一个用于服务端接受客户端的连接
     一个用于进行SocketChannel的网络读写
     */

    /**
     * TimeServer 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:26
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class);

    /**
     * 绑定端口
     *
     * @param port 端口号
     *
     * @throws Exception 异常
     */
    public void bind(int port) throws Exception {
        LOGGER.info("--- [绑定端口] {}", port);
        // 声明Boss线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 声明Worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            LOGGER.info("--- [启动NIO] ");
            // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 将两个NIO线程组传递到ServerBootStrap中
            bootstrap.group(bossGroup, workerGroup)
                    // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类
                    .channel(NioServerSocketChannel.class)
                    // 配置NioServerSocketChannel的TCP参数 backlog设置为1024
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 绑定I/O事件处理类
                    .childHandler(new ChildChannelHandler());
            // 绑定端口,同步等待成功
            // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            // 等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServerHandler.java
================================================
package org.yyx.netty.study.time.demo3;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * 针对网络事件进行读写操作的类
 * <p>
 * create by 叶云轩 at 2018/4/11-下午6:35
 * contact by tdg_yyx@foxmail.com
 */
public class TimeServerHandler extends ChannelHandlerAdapter {
    /**
     * TimeServerHandler 日志控制器
     * Create by 叶云轩 at 2018/4/12 上午11:30
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class);
    /**
     * 模拟粘包/拆包问题计数器
     */
    private int counter;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
    }

    /**
     * 读事件
     *
     * @param ctx ChannelHandlerContext
     * @param msg 消息
     *
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // region 解决模拟粘包/拆包问题相关代码
        String body = (String) msg;
        LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
        currentTime = currentTime + System.getProperty("line.separator");
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        // endregion

        // 异步发送应答消息给Client
        ctx.writeAndFlush(resp); // --> 将消息放到发送缓冲数组中
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketChildHandler.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * webSocketChildHandler
 * <p>
 * create by 叶云轩 at 2018/5/15-下午4:54
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketChildHandler extends ChannelInitializer<SocketChannel> {
    /**
     * 初始化Channel
     *
     * @param socketChannel socketChannel
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 将请求与应答消息编码或者解码为HTTP消息
        pipeline.addLast("http-codec", new HttpServerCodec());
        // 将http消息的多个部分组合成一条完整的HTTP消息
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        // 向客户端发送HTML5文件。主要用于支持浏览器和服务端进行WebSocket通信
        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
        // 服务端Handler
        pipeline.addLast("handler", new WebSocketServerHandler());

    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java
================================================
package org.yyx.netty.study.websocket;

/**
 * WebSocket常量
 * <p>
 * create by 叶云轩 at 2018/5/18-下午1:29
 * contact by tdg_yyx@foxmail.com
 */
public interface WebSocketConstant {

    /**
     * 点对点
     */
    String SEND_TO_USER = "send_to_user";

    /**
     * 群发 广播
     */
    String SEND_TO_USERS = "send_to_users";

    /**
     * 请求成功
     */
    String REQUEST_SUCCESS = "request_success";
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java
================================================
package org.yyx.netty.study.websocket;

import lombok.Data;

import java.io.Serializable;

/**
 * WebSocketMessage
 * <p>
 * create by 叶云轩 at 2018/5/18-下午3:02
 * contact by tdg_yyx@foxmail.com
 */
@Data
public class WebSocketMessage implements Serializable {
    private static final long serialVersionUID = -4666429837358506065L;

    private String accept;
    private String content;
    private Type header;

    enum Type {
        send_user, send_users, request_success;
    }


}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServer.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * webSocket服务器
 * <p>
 * create by 叶云轩 at 2018/5/11-上午11:42
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketServer {
    /**
     * WebSocketServer 日志控制器
     * Create by 叶云轩 at 2018/5/11 上午11:48
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

    public void run(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new WebSocketChildHandler());
            Channel ch = bootstrap.bind(port).sync().channel();
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [服务器启动]: {}\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", port);
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServerHandler.java
================================================
package org.yyx.netty.study.websocket;

import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

/**
 * <p>
 * create by 叶云轩 at 2018/5/11-上午11:49
 * contact by tdg_yyx@foxmail.com
 */
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
    /**
     * WebSocketServerHandler 日志控制器
     * Create by 叶云轩 at 2018/5/11 上午11:50
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServerHandler.class);

    private WebSocketServerHandshaker socketServerHandShaker;

    /**
     * 异常
     *
     * @param channelHandlerContext channelHandlerContext
     * @param cause                 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        channelHandlerContext.close();
    }

    /**
     * 当客户端主动链接服务端的链接后,调用此方法
     *
     * @param channelHandlerContext ChannelHandlerContext
     */
    @Override
    public void channelActive(ChannelHandlerContext channelHandlerContext) {
        // 使用一个结构存储通道
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                        "\t├ [建立连接]: client [{}]\n" +
                        "\t├ [当前在线人数]: {}\n" +
                        "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channelHandlerContext.channel().remoteAddress()
                , WebSocketUsers.getUSERS().size() + 1);
    }

    /**
     * 与客户端断开连接时
     *
     * @param channelHandlerContext channelHandlerContext
     */
    @Override
    public void channelInactive(ChannelHandlerContext channelHandlerContext) {
        Channel channel = channelHandlerContext.channel();
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [断开连接]:client [{}]\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channel.remoteAddress());
        WebSocketUsers.remove(channel);
        ConcurrentMap<String, Channel> users = WebSocketUsers.getUSERS();
        LOGGER.info("\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");
        for (String s : users.keySet()) {
            LOGGER.info(
                    "\t├ [当前在线]: {}", s);
        }
        LOGGER.info("\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");
    }

    /**
     * 读完之后调用的方法
     *
     * @param channelHandlerContext ChannelHandlerContext
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {
        channelHandlerContext.flush();
    }

    /**
     * 接收客户端发送的消息
     *
     * @param ctx ChannelHandlerContext
     * @param msg 消息
     */
    @Override
    protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [收到客户端消息类型]: {} - {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg.getClass(), msg.toString());
        // 传统http接入 第一次需要使用http建立握手
        if (msg instanceof FullHttpRequest) {
            handlerHttpRequest(ctx, (FullHttpRequest) msg);
            ctx.channel().write(new TextWebSocketFrame("连接成功"));
        }
        // WebSocket接入
        else if (msg instanceof WebSocketFrame) {
            handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
        }

    }

    /**
     * 第一次握手
     *
     * @param channelHandlerContext channelHandlerContext
     * @param req                   请求
     */
    private void handlerHttpRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest req) {

        // 构造握手响应返回,本机测试
        WebSocketServerHandshakerFactory wsFactory
                = new WebSocketServerHandshakerFactory("ws://localhost:9999/websocket/{uri}",
                null, false);
        String uri = req.uri();
        String[] split = uri.split("/");
        String userName = split[2];
        // 加入在线用户
        WebSocketUsers.put(userName, channelHandlerContext.channel());
        socketServerHandShaker = wsFactory.newHandshaker(req);
        if (socketServerHandShaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(channelHandlerContext.channel());
        } else {
            socketServerHandShaker.handshake(channelHandlerContext.channel(), req);
        }
    }

    /**
     * webSocket处理逻辑
     *
     * @param channelHandlerContext channelHandlerContext
     * @param frame                 webSocketFrame
     */
    private void handlerWebSocketFrame(ChannelHandlerContext channelHandlerContext, WebSocketFrame frame) throws IOException {
        Channel channel = channelHandlerContext.channel();
        // region 判断是否是关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [关闭与客户端的链接]\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");
            socketServerHandShaker.close(channel, (CloseWebSocketFrame) frame.retain());
            return;
        }
        // endregion
        // region 判断是否是ping消息
        if (frame instanceof PingWebSocketFrame) {
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [Ping消息]\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓");
            channel.write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // endregion
        if (frame instanceof TextWebSocketFrame) {
            String text = ((TextWebSocketFrame) frame).text();
            WebSocketMessage webSocketMessage = JSONObject.parseObject(text, WebSocketMessage.class);
            String accept = webSocketMessage.getAccept();
            WebSocketUsers.sendMessageToUser(accept, webSocketMessage.getContent());
        }
        if (frame instanceof BinaryWebSocketFrame) {
            BinaryWebSocketFrame binaryWebSocketFrame = (BinaryWebSocketFrame) frame;
            ByteBuf content = binaryWebSocketFrame.content();
            LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                    "\t├ [二进制数据]:{}\n" +
                    "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", content);
            final int length = content.readableBytes();
            final byte[] array = new byte[length];
            content.getBytes(content.readerIndex(), array, 0, length);
            MessagePack messagePack = new MessagePack();
            List<Object> list = new ArrayList<>();
            list.add(messagePack.read(array));
            for (Object o : list) {
                LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                        "\t├ [解码数据]: {}\n" +
                        "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", o);
            }

        }
        // 非文本消息处理方式
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(String.format(
                    "%s 暂不支持非文本消息", frame.getClass().getName()
            ));
        }
    }
}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java
================================================
package org.yyx.netty.study.websocket;

import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.internal.PlatformDependent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

/**
 * WebSocket用户集
 */
public class WebSocketUsers {

    /**
     * 用户集
     */
    private static final ConcurrentMap<String, Channel> USERS = PlatformDependent.newConcurrentHashMap();
    /**
     * WebSocketUsers 日志控制器
     * Create by 叶云轩 at 2018/5/15 下午5:41
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);

    private static WebSocketUsers ourInstance = new WebSocketUsers();

    private WebSocketUsers() {
    }

    public static WebSocketUsers getInstance() {
        return ourInstance;
    }

    /**
     * 存储通道
     *
     * @param key     唯一键
     * @param channel 通道
     */
    public static void put(String key, Channel channel) {
        USERS.put(key, channel);
    }

    /**
     * 移除通道
     *
     * @param channel 通道
     *
     * @return 移除结果
     */
    public static boolean remove(Channel channel) {
        String key = null;
        boolean b = USERS.containsValue(channel);
        if (b) {
            Set<Map.Entry<String, Channel>> entries = USERS.entrySet();
            for (Map.Entry<String, Channel> entry : entries) {
                Channel value = entry.getValue();
                if (value.equals(channel)) {
                    key = entry.getKey();
                    break;
                }
            }
        } else {
            return true;
        }
        return remove(key);
    }

    /**
     * 移出通道
     *
     * @param key 键
     */
    public static boolean remove(String key) {
        Channel remove = USERS.remove(key);
        boolean containsValue = USERS.containsValue(remove);
        LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                "\t├ [移出结果]: {}\n" +
                "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", containsValue ? "失败" : "成功");
        return containsValue;
    }

    /**
     * 获取在线用户列表
     *
     * @return 返回用户集合
     */
    public static ConcurrentMap<String, Channel> getUSERS() {
        return USERS;
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    public static void sendMessageToUsers(String message) {
        Collection<Channel> values = USERS.values();
        for (Channel value : values) {
            value.write(new TextWebSocketFrame(message));
            value.flush();
        }
    }

    /**
     * 给某个人发送消息
     *
     * @param userName key
     * @param message  消息
     */
    public static void sendMessageToUser(String userName, String message) {
        Channel channel = USERS.get(userName);
        channel.write(new TextWebSocketFrame(message));
        channel.flush();
    }
}

================================================
FILE: spring-boot-netty/study-netty/study-server/src/main/resources/application.properties
================================================


================================================
FILE: spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/CodeCTest.java
================================================
package org.yyx.netty.study;

import org.junit.Test;
import org.msgpack.MessagePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyx.netty.entity.User;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;

/**
 * <p>
 * create by 叶云轩 at 2018/4/12-下午6:50
 * contact by tdg_yyx@foxmail.com
 *
 * @author 叶云轩 contact by tdg_yyx@foxmail.com
 * @date 2018/8/15 - 12:34
 */
public class CodeCTest {
    /**
     * CodeCTest 日志控制器
     * Create by 叶云轩 at 2018/4/12 下午6:53
     * Concat at tdg_yyx@foxmail.com
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CodeCTest.class);

    // region jdk序列化与基于ByteBuffer的二进制序列化字节数对比
    @Test
    public void testCodeC() throws IOException {
        User user = new User();
        user.setName("yyx");
        user.setId(1);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(user);
        objectOutputStream.flush();
        objectOutputStream.close();
        byte[] bytes = byteArrayOutputStream.toByteArray();
        LOGGER.info("--- [JDK] {}", bytes.length);
        byteArrayOutputStream.close();
        LOGGER.info("--- [byte] {}", user.codeC().length);

        MessagePack messagePack = new MessagePack();
        // 序列化
        byte[] write = messagePack.write(user);
        LOGGER.info("--- [MessagePack] {}", write.length);
    }
    // endregion

    // region jdk序列化效率与基于ByteBuffer的二进制序列化效率对比
    @Test
    public void testCodec() throws Exception {
        User user = new User();
        user.setName("yyx");
        user.setId(1);
        int loop = 1000000;
        ByteArrayOutputStream bos;
        ObjectOutputStream os;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            bos = new ByteArrayOutputStream();
            os = new ObjectOutputStream(bos);
            os.writeObject(user);
            os.flush();
            os.close();
            byte[] bytes = bos.toByteArray();
            bos.close();
        }
        long endTime = System.currentTimeMillis();
        LOGGER.info("--- [jdk耗时] {}", endTime - startTime + "ms");
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            byte[] bytes = user.codeC(buffer);
        }
        endTime = System.currentTimeMillis();
        LOGGER.info("--- [byte耗时] {}", endTime - startTime + "ms");
        startTime = System.currentTimeMillis();
        MessagePack messagePack = new MessagePack();
        for (int i = 0; i < loop; i++) {
            // 序列化
            byte[] write = messagePack.write(user);
        }
        endTime = System.currentTimeMillis();
        LOGGER.info("--- [MessagePack 耗时] {}", endTime - startTime + "ms");


    }
    // endregion

}


================================================
FILE: spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/StudyServerApplicationTests.java
================================================
package org.yyx.netty.study;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.yyx.netty.study.echo.megpack.MessagePackServer;
import org.yyx.netty.study.websocket.WebSocketServer;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudyServerApplicationTests {

    private final int port = 8080;

    // region 启动不考虑粘包/拆包问题的netty服务
    @Test
    public void startNettyServer1() throws Exception {
        new org.yyx.netty.study.time.demo1.TimeServer().bind(port);
    }
    // endregion

    // region 启动模拟粘包/拆包问题的netty服务
    @Test
    public void startNettyServer2() throws Exception {
        new org.yyx.netty.study.time.demo2.TimeServer().bind(port);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty服务 - LineBasedFrameDecoder 实现
    @Test
    public void startNettyServer3() throws Exception {
        new org.yyx.netty.study.time.demo3.TimeServer().bind(port);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty服务 - DelimiterBasedFrameDecoder 实现
    @Test
    public void startNettyServer4() throws Exception {
        new org.yyx.netty.study.echo.delimiter.EchoServer().bind(port);
    }
    // endregion

    // region 启动已解决粘包/拆包问题的netty服务 - FixedLengthFrameDecoder 实现
    @Test
    public void startNettyServer5() throws Exception {
        new org.yyx.netty.study.echo.fixlength.EchoServer().bind(port);
    }
    // endregion

    // region 启动不考虑粘包/拆包问题 基于MessagePack编解码的Netty服务
    @Test
    public void testMessagePackEchoServer() throws Exception {
        new MessagePackServer().bind(port);
    }
    // endregion

    // region 启动WebSocket服务器
    @Test
    public void startWebSocketServer() throws Exception {
        new WebSocketServer().run(9999);
    }
    // endregion
}
Download .txt
gitextract_3mkei390/

├── .gitignore
├── README.md
└── spring-boot-netty/
    ├── netty-commons/
    │   ├── pom.xml
    │   └── src/
    │       └── main/
    │           └── java/
    │               └── org/
    │                   └── yyx/
    │                       └── netty/
    │                           ├── entity/
    │                           │   ├── MethodInvokeMeta.java
    │                           │   ├── NullWritable.java
    │                           │   └── User.java
    │                           ├── exception/
    │                           │   ├── ErrorParamsException.java
    │                           │   └── NoUseableChannel.java
    │                           └── util/
    │                               ├── ObjectCodec.java
    │                               └── ObjectSerializerUtils.java
    ├── netty-demo/
    │   ├── netty-client/
    │   │   ├── README.md
    │   │   ├── pom.xml
    │   │   └── src/
    │   │       ├── main/
    │   │       │   ├── java/
    │   │       │   │   └── org/
    │   │       │   │       └── yyx/
    │   │       │   │           └── netty/
    │   │       │   │               ├── NettyClientApplication.java
    │   │       │   │               ├── action/
    │   │       │   │               │   └── MainAction.java
    │   │       │   │               ├── client/
    │   │       │   │               │   ├── NettyClient.java
    │   │       │   │               │   ├── NettyClientHandler.java
    │   │       │   │               │   ├── NettyClientHandlerAdapter.java
    │   │       │   │               │   ├── NettyClientListener.java
    │   │       │   │               │   └── RPCProxyFactoryBean.java
    │   │       │   │               ├── config/
    │   │       │   │               │   ├── NettyConfig.java
    │   │       │   │               │   └── NettyConfiguration.java
    │   │       │   │               └── rpc/
    │   │       │   │                   ├── service/
    │   │       │   │                   │   └── DemoService.java
    │   │       │   │                   └── util/
    │   │       │   │                       ├── ChannelUtil.java
    │   │       │   │                       ├── NettyBeanScanner.java
    │   │       │   │                       ├── PackageClassUtils.java
    │   │       │   │                       ├── RemoteMethodInvokeUtil.java
    │   │       │   │                       └── WrapMethodUtils.java
    │   │       │   └── resources/
    │   │       │       └── application.yml
    │   │       └── test/
    │   │           └── java/
    │   │               └── org/
    │   │                   └── yyx/
    │   │                       └── netty/
    │   │                           └── client/
    │   │                               └── NettyClientApplicationTests.java
    │   ├── netty-server/
    │   │   ├── README.md
    │   │   ├── pom.xml
    │   │   └── src/
    │   │       ├── main/
    │   │       │   ├── java/
    │   │       │   │   └── org/
    │   │       │   │       └── yyx/
    │   │       │   │           └── netty/
    │   │       │   │               ├── NettyServerApplication.java
    │   │       │   │               ├── rpc/
    │   │       │   │               │   └── service/
    │   │       │   │               │       └── DemoService.java
    │   │       │   │               └── server/
    │   │       │   │                   ├── adapter/
    │   │       │   │                   │   └── ServerChannelHandlerAdapter.java
    │   │       │   │                   ├── config/
    │   │       │   │                   │   └── NettyServerConfig.java
    │   │       │   │                   ├── dispatcher/
    │   │       │   │                   │   └── RequestDispatcher.java
    │   │       │   │                   ├── impl/
    │   │       │   │                   │   └── DemoServiceImpl.java
    │   │       │   │                   └── listener/
    │   │       │   │                       └── NettyServerListener.java
    │   │       │   └── resources/
    │   │       │       └── application.yml
    │   │       └── test/
    │   │           └── java/
    │   │               └── org/
    │   │                   └── yyx/
    │   │                       └── netty/
    │   │                           └── NettyServerApplicationTests.java
    │   └── pom.xml
    ├── pom.xml
    └── study-netty/
        ├── pom.xml
        ├── study-client/
        │   ├── pom.xml
        │   └── src/
        │       ├── main/
        │       │   ├── java/
        │       │   │   └── org/
        │       │   │       └── yyx/
        │       │   │           └── netty/
        │       │   │               └── study/
        │       │   │                   ├── StudyClientApplication.java
        │       │   │                   ├── codec/
        │       │   │                   │   └── msgpack/
        │       │   │                   │       ├── MsgPackDecoder.java
        │       │   │                   │       └── MsgPackEncoder.java
        │       │   │                   ├── echo/
        │       │   │                   │   ├── delimiter/
        │       │   │                   │   │   ├── EchoClient.java
        │       │   │                   │   │   └── EchoClientHandler.java
        │       │   │                   │   ├── fixlength/
        │       │   │                   │   │   ├── EchoClient.java
        │       │   │                   │   │   └── EchoClientHandler.java
        │       │   │                   │   └── megpack/
        │       │   │                   │       ├── MessagePackClient.java
        │       │   │                   │       └── MessagePackClientHandler.java
        │       │   │                   ├── time/
        │       │   │                   │   ├── demo1/
        │       │   │                   │   │   ├── TimeClient.java
        │       │   │                   │   │   └── TimeClientHandler.java
        │       │   │                   │   ├── demo2/
        │       │   │                   │   │   ├── TimeClient.java
        │       │   │                   │   │   └── TimeClientHandler.java
        │       │   │                   │   └── demo3/
        │       │   │                   │       ├── TimeClient.java
        │       │   │                   │       └── TimeClientHandler.java
        │       │   │                   └── websocket/
        │       │   │                       ├── WebSocketClient.java
        │       │   │                       ├── WebSocketClientHandler.java
        │       │   │                       ├── WebSocketConstant.java
        │       │   │                       ├── WebSocketHandlerClient.java
        │       │   │                       ├── WebSocketMessage.java
        │       │   │                       └── WebSocketUsers.java
        │       │   └── resources/
        │       │       └── application.properties
        │       └── test/
        │           └── java/
        │               └── org/
        │                   └── yyx/
        │                       └── netty/
        │                           └── study/
        │                               └── StudyClientApplicationTests.java
        └── study-server/
            ├── pom.xml
            └── src/
                ├── main/
                │   ├── java/
                │   │   └── org/
                │   │       └── yyx/
                │   │           └── netty/
                │   │               └── study/
                │   │                   ├── StudyServerApplication.java
                │   │                   ├── codec/
                │   │                   │   └── msgpack/
                │   │                   │       ├── MsgPackDecoder.java
                │   │                   │       └── MsgPackEncoder.java
                │   │                   ├── echo/
                │   │                   │   ├── delimiter/
                │   │                   │   │   ├── EchoServer.java
                │   │                   │   │   └── EchoServerHandler.java
                │   │                   │   ├── fixlength/
                │   │                   │   │   ├── EchoServer.java
                │   │                   │   │   └── EchoServerHandler.java
                │   │                   │   └── megpack/
                │   │                   │       ├── MessagePackServer.java
                │   │                   │       └── MessagePackServerHandler.java
                │   │                   ├── time/
                │   │                   │   ├── demo1/
                │   │                   │   │   ├── ChildChannelHandler.java
                │   │                   │   │   ├── TimeServer.java
                │   │                   │   │   └── TimeServerHandler.java
                │   │                   │   ├── demo2/
                │   │                   │   │   ├── ChildChannelHandler.java
                │   │                   │   │   ├── TimeServer.java
                │   │                   │   │   └── TimeServerHandler.java
                │   │                   │   └── demo3/
                │   │                   │       ├── ChildChannelHandler.java
                │   │                   │       ├── TimeServer.java
                │   │                   │       └── TimeServerHandler.java
                │   │                   └── websocket/
                │   │                       ├── WebSocketChildHandler.java
                │   │                       ├── WebSocketConstant.java
                │   │                       ├── WebSocketMessage.java
                │   │                       ├── WebSocketServer.java
                │   │                       ├── WebSocketServerHandler.java
                │   │                       └── WebSocketUsers.java
                │   └── resources/
                │       └── application.properties
                └── test/
                    └── java/
                        └── org/
                            └── yyx/
                                └── netty/
                                    └── study/
                                        ├── CodeCTest.java
                                        └── StudyServerApplicationTests.java
Download .txt
SYMBOL INDEX (282 symbols across 79 files)

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/MethodInvokeMeta.java
  class MethodInvokeMeta (line 16) | @Data
    method getArgs (line 43) | public Object[] getArgs() {
    method setArgs (line 47) | public void setArgs(Object[] args) {
    method getInterfaceClass (line 51) | public Class<?> getInterfaceClass() {
    method setInterfaceClass (line 55) | public void setInterfaceClass(Class<?> interfaceClass) {
    method getMethodName (line 59) | public String getMethodName() {
    method setMethodName (line 63) | public void setMethodName(String methodName) {
    method getParameterTypes (line 67) | public Class[] getParameterTypes() {
    method setParameterTypes (line 71) | public void setParameterTypes(Class<?>[] parameterTypes) {
    method getReturnType (line 75) | public Class getReturnType() {
    method setReturnType (line 79) | public void setReturnType(Class returnType) {

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/NullWritable.java
  class NullWritable (line 14) | @Data
    method NullWritable (line 28) | private NullWritable() {
    method nullWritable (line 36) | public static NullWritable nullWritable() {

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/User.java
  class User (line 19) | @Data
    method codeC (line 44) | public byte[] codeC() {
    method codeC (line 57) | public byte[] codeC(ByteBuffer buffer) {

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/ErrorParamsException.java
  class ErrorParamsException (line 10) | public class ErrorParamsException extends RuntimeException {
    method ErrorParamsException (line 13) | public ErrorParamsException() {
    method ErrorParamsException (line 17) | public ErrorParamsException(String message) {

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/NoUseableChannel.java
  class NoUseableChannel (line 10) | public class NoUseableChannel extends RuntimeException{
    method NoUseableChannel (line 13) | public NoUseableChannel() {
    method NoUseableChannel (line 17) | public NoUseableChannel(String message) {

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectCodec.java
  class ObjectCodec (line 15) | public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {
    method encode (line 17) | @Override
    method decode (line 25) | @Override

FILE: spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectSerializerUtils.java
  class ObjectSerializerUtils (line 14) | public class ObjectSerializerUtils {
    method deSerilizer (line 28) | public static Object deSerilizer(byte[] data) {
    method serilizer (line 51) | public static byte[] serilizer(Object obj) {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/NettyClientApplication.java
  class NettyClientApplication (line 10) | @SpringBootApplication
    method main (line 13) | public static void main(String[] args) {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/action/MainAction.java
  class MainAction (line 18) | @Component
    method call (line 36) | public void call() throws InterruptedException {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClient.java
  class NettyClient (line 18) | public class NettyClient {
    method NettyClient (line 47) | public NettyClient(int port, String url) {
    method start (line 56) | public void start() {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandler.java
  class NettyClientHandler (line 19) | public class NettyClientHandler extends ChannelInitializer<SocketChannel> {
    method initChannel (line 21) | @Override

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandlerAdapter.java
  class NettyClientHandlerAdapter (line 18) | public class NettyClientHandlerAdapter extends ChannelHandlerAdapter {
    method exceptionCaught (line 25) | @Override
    method channelActive (line 31) | @Override
    method channelRead (line 38) | @Override
    method channelReadComplete (line 48) | @Override
    method userEventTriggered (line 56) | @Override
    method close (line 66) | @Override

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientListener.java
  class NettyClientListener (line 21) | @Order(0)
    method run (line 40) | @Override

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/RPCProxyFactoryBean.java
  class RPCProxyFactoryBean (line 23) | public class RPCProxyFactoryBean extends AbstractFactoryBean<Object> imp...
    method getObjectType (line 33) | @Override
    method createInstance (line 43) | @Override
    method invoke (line 59) | @Override
    method setInterfaceClass (line 83) | public void setInterfaceClass(Class interfaceClass) {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfig.java
  class NettyConfig (line 14) | @Component

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfiguration.java
  class NettyConfiguration (line 15) | @Configuration
    method initNettyBeanScanner (line 25) | @Bean

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/service/DemoService.java
  type DemoService (line 15) | public interface DemoService {
    method division (line 24) | double division(int numberA, int numberB) throws ErrorParamsException;
    method getUserInfo (line 31) | User getUserInfo();
    method print (line 38) | String print();
    method sum (line 47) | int sum(int numberA, int numberB);

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/ChannelUtil.java
  class ChannelUtil (line 24) | public class ChannelUtil {
    method ChannelUtil (line 38) | private ChannelUtil() {
    method calculateResult (line 47) | public static void calculateResult(String key, Object result) {
    method getResultKey (line 57) | public static String getResultKey(String key) {
    method getResult (line 67) | public static Object getResult(String key) {
    method registerChannel (line 76) | public static void registerChannel(Channel channel) {
    method remoteCall (line 88) | public static void remoteCall(MethodInvokeMeta methodInvokeMeta, Strin...
    method remove (line 106) | public static void remove(Channel channel) {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/NettyBeanScanner.java
  class NettyBeanScanner (line 20) | public class NettyBeanScanner implements BeanFactoryPostProcessor {
    method NettyBeanScanner (line 41) | public NettyBeanScanner(String basePackage, String clientName) {
    method postProcessBeanFactory (line 53) | @Override

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/PackageClassUtils.java
  class PackageClassUtils (line 16) | public class PackageClassUtils {
    method getAllFile (line 26) | private static void getAllFile(String s, File file, List<String> class...
    method getClassReferenceList (line 48) | private static List<String> getClassReferenceList(List<String> classSt...
    method resolver (line 69) | public static List<String> resolver(String basePackage) {

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/RemoteMethodInvokeUtil.java
  class RemoteMethodInvokeUtil (line 17) | public class RemoteMethodInvokeUtil implements ApplicationContextAware {
    method processMethod (line 21) | public Object processMethod(MethodInvokeMeta methodInvokeMeta) throws ...
    method setApplicationContext (line 35) | @Override

FILE: spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/WrapMethodUtils.java
  class WrapMethodUtils (line 15) | public class WrapMethodUtils {
    method readMethod (line 24) | public static MethodInvokeMeta readMethod(Class interfaceClass, Method...

FILE: spring-boot-netty/netty-demo/netty-client/src/test/java/org/yyx/netty/client/NettyClientApplicationTests.java
  class NettyClientApplicationTests (line 8) | @RunWith(SpringRunner.class)
    method contextLoads (line 12) | @Test

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/NettyServerApplication.java
  class NettyServerApplication (line 16) | @SpringBootApplication
    method main (line 22) | public static void main(String[] args) {
    method run (line 26) | @Override

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/rpc/service/DemoService.java
  type DemoService (line 13) | public interface DemoService {
    method division (line 22) | double division(int numberA, int numberB) throws ErrorParamsException;
    method getUserInfo (line 29) | User getUserInfo();
    method print (line 36) | String print();
    method sum (line 45) | int sum(int numberA, int numberB);

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/adapter/ServerChannelHandlerAdapter.java
  class ServerChannelHandlerAdapter (line 23) | @Component
    method exceptionCaught (line 39) | @Override
    method channelRead (line 52) | @Override
    method userEventTriggered (line 78) | @Override

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/config/NettyServerConfig.java
  class NettyServerConfig (line 13) | @Component
    method getPort (line 30) | public int getPort() {
    method setPort (line 34) | public void setPort(int port) {
    method getMaxThreads (line 38) | public int getMaxThreads() {
    method setMaxThreads (line 42) | public void setMaxThreads(int maxThreads) {
    method getMaxFrameLength (line 46) | public int getMaxFrameLength() {
    method setMaxFrameLength (line 50) | public void setMaxFrameLength(int maxFrameLength) {

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/dispatcher/RequestDispatcher.java
  class RequestDispatcher (line 26) | @Component
    method dispatcher (line 44) | public void dispatcher(final ChannelHandlerContext channelHandlerConte...
    method setApplicationContext (line 109) | @Override

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/impl/DemoServiceImpl.java
  class DemoServiceImpl (line 15) | @Service
    method division (line 18) | @Override
    method getUserInfo (line 26) | @Override
    method print (line 40) | @Override
    method sum (line 45) | @Override

FILE: spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/listener/NettyServerListener.java
  class NettyServerListener (line 37) | @Component
    method close (line 72) | @PreDestroy
    method start (line 83) | public void start() {

FILE: spring-boot-netty/netty-demo/netty-server/src/test/java/org/yyx/netty/NettyServerApplicationTests.java
  class NettyServerApplicationTests (line 8) | @RunWith(SpringRunner.class)
    method contextLoads (line 12) | @Test

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/StudyClientApplication.java
  class StudyClientApplication (line 7) | @SpringBootApplication
    method main (line 10) | public static void main(String[] args) {
    method run (line 14) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java
  class MsgPackDecoder (line 18) | public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {
    method decode (line 26) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java
  class MsgPackEncoder (line 18) | public class MsgPackEncoder extends MessageToByteEncoder<Object> {
    method encode (line 27) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClient.java
  class EchoClient (line 23) | public class EchoClient {
    method connect (line 25) | public void connect(int port, String host) throws Exception {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClientHandler.java
  class EchoClientHandler (line 14) | public class EchoClientHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 27) | @Override
    method channelActive (line 33) | @Override
    method channelRead (line 40) | @Override
    method channelReadComplete (line 45) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClient.java
  class EchoClient (line 22) | public class EchoClient {
    method connect (line 24) | public void connect(int port, String host) throws Exception {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClientHandler.java
  class EchoClientHandler (line 14) | public class EchoClientHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 27) | @Override
    method channelActive (line 33) | @Override
    method channelRead (line 40) | @Override
    method channelReadComplete (line 45) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClient.java
  class MessagePackClient (line 21) | public class MessagePackClient {
    method connect (line 31) | public void connect(int port, String host, int sendNumber) throws Exce...

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClientHandler.java
  class MessagePackClientHandler (line 14) | public class MessagePackClientHandler extends ChannelHandlerAdapter {
    method MessagePackClientHandler (line 25) | public MessagePackClientHandler(int sendNumber) {
    method exceptionCaught (line 29) | @Override
    method channelActive (line 35) | @Override
    method channelRead (line 45) | @Override
    method channelReadComplete (line 50) | @Override
    method UserInfo (line 55) | private User[] UserInfo() {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClient.java
  class TimeClient (line 18) | public class TimeClient {
    method connect (line 20) | public void connect(int port, String host) throws Exception {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClientHandler.java
  class TimeClientHandler (line 15) | public class TimeClientHandler extends ChannelHandlerAdapter {
    method TimeClientHandler (line 32) | public TimeClientHandler() {
    method exceptionCaught (line 46) | @Override
    method channelActive (line 60) | @Override
    method channelRead (line 73) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClient.java
  class TimeClient (line 18) | public class TimeClient {
    method connect (line 20) | public void connect(int port, String host) throws Exception {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClientHandler.java
  class TimeClientHandler (line 15) | public class TimeClientHandler extends ChannelHandlerAdapter {
    method TimeClientHandler (line 33) | public TimeClientHandler() {
    method exceptionCaught (line 46) | @Override
    method channelActive (line 59) | @Override
    method channelRead (line 78) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClient.java
  class TimeClient (line 20) | public class TimeClient {
    method connect (line 22) | public void connect(int port, String host) throws Exception {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClientHandler.java
  class TimeClientHandler (line 15) | public class TimeClientHandler extends ChannelHandlerAdapter {
    method TimeClientHandler (line 33) | public TimeClientHandler() {
    method exceptionCaught (line 46) | @Override
    method channelActive (line 59) | @Override
    method channelRead (line 78) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClient.java
  class WebSocketClient (line 20) | public class WebSocketClient {
    method connect (line 22) | public void connect(int port, String host, String userName) throws Exc...

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClientHandler.java
  class WebSocketClientHandler (line 22) | public class WebSocketClientHandler extends SimpleChannelInboundHandler<...
    method WebSocketClientHandler (line 33) | WebSocketClientHandler(WebSocketClientHandshaker webSocketClientHandsh...
    method handlerAdded (line 37) | @Override
    method exceptionCaught (line 48) | @Override
    method channelActive (line 61) | @Override
    method channelInactive (line 77) | @Override
    method channelReadComplete (line 92) | @Override
    method messageReceived (line 97) | @Override
    method getUserNameInPath (line 142) | private String getUserNameInPath() {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java
  type WebSocketConstant (line 9) | public interface WebSocketConstant {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketHandlerClient.java
  class WebSocketHandlerClient (line 18) | public class WebSocketHandlerClient extends ChannelInitializer<SocketCha...
    method WebSocketHandlerClient (line 22) | public WebSocketHandlerClient(WebSocketClientHandler webSocketClientHa...
    method initChannel (line 31) | @Override

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java
  class WebSocketMessage (line 13) | @Data
    type Type (line 21) | enum Type {

FILE: spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java
  class WebSocketUsers (line 17) | public class WebSocketUsers {
    method WebSocketUsers (line 32) | private WebSocketUsers() {
    method getInstance (line 35) | public static WebSocketUsers getInstance() {
    method put (line 45) | public static void put(String key, Channel channel) {
    method remove (line 56) | public static boolean remove(Channel channel) {
    method remove (line 79) | public static boolean remove(String key) {
    method getUSERS (line 93) | public static ConcurrentMap<String, Channel> getUSERS() {
    method sendMessageToUsers (line 102) | public static void sendMessageToUsers(String message) {
    method sendMessageToUser (line 116) | public static void sendMessageToUser(String userName, String message) {

FILE: spring-boot-netty/study-netty/study-client/src/test/java/org/yyx/netty/study/StudyClientApplicationTests.java
  class StudyClientApplicationTests (line 11) | @RunWith(SpringRunner.class)
    method startNettyClient1 (line 25) | @Test
    method startNettyClient2 (line 32) | @Test
    method startNettyClient3 (line 39) | @Test
    method startNettyClient4 (line 46) | @Test
    method startNettyServer5 (line 53) | @Test
    method testMessagePackEchoClient (line 60) | @Test
    method startWebSocketClient (line 67) | @Test
    method startWebSocketClient2 (line 72) | @Test

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/StudyServerApplication.java
  class StudyServerApplication (line 7) | @SpringBootApplication
    method main (line 10) | public static void main(String[] args) {
    method run (line 14) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java
  class MsgPackDecoder (line 18) | public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {
    method decode (line 31) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java
  class MsgPackEncoder (line 16) | public class MsgPackEncoder extends MessageToByteEncoder<Object> {
    method encode (line 27) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServer.java
  class EchoServer (line 26) | public class EchoServer {
    method bind (line 35) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServerHandler.java
  class EchoServerHandler (line 15) | public class EchoServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 28) | @Override
    method channelRead (line 34) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServer.java
  class EchoServer (line 24) | public class EchoServer {
    method bind (line 33) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServerHandler.java
  class EchoServerHandler (line 13) | public class EchoServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 22) | @Override
    method channelRead (line 28) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServer.java
  class MessagePackServer (line 24) | public class MessagePackServer {
    method bind (line 33) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServerHandler.java
  class MessagePackServerHandler (line 19) | public class MessagePackServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 28) | @Override
    method channelActive (line 35) | @Override
    method channelRead (line 40) | @Override
    method channelReadComplete (line 48) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/ChildChannelHandler.java
  class ChildChannelHandler (line 14) | public class ChildChannelHandler extends ChannelInitializer<SocketChanne...
    method initChannel (line 32) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServer.java
  class TimeServer (line 17) | public class TimeServer {
    method bind (line 37) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServerHandler.java
  class TimeServerHandler (line 18) | public class TimeServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 30) | @Override
    method channelRead (line 43) | @Override
    method channelReadComplete (line 76) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/ChildChannelHandler.java
  class ChildChannelHandler (line 17) | public class ChildChannelHandler extends ChannelInitializer<SocketChanne...
    method initChannel (line 34) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServer.java
  class TimeServer (line 17) | public class TimeServer {
    method bind (line 37) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServerHandler.java
  class TimeServerHandler (line 18) | public class TimeServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 30) | @Override
    method channelRead (line 42) | @Override
    method channelReadComplete (line 66) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/ChildChannelHandler.java
  class ChildChannelHandler (line 16) | public class ChildChannelHandler extends ChannelInitializer<SocketChanne...
    method initChannel (line 40) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServer.java
  class TimeServer (line 17) | public class TimeServer {
    method bind (line 38) | public void bind(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServerHandler.java
  class TimeServerHandler (line 18) | public class TimeServerHandler extends ChannelHandlerAdapter {
    method exceptionCaught (line 30) | @Override
    method channelRead (line 43) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketChildHandler.java
  class WebSocketChildHandler (line 16) | public class WebSocketChildHandler extends ChannelInitializer<SocketChan...
    method initChannel (line 22) | @Override

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java
  type WebSocketConstant (line 9) | public interface WebSocketConstant {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java
  class WebSocketMessage (line 13) | @Data
    type Type (line 21) | enum Type {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServer.java
  class WebSocketServer (line 18) | public class WebSocketServer {
    method run (line 26) | public void run(int port) throws Exception {

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServerHandler.java
  class WebSocketServerHandler (line 31) | public class WebSocketServerHandler extends SimpleChannelInboundHandler<...
    method exceptionCaught (line 47) | @Override
    method channelActive (line 57) | @Override
    method channelInactive (line 72) | @Override
    method channelReadComplete (line 93) | @Override
    method messageReceived (line 104) | @Override
    method handlerHttpRequest (line 127) | private void handlerHttpRequest(ChannelHandlerContext channelHandlerCo...
    method handlerWebSocketFrame (line 152) | private void handlerWebSocketFrame(ChannelHandlerContext channelHandle...

FILE: spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java
  class WebSocketUsers (line 17) | public class WebSocketUsers {
    method WebSocketUsers (line 32) | private WebSocketUsers() {
    method getInstance (line 35) | public static WebSocketUsers getInstance() {
    method put (line 45) | public static void put(String key, Channel channel) {
    method remove (line 56) | public static boolean remove(Channel channel) {
    method remove (line 79) | public static boolean remove(String key) {
    method getUSERS (line 93) | public static ConcurrentMap<String, Channel> getUSERS() {
    method sendMessageToUsers (line 102) | public static void sendMessageToUsers(String message) {
    method sendMessageToUser (line 116) | public static void sendMessageToUser(String userName, String message) {

FILE: spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/CodeCTest.java
  class CodeCTest (line 22) | public class CodeCTest {
    method testCodeC (line 31) | @Test
    method testCodec (line 54) | @Test

FILE: spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/StudyServerApplicationTests.java
  class StudyServerApplicationTests (line 10) | @RunWith(SpringRunner.class)
    method startNettyServer1 (line 17) | @Test
    method startNettyServer2 (line 24) | @Test
    method startNettyServer3 (line 31) | @Test
    method startNettyServer4 (line 38) | @Test
    method startNettyServer5 (line 45) | @Test
    method testMessagePackEchoServer (line 52) | @Test
    method startWebSocketServer (line 59) | @Test
Condensed preview — 95 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (181K chars).
[
  {
    "path": ".gitignore",
    "chars": 305,
    "preview": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Packa"
  },
  {
    "path": "README.md",
    "chars": 688,
    "preview": "# spring boot netty\n\n### 资源库说明\n\n首先,不是一个项目.而是一个DEMO性质的代码分享.\n\n**当前资源库中包含了两大部分**\n\n1. netty-demo下的可以在项目中使用的netty与SpringBoot集"
  },
  {
    "path": "spring-boot-netty/netty-commons/pom.xml",
    "chars": 518,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/MethodInvokeMeta.java",
    "chars": 1609,
    "preview": "package org.yyx.netty.entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimp"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/NullWritable.java",
    "chars": 688,
    "preview": "package org.yyx.netty.entity;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 服务器可能返回空的处理\n * <p>\n *\n * @autho"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/User.java",
    "chars": 1477,
    "preview": "package org.yyx.netty.entity;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.msgpack.annotation.Messag"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/ErrorParamsException.java",
    "chars": 400,
    "preview": "package org.yyx.netty.exception;\n\n/**\n * 参数错误异常\n * <p>\n *\n * @author 叶云轩 at tdg_yyx@foxmail.com\n * @date 2018/11/1-14:41"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/NoUseableChannel.java",
    "chars": 391,
    "preview": "package org.yyx.netty.exception;\n\n/**\n * 没有可用的通道异常\n * <p>\n *\n * @author 叶云轩 at marquis_xuan@163.com\n * @date 2018/11/2-1"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectCodec.java",
    "chars": 931,
    "preview": "package org.yyx.netty.util;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.Ch"
  },
  {
    "path": "spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectSerializerUtils.java",
    "chars": 1720,
    "preview": "package org.yyx.netty.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\n\n/**\n * 对象序列化工具"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/README.md",
    "chars": 714,
    "preview": "# Netty-client\n\n> 该项目源自博主项目中实际应用场景。主要用作RPC通信中的客户端处理。\n\n## 阅读说明\n\n### 阅读顺序:\n\nnetty-client阅读顺序参考以下链条\n\n```java\nNettyConfigura"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/pom.xml",
    "chars": 1331,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://mave"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/NettyClientApplication.java",
    "chars": 407,
    "preview": "package org.yyx.netty;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/action/MainAction.java",
    "chars": 1476,
    "preview": "package org.yyx.netty.action;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stere"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClient.java",
    "chars": 2009,
    "preview": "package org.yyx.netty.client;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.ChannelFuture;\nimport io.net"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandler.java",
    "chars": 1140,
    "preview": "package org.yyx.netty.client;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipeline;\nimpo"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandlerAdapter.java",
    "chars": 2645,
    "preview": "package org.yyx.netty.client;\n\nimport io.netty.channel.ChannelHandlerAdapter;\nimport io.netty.channel.ChannelHandlerCont"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientListener.java",
    "chars": 1546,
    "preview": "package org.yyx.netty.client;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot."
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/RPCProxyFactoryBean.java",
    "chars": 2777,
    "preview": "package org.yyx.netty.client;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfig.java",
    "chars": 409,
    "preview": "package org.yyx.netty.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationPrope"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfiguration.java",
    "chars": 876,
    "preview": "package org.yyx.netty.config;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.con"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/service/DemoService.java",
    "chars": 822,
    "preview": "package org.yyx.netty.rpc.service;\n\nimport org.yyx.netty.entity.User;\nimport org.yyx.netty.exception.ErrorParamsExceptio"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/ChannelUtil.java",
    "chars": 2870,
    "preview": "package org.yyx.netty.rpc.util;\n\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelFuture;\nimport io.netty"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/NettyBeanScanner.java",
    "chars": 2540,
    "preview": "package org.yyx.netty.rpc.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.facto"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/PackageClassUtils.java",
    "chars": 3080,
    "preview": "package org.yyx.netty.rpc.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport j"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/RemoteMethodInvokeUtil.java",
    "chars": 1361,
    "preview": "package org.yyx.netty.rpc.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.App"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/WrapMethodUtils.java",
    "chars": 972,
    "preview": "package org.yyx.netty.rpc.util;\n\n\nimport org.yyx.netty.entity.MethodInvokeMeta;\n\nimport java.lang.reflect.Method;\n\n/**\n "
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/main/resources/application.yml",
    "chars": 105,
    "preview": "netty:\n  port: 12345\n  basePackage: org.yyx.netty.rpc.service;\n  clientName: nettyClient\n  url: 127.0.0.1"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-client/src/test/java/org/yyx/netty/client/NettyClientApplicationTests.java",
    "chars": 342,
    "preview": "package org.yyx.netty.client;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.t"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/README.md",
    "chars": 80,
    "preview": "# netty-server\n>博文源码,基于SOA架构的服务端,使用Netty做RPC通信.\n\n这是一个Netty服务端与SpringBoot集成的Demo."
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/pom.xml",
    "chars": 1387,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://mave"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/NettyServerApplication.java",
    "chars": 762,
    "preview": "package org.yyx.netty;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplica"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/rpc/service/DemoService.java",
    "chars": 747,
    "preview": "package org.yyx.netty.rpc.service;\n\n\nimport org.yyx.netty.entity.User;\nimport org.yyx.netty.exception.ErrorParamsExcepti"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/adapter/ServerChannelHandlerAdapter.java",
    "chars": 3249,
    "preview": "package org.yyx.netty.server.adapter;\n\nimport io.netty.channel.ChannelHandler.Sharable;\nimport io.netty.channel.ChannelH"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/config/NettyServerConfig.java",
    "chars": 997,
    "preview": "package org.yyx.netty.server.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/dispatcher/RequestDispatcher.java",
    "chars": 4269,
    "preview": "package org.yyx.netty.server.dispatcher;\n\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelFutureLi"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/impl/DemoServiceImpl.java",
    "chars": 1160,
    "preview": "package org.yyx.netty.server.impl;\n\nimport org.springframework.stereotype.Service;\nimport org.yyx.netty.entity.User;\nimp"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/listener/NettyServerListener.java",
    "chars": 3704,
    "preview": "package org.yyx.netty.server.listener;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFuture"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/main/resources/application.yml",
    "chars": 106,
    "preview": "# netty配置\nnetty:\n  # 端口号\n  port: 12345\n  # 最大线程数\n  maxThreads: 1024\n  # 数据包的最大长度\n  max_frame_length: 65535"
  },
  {
    "path": "spring-boot-netty/netty-demo/netty-server/src/test/java/org/yyx/netty/NettyServerApplicationTests.java",
    "chars": 335,
    "preview": "package org.yyx.netty;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.con"
  },
  {
    "path": "spring-boot-netty/netty-demo/pom.xml",
    "chars": 1283,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "spring-boot-netty/pom.xml",
    "chars": 3486,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://mave"
  },
  {
    "path": "spring-boot-netty/study-netty/pom.xml",
    "chars": 1214,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://mave"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/pom.xml",
    "chars": 788,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/StudyClientApplication.java",
    "chars": 490,
    "preview": "package org.yyx.netty.study;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringA"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java",
    "chars": 1063,
    "preview": "package org.yyx.netty.study.codec.msgpack;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContex"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java",
    "chars": 1061,
    "preview": "package org.yyx.netty.study.codec.msgpack;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContex"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClient.java",
    "chars": 1883,
    "preview": "package org.yyx.netty.study.echo.delimiter;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.buffer.ByteBuf;\nimport"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClientHandler.java",
    "chars": 1443,
    "preview": "package org.yyx.netty.study.echo.delimiter;\n\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.ChannelHandlerAdap"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClient.java",
    "chars": 1806,
    "preview": "package org.yyx.netty.study.echo.fixlength;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.buffer.ByteBuf;\nimport"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClientHandler.java",
    "chars": 1443,
    "preview": "package org.yyx.netty.study.echo.fixlength;\n\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.ChannelHandlerAdap"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClient.java",
    "chars": 2070,
    "preview": "package org.yyx.netty.study.echo.megpack;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.ChannelFuture;\ni"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClientHandler.java",
    "chars": 1736,
    "preview": "package org.yyx.netty.study.echo.megpack;\n\nimport io.netty.channel.ChannelHandlerAdapter;\nimport io.netty.channel.Channe"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClient.java",
    "chars": 1860,
    "preview": "package org.yyx.netty.study.time.demo1;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.ChannelFuture;\nimp"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClientHandler.java",
    "chars": 1990,
    "preview": "package org.yyx.netty.study.time.demo1;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClient.java",
    "chars": 1860,
    "preview": "package org.yyx.netty.study.time.demo2;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.ChannelFuture;\nimp"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClientHandler.java",
    "chars": 2206,
    "preview": "package org.yyx.netty.study.time.demo2;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClient.java",
    "chars": 2214,
    "preview": "package org.yyx.netty.study.time.demo3;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.ChannelFuture;\nimp"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClientHandler.java",
    "chars": 2106,
    "preview": "package org.yyx.netty.study.time.demo3;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClient.java",
    "chars": 1680,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.Channel;\nimport io."
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClientHandler.java",
    "chars": 5158,
    "preview": "package org.yyx.netty.study.websocket;\n\n\nimport com.alibaba.fastjson.JSONObject;\nimport io.netty.channel.Channel;\nimport"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java",
    "chars": 401,
    "preview": "package org.yyx.netty.study.websocket;\n\n/**\n * WebSocket常量\n * <p>\n * create by 叶云轩 at 2018/5/18-下午1:29\n * contact by tdg"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketHandlerClient.java",
    "chars": 1441,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipel"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java",
    "chars": 487,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * WebSocketMessage\n * <p"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java",
    "chars": 2963,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.channel.Channel;\nimport io.netty.handler.codec.http.websocketx.T"
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/main/resources/application.properties",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "spring-boot-netty/study-netty/study-client/src/test/java/org/yyx/netty/study/StudyClientApplicationTests.java",
    "chars": 2235,
    "preview": "package org.yyx.netty.study;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.te"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/pom.xml",
    "chars": 897,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://mave"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/StudyServerApplication.java",
    "chars": 488,
    "preview": "package org.yyx.netty.study;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringA"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java",
    "chars": 1388,
    "preview": "package org.yyx.netty.study.codec.msgpack;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContex"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java",
    "chars": 1100,
    "preview": "package org.yyx.netty.study.codec.msgpack;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContex"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServer.java",
    "chars": 2719,
    "preview": "package org.yyx.netty.study.echo.delimiter;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.buffer.ByteBuf;\n"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServerHandler.java",
    "chars": 1190,
    "preview": "package org.yyx.netty.study.echo.delimiter;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io."
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServer.java",
    "chars": 2456,
    "preview": "package org.yyx.netty.study.echo.fixlength;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelF"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServerHandler.java",
    "chars": 904,
    "preview": "package org.yyx.netty.study.echo.fixlength;\n\nimport io.netty.channel.ChannelHandlerAdapter;\nimport io.netty.channel.Chan"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServer.java",
    "chars": 2682,
    "preview": "package org.yyx.netty.study.echo.megpack;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFut"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServerHandler.java",
    "chars": 1485,
    "preview": "package org.yyx.netty.study.echo.megpack;\n\nimport io.netty.channel.ChannelHandlerAdapter;\nimport io.netty.channel.Channe"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/ChildChannelHandler.java",
    "chars": 943,
    "preview": "package org.yyx.netty.study.time.demo1;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.socket.Sock"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServer.java",
    "chars": 2185,
    "preview": "package org.yyx.netty.study.time.demo1;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFutur"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServerHandler.java",
    "chars": 2507,
    "preview": "package org.yyx.netty.study.time.demo1;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/ChildChannelHandler.java",
    "chars": 1012,
    "preview": "package org.yyx.netty.study.time.demo2;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.socket.Sock"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServer.java",
    "chars": 2185,
    "preview": "package org.yyx.netty.study.time.demo2;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFutur"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServerHandler.java",
    "chars": 2172,
    "preview": "package org.yyx.netty.study.time.demo2;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/ChildChannelHandler.java",
    "chars": 1386,
    "preview": "package org.yyx.netty.study.time.demo3;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.socket.Sock"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServer.java",
    "chars": 2192,
    "preview": "package org.yyx.netty.study.time.demo3;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFutur"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServerHandler.java",
    "chars": 1590,
    "preview": "package org.yyx.netty.study.time.demo3;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport io.nett"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketChildHandler.java",
    "chars": 1186,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipel"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java",
    "chars": 401,
    "preview": "package org.yyx.netty.study.websocket;\n\n/**\n * WebSocket常量\n * <p>\n * create by 叶云轩 at 2018/5/18-下午1:29\n * contact by tdg"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java",
    "chars": 487,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * WebSocketMessage\n * <p"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServer.java",
    "chars": 1470,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.Channel;\nimpo"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServerHandler.java",
    "chars": 7642,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport com.alibaba.fastjson.JSONObject;\nimport io.netty.buffer.ByteBuf;\nimport i"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java",
    "chars": 2963,
    "preview": "package org.yyx.netty.study.websocket;\n\nimport io.netty.channel.Channel;\nimport io.netty.handler.codec.http.websocketx.T"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/main/resources/application.properties",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/CodeCTest.java",
    "chars": 3027,
    "preview": "package org.yyx.netty.study;\n\nimport org.junit.Test;\nimport org.msgpack.MessagePack;\nimport org.slf4j.Logger;\nimport org"
  },
  {
    "path": "spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/StudyServerApplicationTests.java",
    "chars": 1857,
    "preview": "package org.yyx.netty.study;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.te"
  }
]

About this extraction

This page contains the full source code of the marquisXuan/netty GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 95 files (146.3 KB), approximately 45.2k tokens, and a symbol index with 282 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!