diff --git a/module-instant-message/pom.xml b/module-instant-message/pom.xml
new file mode 100644
index 00000000..2f48ef3c
--- /dev/null
+++ b/module-instant-message/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ wg-basic
+ ink.wgink
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ module-instant-message
+ WebSocket即时通讯
+
+
+
+ ink.wgink
+ common
+ 1.0-SNAPSHOT
+
+
+
+
+ io.netty
+ netty-all
+
+
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+
+
\ No newline at end of file
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/api/WebSocketClientController.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/api/WebSocketClientController.java
new file mode 100644
index 00000000..ecdcee74
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/api/WebSocketClientController.java
@@ -0,0 +1,37 @@
+package ink.wgink.module.instantmessage.controller.api;
+
+import ink.wgink.interfaces.consts.ISystemConstant;
+import ink.wgink.module.instantmessage.service.IWebSocketClientService;
+import ink.wgink.pojo.result.ErrorResult;
+import ink.wgink.pojo.result.SuccessResultData;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @ClassName: WebSocketClientController
+ * @Description: WebSocket客户端
+ * @Author: wanggeng
+ * @Date: 2021/9/12 4:15 下午
+ * @Version: 1.0
+ */
+@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "websocket客户端")
+@RestController
+@RequestMapping(ISystemConstant.API_PREFIX + "/websocket/client")
+public class WebSocketClientController {
+
+ @Autowired
+ private IWebSocketClientService webSocketClientService;
+
+ @ApiOperation(value = "登录", notes = "登录接口,返回Socket认证token")
+ @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
+ @GetMapping("login/{clientName}")
+ public SuccessResultData login(@PathVariable("clientName") String clientName) {
+ String sessionId = webSocketClientService.login(clientName);
+ return new SuccessResultData<>(sessionId);
+ }
+
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/app/WebSocketClientAppController.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/app/WebSocketClientAppController.java
new file mode 100644
index 00000000..f8fb23ac
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/controller/app/WebSocketClientAppController.java
@@ -0,0 +1,37 @@
+package ink.wgink.module.instantmessage.controller.app;
+
+import ink.wgink.interfaces.consts.ISystemConstant;
+import ink.wgink.module.instantmessage.service.IWebSocketClientService;
+import ink.wgink.pojo.result.ErrorResult;
+import ink.wgink.pojo.result.SuccessResultData;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @ClassName: WebSocketClientAppController
+ * @Description: WebSocket客户端
+ * @Author: wanggeng
+ * @Date: 2021/9/12 4:15 下午
+ * @Version: 1.0
+ */
+@Api(tags = ISystemConstant.API_TAGS_APP_PREFIX + "websocket客户端")
+@RestController
+@RequestMapping(ISystemConstant.APP_PREFIX + "/websocket/client")
+public class WebSocketClientAppController {
+
+ @Autowired
+ private IWebSocketClientService webSocketClientService;
+
+ @ApiOperation(value = "登录", notes = "登录接口,返回userId(sessionId)")
+ @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
+ @GetMapping("login/{clientName}")
+ public SuccessResultData login(@RequestHeader("token") String token, @PathVariable("clientName") String clientName) {
+ String sessionId = webSocketClientService.login(token, clientName);
+ return new SuccessResultData<>(sessionId);
+ }
+
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/IWebSocketClientService.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/IWebSocketClientService.java
new file mode 100644
index 00000000..61173bd7
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/IWebSocketClientService.java
@@ -0,0 +1,28 @@
+package ink.wgink.module.instantmessage.service;
+
+/**
+ * @ClassName: IWebSocketClientService
+ * @Description: websocket客户端
+ * @Author: wanggeng
+ * @Date: 2021/9/12 4:44 下午
+ * @Version: 1.0
+ */
+public interface IWebSocketClientService {
+ /**
+ * 登录
+ *
+ * @param clientName 客户端名称
+ * @return sessionId
+ */
+ String login(String clientName);
+
+ /**
+ * 登录
+ *
+ * @param token
+ * @param clientName 客户端名称
+ * @return sessionId
+ */
+ String login(String token, String clientName);
+
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/impl/WebSocketClientServiceImpl.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/impl/WebSocketClientServiceImpl.java
new file mode 100644
index 00000000..deed8423
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/service/impl/WebSocketClientServiceImpl.java
@@ -0,0 +1,46 @@
+package ink.wgink.module.instantmessage.service.impl;
+
+import ink.wgink.common.base.DefaultBaseService;
+import ink.wgink.module.instantmessage.service.IWebSocketClientService;
+import ink.wgink.module.instantmessage.websocket.manager.WebSocketChannelManager;
+import ink.wgink.pojo.app.AppTokenUser;
+import ink.wgink.util.UUIDUtil;
+import org.springframework.stereotype.Service;
+
+/**
+ * @ClassName: WebSocketClientServiceImpl
+ * @Description: webSocketk客户端
+ * @Author: wanggeng
+ * @Date: 2021/9/12 4:44 下午
+ * @Version: 1.0
+ */
+@Service
+public class WebSocketClientServiceImpl extends DefaultBaseService implements IWebSocketClientService {
+
+ @Override
+ public String login(String clientName) {
+ String userId = securityComponent.getCurrentUser().getUserId();
+ return initSession(userId, clientName);
+ }
+
+ @Override
+ public String login(String token, String clientName) {
+ AppTokenUser appTokenUser = getAppTokenUser(token);
+ return initSession(appTokenUser.getId(), clientName);
+ }
+
+ /**
+ * 初始化Socket会话
+ *
+ * @param userId 用户ID
+ * @param clientName 客户端名称
+ * @return
+ */
+ private String initSession(String userId, String clientName) {
+ String sessionId = UUIDUtil.getUUID();
+ WebSocketChannelManager.getInstance().init(userId, sessionId, clientName);
+ return sessionId;
+ }
+
+
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/startup/WebSocketStartUp.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/startup/WebSocketStartUp.java
new file mode 100644
index 00000000..f850d2e4
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/startup/WebSocketStartUp.java
@@ -0,0 +1,27 @@
+package ink.wgink.module.instantmessage.startup;
+
+import ink.wgink.module.instantmessage.websocket.server.WebSocketServer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName: WebSocketStartUp
+ * @Description:
+ * @Author: wanggeng
+ * @Date: 2021/9/11 10:38 下午
+ * @Version: 1.0
+ */
+@Component
+public class WebSocketStartUp implements ApplicationRunner {
+
+ @Autowired
+ private WebSocketServer webSocketServer;
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ new Thread(webSocketServer).start();
+ }
+
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/channel/WebSocketChannelInitializer.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/channel/WebSocketChannelInitializer.java
new file mode 100644
index 00000000..7260ba11
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/channel/WebSocketChannelInitializer.java
@@ -0,0 +1,43 @@
+package ink.wgink.module.instantmessage.websocket.channel;
+
+import ink.wgink.module.instantmessage.websocket.handler.WebSocketHandler;
+import ink.wgink.properties.websocket.WebSocketProperties;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.handler.stream.ChunkedWriteHandler;
+
+
+/**
+ * @ClassName: WebSocketChannelInitializer
+ * @Description: WebSocket
+ * @Author: wanggeng
+ * @Date: 2021/9/11 11:38 上午
+ * @Version: 1.0
+ */
+public class WebSocketChannelInitializer extends ChannelInitializer {
+
+ private WebSocketProperties webSocketProperties;
+
+ @Override
+ protected void initChannel(SocketChannel socketChannel) throws Exception {
+ //设置log监听器,并且日志级别为debug,方便观察运行流程
+ socketChannel.pipeline().addLast("logging", new LoggingHandler("DEBUG"));
+ //设置解码器
+ socketChannel.pipeline().addLast("http-codec", new HttpServerCodec());
+ //聚合器,使用websocket会用到
+ socketChannel.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
+ //用于大数据的分区传输
+ socketChannel.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
+ //自定义的业务handler
+ WebSocketHandler webSocketHandler = new WebSocketHandler();
+ webSocketHandler.setWebSocketProperties(webSocketProperties);
+ socketChannel.pipeline().addLast("handler", webSocketHandler);
+ }
+
+ public void setWebSocketProperties(WebSocketProperties webSocketProperties) {
+ this.webSocketProperties = webSocketProperties;
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/ClientSocketTypeEnum.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/ClientSocketTypeEnum.java
new file mode 100644
index 00000000..61859fbc
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/ClientSocketTypeEnum.java
@@ -0,0 +1,52 @@
+package ink.wgink.module.instantmessage.websocket.enums;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: AppSocketTypeEnum
+ * @Description: App Socket 类型
+ * @Author: wanggeng
+ * @Date: 2021/1/13 11:20 下午
+ * @Version: 1.0
+ */
+public enum ClientSocketTypeEnum {
+
+ REGISTER(100, "注册消息,body 为 RegisterBody 的 JSON 字符串"),
+ MESSAGE(101, "文本消息"),
+ GROUP_MESSAGE(102, "群发文本消息"),
+ SYSTEM_MESSAGE(103, "系统消息"),
+ SYSTEM_GROUP_MESSAGE(104, "系统群发问呢消息"),
+ SYSTEM_TARGET_MESSAGE(105, "系统目标消息"),
+ LAYIM_HREF_MESSAGE(106, "LAYIM 连接消息"),
+ LAYIM_IMAGE_MESSAGE(107, "LAYIM 图片消息"),
+ LAYIM_FILE_MESSAGE(108, "LAYIM 文件消息"),
+ LAYIM_AUDIO_MESSAGE(109, "LAYIM 音频消息"),
+ LAYIM_VIDEO_MESSAGE(110, "LAYIM 视频消息"),
+ NOTICE(106, "通知"),
+ NOTICE_GROUP_MESSAGE(107, "群通知"),
+ NOTICE_TARGET_MESSAGE(108, "目标通知,用于APP打开特定页面"),
+ UPDATE_SERVICE_HANDLE_STATUS(501, "更新业务的处理状态"),
+ SEARCH_ONLINE_USER(600, "查询全部在线用户,body 为查询用户的 userId"),
+ SEARCH_ONLINE_USER_FRIEND(601, "查询朋友在线用户,body 为查询用户的 userId"),
+ SEARCH_COUNT_NEED_TO_DEALT_WITH(602, "查询全部待办总数"),
+ SEND_STATUS(1100, "发送状态,body 为 BaseResult 的 JSON 字符串"),
+ SEND_STATUS_ONLINE(1101, "发送在线状态,body 为 在线用户的ID JSONArray 字符串"),
+ SEND_STATUS_OFFLINE(1102, "发送离线状态,body 为 离线用户的ID JSONArray 字符串");
+
+ private int value;
+ private String summary;
+
+ ClientSocketTypeEnum(int value, String summary) {
+ this.value = value;
+ this.summary = summary;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String getSummary() {
+ return summary == null ? "" : summary.trim();
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/SendStatusEnum.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/SendStatusEnum.java
new file mode 100644
index 00000000..4812a5be
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/SendStatusEnum.java
@@ -0,0 +1,64 @@
+package ink.wgink.module.instantmessage.websocket.enums;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: ResultCodeEnum
+ * @Description: 返回编码类型
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:59 下午
+ * @Version: 1.0
+ */
+public enum SendStatusEnum {
+ /**
+ * 成功
+ */
+ SUCCESS(200),
+ /**
+ * 失败
+ */
+ FAILED(300),
+ /**
+ * 消息错误
+ */
+ MESSAGE_ERROR(400),
+ /**
+ * 会话错误
+ */
+ SESSION_ERROR(402),
+ /**
+ * APP会话错误
+ */
+ APP_SESSION_ERROR(403),
+ /**
+ * 用户会话错误
+ */
+ USER_SESSION_ERROR(404),
+ /**
+ * 类型错误
+ */
+ TYPE_ERROR(405),
+ /**
+ * 消息体错误
+ */
+ BODY_ERROR(406),
+ /**
+ * 来源错误
+ */
+ FROM_ERROR(407),
+ /**
+ * 接受用户错误
+ */
+ TO_ERROR(408);
+
+ private int value;
+
+ SendStatusEnum(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/WebSocketClientTypeEnum.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/WebSocketClientTypeEnum.java
new file mode 100644
index 00000000..e4c8dbf7
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/enums/WebSocketClientTypeEnum.java
@@ -0,0 +1,24 @@
+package ink.wgink.module.instantmessage.websocket.enums;
+
+/**
+ * @ClassName: WebSocketClientTypeEnum
+ * @Description: 客户端类型
+ * @Author: wanggeng
+ * @Date: 2021/9/11 11:59 下午
+ * @Version: 1.0
+ */
+public enum WebSocketClientTypeEnum {
+
+ APP("app"),
+ WEB("web");
+
+ private String value;
+
+ WebSocketClientTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value == null ? "" : value.trim();
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/AppSessionException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/AppSessionException.java
new file mode 100644
index 00000000..3c56e2b0
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/AppSessionException.java
@@ -0,0 +1,33 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: AppException
+ * @Description: App异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:23 下午
+ * @Version: 1.0
+ */
+public class AppSessionException extends SessionException {
+
+ public AppSessionException() {
+ }
+
+ public AppSessionException(String message) {
+ super(message);
+ }
+
+ public AppSessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AppSessionException(Throwable cause) {
+ super(cause);
+ }
+
+ public AppSessionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BaseSocketException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BaseSocketException.java
new file mode 100644
index 00000000..0ce49288
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BaseSocketException.java
@@ -0,0 +1,33 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: BaseSocketException
+ * @Description: socket基础异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:12 下午
+ * @Version: 1.0
+ */
+public class BaseSocketException extends Exception{
+
+ public BaseSocketException() {
+ }
+
+ public BaseSocketException(String message) {
+ super(message);
+ }
+
+ public BaseSocketException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BaseSocketException(Throwable cause) {
+ super(cause);
+ }
+
+ public BaseSocketException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BodyException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BodyException.java
new file mode 100644
index 00000000..fe1f0722
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/BodyException.java
@@ -0,0 +1,32 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: BodyEmptyException
+ * @Description: 主体为空异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:11 下午
+ * @Version: 1.0
+ */
+public class BodyException extends BaseSocketException {
+ public BodyException() {
+ }
+
+ public BodyException(String message) {
+ super(message);
+ }
+
+ public BodyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BodyException(Throwable cause) {
+ super(cause);
+ }
+
+ public BodyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/SessionException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/SessionException.java
new file mode 100644
index 00000000..e2c39646
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/SessionException.java
@@ -0,0 +1,33 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: SessionInvalidException
+ * @Description: 会话无效异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:04 下午
+ * @Version: 1.0
+ */
+public class SessionException extends BaseSocketException {
+
+ public SessionException() {
+ }
+
+ public SessionException(String message) {
+ super(message);
+ }
+
+ public SessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SessionException(Throwable cause) {
+ super(cause);
+ }
+
+ public SessionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/TypeErrorException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/TypeErrorException.java
new file mode 100644
index 00000000..b510bbef
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/TypeErrorException.java
@@ -0,0 +1,32 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: TypeErrorException
+ * @Description: 类型错误异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:14 下午
+ * @Version: 1.0
+ */
+public class TypeErrorException extends BaseSocketException {
+ public TypeErrorException() {
+ }
+
+ public TypeErrorException(String message) {
+ super(message);
+ }
+
+ public TypeErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TypeErrorException(Throwable cause) {
+ super(cause);
+ }
+
+ public TypeErrorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserErrorException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserErrorException.java
new file mode 100644
index 00000000..e962687e
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserErrorException.java
@@ -0,0 +1,33 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: UserErrorException
+ * @Description: 用户错误异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:51 下午
+ * @Version: 1.0
+ */
+public class UserErrorException extends BaseSocketException {
+
+ public UserErrorException() {
+ }
+
+ public UserErrorException(String message) {
+ super(message);
+ }
+
+ public UserErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UserErrorException(Throwable cause) {
+ super(cause);
+ }
+
+ public UserErrorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserSessionException.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserSessionException.java
new file mode 100644
index 00000000..83735158
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/exception/UserSessionException.java
@@ -0,0 +1,33 @@
+package ink.wgink.module.instantmessage.websocket.exception;
+
+/**
+ * When you feel like quitting. Think about why you started
+ * 当你想要放弃的时候,想想当初你为何开始
+ *
+ * @ClassName: UserException
+ * @Description: 用户异常
+ * @Author: wanggeng
+ * @Date: 2021/1/14 12:21 下午
+ * @Version: 1.0
+ */
+public class UserSessionException extends SessionException {
+
+ public UserSessionException() {
+ }
+
+ public UserSessionException(String message) {
+ super(message);
+ }
+
+ public UserSessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UserSessionException(Throwable cause) {
+ super(cause);
+ }
+
+ public UserSessionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/handler/WebSocketHandler.java b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/handler/WebSocketHandler.java
new file mode 100644
index 00000000..296fe342
--- /dev/null
+++ b/module-instant-message/src/main/java/ink/wgink/module/instantmessage/websocket/handler/WebSocketHandler.java
@@ -0,0 +1,146 @@
+package ink.wgink.module.instantmessage.websocket.handler;
+
+import ink.wgink.module.instantmessage.websocket.handler.text.WebSocketTextHandler;
+import ink.wgink.module.instantmessage.websocket.manager.WebSocketChannelManager;
+import ink.wgink.properties.websocket.WebSocketProperties;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.websocketx.*;
+import io.netty.util.CharsetUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+
+/**
+ * @ClassName: WebSocketHandler
+ * @Description: 1.web发起一次类似是http的请求,并在channelRead0方法中进行处理,并通过instanceof去判断帧对象是FullHttpRequest还是WebSocketFrame,建立连接是时候会是FullHttpRequest
+ * 2.在handleHttpRequest方法中去创建websocket,首先是判断Upgrade是不是websocket协议,若不是则通过sendHttpResponse将错误信息返回给客户端,紧接着通过WebSocketServerHandshakerFactory创建socket对象并通过handshaker握手创建连接
+ * 3.在连接创建好后的所以消息流动都是以WebSocketFrame来体现
+ * 4.在handlerWebSocketFrame去处理消息,也可能是客户端发起的关闭指令,ping指令等等
+ * @Author: wanggeng
+ * @Date: 2021/9/11 11:40 上午
+ * @Version: 1.0
+ */
+public class WebSocketHandler extends SimpleChannelInboundHandler