From 42825d820fca1874a9d4afc7262c2b062afb446b Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sat, 27 Oct 2018 11:19:22 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E6=96=B0=E5=BB=BA=E6=96=B0=E5=88=86?= =?UTF-8?q?=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 1 - mvnw | 225 -------------------------- mvnw.cmd | 143 ---------------- 4 files changed, 369 deletions(-) delete mode 100644 .mvn/wrapper/maven-wrapper.jar delete mode 100644 .mvn/wrapper/maven-wrapper.properties delete mode 100644 mvnw delete mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47610 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 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 6c8c0e0..0000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip diff --git a/mvnw b/mvnw deleted file mode 100644 index 5bf251c..0000000 --- a/mvnw +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd deleted file mode 100644 index 019bd74..0000000 --- a/mvnw.cmd +++ /dev/null @@ -1,143 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% From ad8a0dc0eb04203f8c0c9af8086bc2ebd5a43ee4 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sat, 27 Oct 2018 11:33:35 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=9A=82=E6=9C=AA?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 110 +-------- h5/chat.html | 105 -------- h5/home.html | 47 ---- h5/index.html | 67 ------ h5/logoSmall.png | Bin 8661 -> 0 bytes mqttclient/mqttclient/.gitignore | 25 -- .../mqttclient/.mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 - mqttclient/mqttclient/mvnw | 225 ------------------ mqttclient/mqttclient/mvnw.cmd | 143 ----------- mqttclient/mqttclient/pom.xml | 50 ---- .../mqttclient/MqttclientApplication.java | 12 - .../src/main/resources/application.properties | 0 .../MqttclientApplicationTests.java | 16 -- .../myself/nettychat/tcptest/CRC16myself.java | 101 -------- .../nettychat/tcptest/TCPTestClient.java | 82 ------- 16 files changed, 2 insertions(+), 982 deletions(-) delete mode 100644 h5/chat.html delete mode 100644 h5/home.html delete mode 100644 h5/index.html delete mode 100644 h5/logoSmall.png delete mode 100644 mqttclient/mqttclient/.gitignore delete mode 100644 mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar delete mode 100644 mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties delete mode 100644 mqttclient/mqttclient/mvnw delete mode 100644 mqttclient/mqttclient/mvnw.cmd delete mode 100644 mqttclient/mqttclient/pom.xml delete mode 100644 mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java delete mode 100644 mqttclient/mqttclient/src/main/resources/application.properties delete mode 100644 mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java delete mode 100644 src/main/java/com/myself/nettychat/tcptest/CRC16myself.java delete mode 100644 src/main/java/com/myself/nettychat/tcptest/TCPTestClient.java diff --git a/README.md b/README.md index da3b9b3..8834944 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,6 @@ -# InChat(当前版本1.6.0) +# InChat-IM-API(当前版本0.0.2) ## 简介 ->(InChat)Iot Netty Chat +本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去*[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 -仿微信聊天应用,一步一步更新,基于SpringBoot-WebSocket通用框架,结合Netty进行聊天社交,并记录聊天日志, -异步存储,前端暂用SUI Mobile,添加实现TCP/IP后端通信端口(MQTT协议、可实时与单片机等TCP硬件通信)、加入图片处理流, -聊天实现文字与图片发送功能、API调用Netty长链接执行发送消息(在线数、用户列表) - -## 基本架构图(1.5.2版) - -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/ggg1.png) - -## 功能 - ->实时聊天 ->异步CRUD处理消息日志 ->获取聊天历史 ->用户登录、记录登录用户聊天历史 ->防止二次登录 ->SUI Mobile仿微信样式 ->TCP/IP软硬件通信(8092) ->MQTT协议下的Iot物联网通信(8094) ->图片发送聊天功能 ->API调用Netty长链接执行发送消息(在线用户数、用户列表) ->下版(1.7.0):好友功能等 - -## 版本迭代介绍 - -* 1.0.0版本 - -用户登录,聊天历史,随机用户名,异步数据写入:https://segmentfault.com/a/1190000016615063 - -* 1.2.0版本 - -修复聊天记录功能,实现重复信息录入,完善前端页面,回车监听等:https://segmentfault.com/a/1190000016637814 - -* 1.3.0版本 - -用户注册登录功能,系统聊天绑定用户,禁止二次登录等,前端页面大改 - -* 1.4.1版本 - -本人主导SUI Mobile构建仿微信样式页面版,使用时开F12手机界面 - -* 1.5.2版本 - -TCP/IP软硬件通信-单片机等应用的TCP通信,Netty处理二进制图片发送聊天功能 - -* 1.5.8版本 - -MQTT协议软硬件通信等,Iot物联网 - -* 1.6.0版本 - -API调用Netty长链接执行发送消息(在线数、用户列表):https://segmentfault.com/a/1190000016603392 - - -## 配置 - ->application.yml 数据库配置、Netty参数配置 - ->TCP需先去com.myself.nettychat.tcptest包下执行CRC16myself获取发送数据, - ->再执行TCPTestClient发送数据,请勿随意更改发送格式(通信协议来的) - ->http://localhost:8080/susu/admin/loginsui 启动访问路径 - ->mqtt协议测试在mqttclient包下 - ->http://localhost:8080/susu/swagger-ui.html 查看API文档 - -## 效果图 - -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/001%20(5).png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/001%20(3).png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/001%20(4).png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/001%20(2).png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/001%20(1).png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/9.png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/10.png) -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/11.png) - -## 预留BUG - -``` -io.netty.handler.codec.CorruptedFrameException: Max frame length of 65536 has been exceeded. -图片过大,需要在前端做图片上传压缩 - -Uncaught TypeError: msg.substring is not a function at WebSocket.socket.onmessage (newChat.js:38) -前端代码的一点问题,不影响项目正常运行 - -java.io.IOException: 远程主机强迫关闭了一个现有的连接。 -TCP客户端连接主动关闭,不影响,良性报错 -``` - -## 下载地址 - -下载地址:https://github.com/UncleCatMySelf/SBToNettyChat/releases - -## 交流与提问 - -提问与Bug上报:https://github.com/UncleCatMySelf/SBToNettyChat/issues - -QQ群:628793702(仅供交流,不提供问题解答) - -## 关于作者 - -个人公众号:UncleCatMySelf - -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/%E5%85%AC%E4%BC%97%E5%8F%B7.png) diff --git a/h5/chat.html b/h5/chat.html deleted file mode 100644 index 91d15b2..0000000 --- a/h5/chat.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - WebSocket Chat - - - - -
-

SpringBoot netty 聊天室

- -
- - -
-
-
- - \ No newline at end of file diff --git a/h5/home.html b/h5/home.html deleted file mode 100644 index 541e6b7..0000000 --- a/h5/home.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - 酥酥 - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/h5/index.html b/h5/index.html deleted file mode 100644 index 32e88ea..0000000 --- a/h5/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - 酥酥 - - - - - - - - - - -
-
- -
-

登录

-
-
-
-
    - -
  • -
    -
    -
    -
    账号
    -
    - -
    -
    -
    -
  • -
  • -
    -
    -
    -
    密码
    -
    - -
    -
    -
    -
  • -
-
-
-
- - -
-
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/h5/logoSmall.png b/h5/logoSmall.png deleted file mode 100644 index 08b5d51eb40008001afd2293b59a107f0bfc474d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8661 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 diff --git a/mqttclient/mqttclient/.gitignore b/mqttclient/mqttclient/.gitignore deleted file mode 100644 index 82eca33..0000000 --- a/mqttclient/mqttclient/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/build/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar b/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47610 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 diff --git a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties b/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 6c8c0e0..0000000 --- a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip diff --git a/mqttclient/mqttclient/mvnw b/mqttclient/mqttclient/mvnw deleted file mode 100644 index 5bf251c..0000000 --- a/mqttclient/mqttclient/mvnw +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mqttclient/mqttclient/mvnw.cmd b/mqttclient/mqttclient/mvnw.cmd deleted file mode 100644 index 019bd74..0000000 --- a/mqttclient/mqttclient/mvnw.cmd +++ /dev/null @@ -1,143 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/mqttclient/mqttclient/pom.xml b/mqttclient/mqttclient/pom.xml deleted file mode 100644 index 2d1b502..0000000 --- a/mqttclient/mqttclient/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - com.myself - mqttclient - 0.0.1-SNAPSHOT - jar - - mqttclient - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 2.0.5.RELEASE - - - - - UTF-8 - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - diff --git a/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java b/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java deleted file mode 100644 index 569cf76..0000000 --- a/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.myself.mqttclient; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class MqttclientApplication { - - public static void main(String[] args) { - SpringApplication.run(MqttclientApplication.class, args); - } -} diff --git a/mqttclient/mqttclient/src/main/resources/application.properties b/mqttclient/mqttclient/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java b/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java deleted file mode 100644 index 5c938a0..0000000 --- a/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.myself.mqttclient; - -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 MqttclientApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/src/main/java/com/myself/nettychat/tcptest/CRC16myself.java b/src/main/java/com/myself/nettychat/tcptest/CRC16myself.java deleted file mode 100644 index 9f50137..0000000 --- a/src/main/java/com/myself/nettychat/tcptest/CRC16myself.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.myself.nettychat.tcptest; - -import java.math.BigInteger; - -/** - * 获取数据格式,请勿乱更改test的值或长度 - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 19:39 2018\9\20 0020 - */ -public class CRC16myself { - - public static void main(String[] args) { - String test = "F5690137563CC8syyyyyyyyyyyyyyyyynnnnnnn"; - System.out.println("原始内容字符串:" + test); - String crcString = getCRC(test.getBytes()); - System.out.println("str:" + crcString); - int crc = getCRCInt(test.getBytes()); - System.out.println("hex:" + crc); - CRC16myself myself = new CRC16myself(); - float crc16 = myself.parseHex2Float(crcString); - System.out.println("10进制浮点型:" + crc16); - String crc16String = myself.parseFloat2Hex(crc16); - System.out.println("十六进制浮点型:" + crc16String); - System.out.println("输出字符串:" + "gz" + test + crcString + "xr"); - } - - /** - * 计算CRC16校验码 - * - * @param bytes 字节数组 - * @return {@link String} 校验码 - * @since 1.0 - */ - public static String getCRC(byte[] bytes) { - int CRC = 0x0000ffff; - int POLYNOMIAL = 0x0000a001; - int i, j; - for (i = 0; i < bytes.length; i++) { - CRC ^= ((int) bytes[i] & 0x000000ff); - for (j = 0; j < 8; j++) { - if ((CRC & 0x00000001) != 0) { - CRC >>= 1; - CRC ^= POLYNOMIAL; - } else { - CRC >>= 1; - } - } - } - return Integer.toHexString(CRC); - } - - - /** - * 计算CRC16校验码 - * - * @param bytes 字节数组 - * @return {@link String} 校验码 - * @since 1.0 - */ - public static Integer getCRCInt(byte[] bytes) { - int CRC = 0x0000ffff; - int POLYNOMIAL = 0x0000a001; - int i, j; - for (i = 0; i < bytes.length; i++) { - CRC ^= ((int) bytes[i] & 0x000000ff); - for (j = 0; j < 8; j++) { - if ((CRC & 0x00000001) != 0) { - CRC >>= 1; - CRC ^= POLYNOMIAL; - } else { - CRC >>= 1; - } - } - } - return CRC; - } - - /** - * 将16进制单精度浮点型转换为10进制浮点型 - * - * @return float - * @since 1.0 - */ - private float parseHex2Float(String hexStr) { - BigInteger bigInteger = new BigInteger(hexStr, 16); - return Float.intBitsToFloat(bigInteger.intValue()); - } - - /** - * 将十进制浮点型转换为十六进制浮点型 - * - * @return String - * @since 1.0 - */ - private String parseFloat2Hex(float data) { - return Integer.toHexString(Float.floatToIntBits(data)); - } - -} diff --git a/src/main/java/com/myself/nettychat/tcptest/TCPTestClient.java b/src/main/java/com/myself/nettychat/tcptest/TCPTestClient.java deleted file mode 100644 index 083983a..0000000 --- a/src/main/java/com/myself/nettychat/tcptest/TCPTestClient.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.myself.nettychat.tcptest; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; -import java.net.SocketTimeoutException; - -/** - * 先去同包下CRC16myself下执行获取数据,数据格式有规定 - * TCP端测试模拟,建议放置在另一个项目 - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 19:40 2018\9\20 0020 - */ -public class TCPTestClient { - - public static void main(String[] args) throws IOException { - //10万测试 - for (int i = 0;i<100000;i++){ - new Thread(new Runnable() { - @Override - public void run() { - try { - runtest(); - }catch (Exception e){ - e.printStackTrace(); - } - } - }).start(); - sleep(100); - } - } - - private static void runtest() throws IOException{ - //客户端请求与本机在18866端口建立TCP连接 - Socket client = new Socket("127.0.0.1", 8092); - client.setSoTimeout(10000); - //获取键盘输入 - BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); - //获取Socket的输出流,用来发送数据到服务端 - PrintStream out = new PrintStream(client.getOutputStream()); - //获取Socket的输入流,用来接收从服务端发送过来的数据 - BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream())); - boolean flag = true; - int i = 1; - while(flag){ - //if (i == 1){ - //帧头+ID+数据类型+24把锁状态+crc校验+帧尾 - String str = "test"; - //发送数据到服务端 - out.println(str); - if("bye".equals(str)){ - flag = false; - }else{ - try{ - //从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常 - String echo = buf.readLine(); - System.out.println(echo); - }catch(SocketTimeoutException e){ - System.out.println("Time out, No response"); - } - } - sleep(5000); - } - input.close(); - if(client != null){ - //如果构造函数建立起了连接,则关闭套接字,如果没有建立起连接,自然不用关闭 - client.close(); //只关闭socket,其关联的输入输出流也会被关闭 - } - } - - private static void sleep(Integer time){ - try { - Thread.sleep(time); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} From 0a9fc2402c8b7bd3e3c47bba913b7b363fc414a1 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sun, 28 Oct 2018 16:22:37 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8834944..62beccd 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,5 @@ ## 简介 -本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去*[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 +本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 From 3219d74f63dca19abbb3366310670c046350ffe3 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sun, 28 Oct 2018 16:29:52 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 - src/main/resources/static/css/allchat.css | 59 ------ src/main/resources/static/css/chat.css | 43 ----- src/main/resources/static/css/newChat.css | 172 ------------------ src/main/resources/static/css/registered.css | 160 ---------------- src/main/resources/static/image/logoBig.jpg | Bin 137374 -> 0 bytes src/main/resources/static/image/logoSmall.png | Bin 8661 -> 0 bytes src/main/resources/static/image/nuandao.png | Bin 62980 -> 0 bytes src/main/resources/static/js/chat.js | 51 ------ src/main/resources/static/js/newChat.js | 165 ----------------- src/main/resources/static/js/registered.js | 72 -------- src/main/resources/templates/chat/allchat.ftl | 49 ----- src/main/resources/templates/chat/chat.ftl | 33 ---- src/main/resources/templates/common/floor.ftl | 3 - .../resources/templates/common/header.ftl | 12 -- src/main/resources/templates/find/find.ftl | 41 ----- src/main/resources/templates/h5.ftl | 58 ------ src/main/resources/templates/home/home.ftl | 62 ------- src/main/resources/templates/login/login.ftl | 57 ------ .../resources/templates/login/loginSui.ftl | 124 ------------- src/main/resources/templates/me/me.ftl | 78 -------- 21 files changed, 1243 deletions(-) delete mode 100644 src/main/resources/static/css/allchat.css delete mode 100644 src/main/resources/static/css/chat.css delete mode 100644 src/main/resources/static/css/newChat.css delete mode 100644 src/main/resources/static/css/registered.css delete mode 100644 src/main/resources/static/image/logoBig.jpg delete mode 100644 src/main/resources/static/image/logoSmall.png delete mode 100644 src/main/resources/static/image/nuandao.png delete mode 100644 src/main/resources/static/js/chat.js delete mode 100644 src/main/resources/static/js/newChat.js delete mode 100644 src/main/resources/static/js/registered.js delete mode 100644 src/main/resources/templates/chat/allchat.ftl delete mode 100644 src/main/resources/templates/chat/chat.ftl delete mode 100644 src/main/resources/templates/common/floor.ftl delete mode 100644 src/main/resources/templates/common/header.ftl delete mode 100644 src/main/resources/templates/find/find.ftl delete mode 100644 src/main/resources/templates/h5.ftl delete mode 100644 src/main/resources/templates/home/home.ftl delete mode 100644 src/main/resources/templates/login/login.ftl delete mode 100644 src/main/resources/templates/login/loginSui.ftl delete mode 100644 src/main/resources/templates/me/me.ftl diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 77aaf54..6da58e9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,10 +6,6 @@ spring: url: jdbc:mysql://localhost:3306/nettychat?characterEncoding=utf-8&useSSL=false jpa: show-sql: true -# database: oracle -# properties: -# hibernate: -# dialect: org.hibernate.dialect.Oracle12cDialect server: servlet: context-path: /susu diff --git a/src/main/resources/static/css/allchat.css b/src/main/resources/static/css/allchat.css deleted file mode 100644 index b35a89f..0000000 --- a/src/main/resources/static/css/allchat.css +++ /dev/null @@ -1,59 +0,0 @@ -.history{ - width: 90%; -} - -.his{ - margin-right: auto; - border-left: 20px; - padding-left: 20px; -} - -.chat { - width: 100%; - position: absolute; - box-sizing: border-box; - padding: 10px 4px; - padding-top: 20px; - bottom: 0; -} - -.msgCente{ - text-align: center; - margin-bottom: 100px; -} - -.chatimg{ - height: 90px; - width: 90px; -} - -.msgRight { - text-align: right; -} - -.msgLeft { - text-align: left; -} - -.msgLeft, -.msgRight { - margin-bottom: 26px; -} - -.msgLeft>span, -.msgRight>span { - padding: 10px; - font-size: 16px; - line-height: 20px; - border-radius: 10px; -} - -.msgLeft>span { - background-color: #d8f1f9; - box-shadow: inset -2px -3px 8px 1px #75ddff; -} - -.msgRight>span { - background-color: #FFF; - box-shadow: inset -2px -3px 8px 1px #d8d8d8; -} \ No newline at end of file diff --git a/src/main/resources/static/css/chat.css b/src/main/resources/static/css/chat.css deleted file mode 100644 index 3a25d39..0000000 --- a/src/main/resources/static/css/chat.css +++ /dev/null @@ -1,43 +0,0 @@ -form { - width: 406px; - height: 650px; - border: 4px solid #98bcde; - border-radius: 10px; - margin: 0 auto; - background-color: #eceff9; - display: flex; - flex-wrap: wrap; - justify-content: space-around; -} - -h3 { - color: #92acdc; - text-align: center; - font-size: 26px; -} - -textarea { - resize: none; - font-size: 20px; - width: 401px; - height: 511px; -} - -.msg { - width: 324px; - height: 40px; - text-indent: 10px; - font-size: 20px; - outline: none; -} - -.btn { - width: 78px; - height: 46px; - background-color: #d8f1f9; - border-radius: 6px; - border: 1px solid #98bcde; - font-size: 18px; - color: #92acdc; - font-weight: bold; -} \ No newline at end of file diff --git a/src/main/resources/static/css/newChat.css b/src/main/resources/static/css/newChat.css deleted file mode 100644 index 77f1406..0000000 --- a/src/main/resources/static/css/newChat.css +++ /dev/null @@ -1,172 +0,0 @@ -.container { - width: 750px; - border: 4px solid #98bcde; - border-radius: 10px; - margin: 100px auto; - background-color: #fff; - /*display: flex; - justify-content: space-around;*/ - display: table; - clear: both; - overflow: hidden; -} - -.left_content { - width: 160px; - height: 600px; - background-color: #d8f1f9; - float: left; - text-align: center; -} - -.content { - width: 590px; - height: 600px; - background-color: #FFF; - float: right; -} - -.content_top { - width: 100%; - height: 80px; - background-color: #6fdcff; - position: relative; -} - -.content_top .tips { - text-align: center; - font-size: 16px; - line-height: 30px; - color: #000000; -} - -.content_bodyer { - width: 100%; - height: 410px; - background: #f4f4f4; - overflow: hidden; - position: relative; -} - -.chat { - width: 100%; - position: absolute; - box-sizing: border-box; - padding: 10px 4px; - padding-top: 20px; - bottom: 0; -} - -form { - width: 100%; - height: 110px; - position: relative; -} - -textarea { - resize: none; - font-size: 20px; - width: 100%; - height: 511px; -} - -.msg { - width: 486px; - border: none; - height: 74px; - box-sizing: border-box; - padding: 4px 10px; - font-size: 20px; - outline: none; -} - -.btn { - width: 78px; - height: 30px; - background-color: #d8f1f9; - border-radius: 6px; - border: 1px solid #98bcde; - font-size: 16px; - color: #92acdc; - font-weight: bold; - position: absolute; - bottom: 4px; - right: 4px; -} - -.openHistory { - font-size: 16px; - color: #000; - z-index: 10; - position: absolute; - bottom: 5px; - right: 5px; - cursor: pointer; -} - -.history { - width: 250px; - height: 410px; - background-color: rgba(0, 0, 0, 0.5); - color: #FFF; - font-size: 16px; - position: absolute; - top: 0; - right: 0; - display: none; - z-index: 10; -} - -.scrollbar { - width: 12px; - height: 100%; - background: black; - position: absolute; - right: 0; - top: 0; - opacity: 0.5; - overflow: hidden; - display: none; - z-index: 10; -} - -.scrollbar .thumb { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 40px; - background: #6d6a6a; - cursor: pointer; -} - -.msgRight { - text-align: right; -} - -.msgLeft { - text-align: left; -} - -.msgLeft, -.msgRight { - margin-bottom: 26px; -} - -.msgLeft>span, -.msgRight>span { - padding: 10px; - font-size: 16px; - line-height: 20px; - border-radius: 10px; -} - -.msgLeft>span { - background-color: #d8f1f9; - box-shadow: inset -2px -3px 8px 1px #75ddff; -} - -.msgRight>span { - background-color: #FFF; - box-shadow: inset -2px -3px 8px 1px #d8d8d8; -} \ No newline at end of file diff --git a/src/main/resources/static/css/registered.css b/src/main/resources/static/css/registered.css deleted file mode 100644 index 206cf22..0000000 --- a/src/main/resources/static/css/registered.css +++ /dev/null @@ -1,160 +0,0 @@ -* { - margin: 0; - padding: 0; -} - -.bodyer .panel { - width: 362px; - height: 320px; - border-radius: 6px; - margin: 90px auto; - background: #FFF; - position: relative; - overflow: hidden; -} - -.logo { - width: 100px; - height: 100px; - background: url("../image/logoSmall.png") no-repeat; - position: absolute; - top: 29px; - right: -8px; - transform: rotate(30deg); -} - -.panel_tit { - text-align: center; - line-height: 40px; - font-size: 24px; - padding-top: 20px; - color: #92acdc; -} - -.bodyer .panel .register { - padding-left: 45px; - float: left; - color: #999; - width: 268px; - position: relative; -} - -.bodyer .panel .register .register_top { - font-size: 14px; - margin: 30px 0 26px; -} - -.bodyer .panel .register .register_top span { - margin: 0 2px; -} - -.bodyer .panel .register .register_top a { - font-size: 14px; - text-decoration: none; -} - -.bodyer .panel .register .register_top a:first-child { - color: #303030; -} - -.bodyer .panel .register .register_top a:last-child { - color: #999; -} - -.bodyer .panel .register input { - font-size: 14px; - width: 268px; - height: 38px; - display: block; - box-sizing: border-box; - border: 1px solid #dfdfdf; - padding: 8px 0 8px 36px; -} - -.bodyer .panel .register .user_icon { - position: relative; - margin-bottom: 18px; -} - -.bodyer .panel .register .user_icon input:focus { - border: 1px solid #98bcde; - outline: #98bcde solid 1px; -} - -.bodyer .panel .register .user_icon i { - position: absolute; - display: block; - width: 30px; - height: 30px; - background: url("../image/nuandao.png") no-repeat -310px -390px; -} - -.bodyer .panel .register .pass_icon { - position: relative; - margin-bottom: 30px; -} - -.bodyer .panel .register .pass_icon input:focus { - border: 1px solid #98bcde; - outline: #98bcde solid 1px; -} - -.bodyer .panel .register .pass_icon i { - position: absolute; - display: block; - width: 30px; - height: 30px; - background: url("../image/nuandao.png") no-repeat -333px -434px; -} - -.bodyer .panel .register .pass_icon span { - position: absolute; - left: 0; - bottom: -20px; - color: #d9534f; - font-size: 13px; - display: none; -} - -.bodyer .panel .register input.btn1 { - margin: 0; - padding: 0; - display: block; - width: 100%; - height: 40px; - font-size: 16px; - line-height: 40px; - text-align: center; - text-decoration: none; - color: #FFF; - background: #98bcde; - margin-bottom: 20px; -} - -.bodyer .panel .register input.btn1:hover { - background: #5694ce; -} - -.tips { - width: 100px; - height: 30px; - text-align: center; - font-size: 14px; - line-height: 30px; - background-color: rgba(0, 0, 0, 0.5); - color: #FFF; - position: absolute; - top: 35%; - left: 40%; - display: none; -} - -.mask2 { - width: 100%; - height: 100%; - z-index: -100; - position: fixed; - top: 0; - left: 0; - background: rgba(0, 0, 0, 0.5); -} \ No newline at end of file diff --git a/src/main/resources/static/image/logoBig.jpg b/src/main/resources/static/image/logoBig.jpg deleted file mode 100644 index 0c62304cc054799f416de6d121214a05ecd90830..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137374 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

#9~{ty;#sH!*QVc3J0 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 diff --git a/src/main/resources/static/image/nuandao.png b/src/main/resources/static/image/nuandao.png deleted file mode 100644 index be015d1bf319cd13e32c5b9bcf66451d732dd398..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62980 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 diff --git a/src/main/resources/static/js/chat.js b/src/main/resources/static/js/chat.js deleted file mode 100644 index 3380613..0000000 --- a/src/main/resources/static/js/chat.js +++ /dev/null @@ -1,51 +0,0 @@ -var socket; -if(!window.WebSocket) { - window.WebSocket = window.MozWebSocket; -} - -if(window.WebSocket) { - socket = new WebSocket("ws://localhost:8090/ws"); - socket.onmessage = function(event) { - var ta = document.getElementById('responseText'); - ta.value = ta.value + '\n' + event.data - }; - socket.onopen = function(event) { - var ta = document.getElementById('responseText'); - if(msg.length > 0){ - ta.value = "--- 连接开启! ---"+'\n'+msg; - }else{ - ta.value = "--- 连接开启! ---" - } - }; - socket.onclose = function(event) { - var ta = document.getElementById('responseText'); - ta.value = ta.value + "连接被关闭"; - }; -} else { - alert("你的浏览器不支持 WebSocket!"); -} - -function send(message) { - if(!window.WebSocket) { - return; - } - if(socket.readyState == WebSocket.OPEN) { - socket.send(message); - } else { - alert("连接没有开启."); - } -} - -window.onbeforeunload = function(event) { - event.returnValue = "刷新提醒"; -} - -; -document.onkeydown = function(e) { - var userName = document.getElementById('userName'); - if(e.keyCode == 13) { - var message = userName.value + '-' +document.getElementsByClassName('msg')[0].value; - send(message); - document.getElementsByClassName('msg')[0].value = ''; - } -} \ No newline at end of file diff --git a/src/main/resources/static/js/newChat.js b/src/main/resources/static/js/newChat.js deleted file mode 100644 index 1bc7e78..0000000 --- a/src/main/resources/static/js/newChat.js +++ /dev/null @@ -1,165 +0,0 @@ -var socket; -if(!window.WebSocket) { - window.WebSocket = window.MozWebSocket; -} - -var chars = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']; - -function generateMixed(n) { - var res = ""; - for(var i = 0; i < n ; i ++) { - var id = Math.ceil(Math.random()*35); - res += chars[id]; - } - return res; -} - -if(window.WebSocket) { - socket = new WebSocket("ws://localhost:8090/ws"); - socket.onmessage = function(event) { - var msg = event.data; - console.log(msg); - if(msg instanceof Blob){ - console.log("blobs"); - var idran = generateMixed(3); - var ta = "

"; - $('.chat').append(ta); - //var previewImg = document.querySelector('img'); - var previewImg = document.getElementById(idran); - - var reader = new FileReader(); - // 监听reader对象的的onload事件,当图片加载完成时,把base64编码賦值给预览图片 - reader.addEventListener("load", function () { - previewImg.src = reader.result; - }, false); - // 调用reader.readAsDataURL()方法,把图片转成base64 - reader.readAsDataURL(msg); - } - if(msg.substring(0, 1) == '[') { - var ta = "
"+event.data+"
"; - $('.chat').append(ta); - } else { - var ta = "
"+event.data+"
"; - $('.chat').append(ta); - } - }; - socket.onopen = function(event) { - $('.tips').html('连接开启!'); - $('.tips').css('color', 'green'); - - var his = $('#TTHistory'); - - console.log(msg); - his.html(msg); - }; - socket.onclose = function(event) { - $('.tips').html('连接被关闭'); - $('.tips').css('color', 'red'); - }; -} else { - var ta = "
你的浏览器不支持 WebSocket!
"; - $('.content_bodyer').append(ta); -} - -function send(message) { - if(!window.WebSocket) { - return; - } - if(socket.readyState == WebSocket.OPEN) { - socket.send(message); - } else { - alert("连接没有开启."); - } -} - -window.onbeforeunload = function(event) { - event.returnValue = "刷新提醒"; -} - -; -// document.onkeydown = function(e) { -// -// var userName = $('#userName'); -// if(e.keyCode == 13) { -// $('.chat').css('bottom', 0); -// var message = userName.val() + '-' + $('.msg').val().trim(); -// send(message); -// $('.msg').val(''); -// } -// } - -function sendd() { - var userName = $('#userName'); - var message = userName.val() + '-' + $('.msg').val().trim(); - send(message); - sendFile(); - $('.msg').val(''); -} - -$('.openHistory').click(function() { - $('.history').fadeToggle(); -}); - -$('.content_bodyer').mouseenter(function() { - $('.scrollbar').fadeIn(); -}) - -$('.content_bodyer').mouseleave(function() { - $('.scrollbar').fadeOut(); -}) - -function sendFile(){ - var thum = $('#file')[0].files[0]; - if(!thum) return; - console.log(thum); - var reader = new FileReader(); - //以二进制形式读取文件 - reader.readAsArrayBuffer(thum); - //文件读取完毕后该函数响应 - reader.onload = function loaded(evt) { - console.log(evt); - var blob = evt.target.result; - //发送二进制表示的文件 - socket.send(blob); - console.log(blob); - - console.log("finnish"); - } -} - -var thumb = $('.thumb'); -var scrollBar = $('.scrollbar'); -var chat = $('.chat'); - -thumb.on('mousedown', function(e) { - chat.css('bottom', 'initial'); - thumb.isMouseDown = true; - return false; -}); - -thumb.on('selectstart', function() { - return false; -}); - -$(window).on('mouseup', function(e) { - thumb.isMouseDown = false; -}); - -$(window).on('mousemove', function(e) { - if (thumb.isMouseDown){ - var pos = e.clientY - scrollBar.parent().offset().top - thumb.height() / 2; - if (pos < 0 ){ - pos = 0; - } else if(pos > scrollBar.height() - thumb.height()){ - pos = scrollBar.height() - thumb.height(); - } - thumb.css("top", pos + "px"); - - // 计算thumb所在的位置占父亲的% - var percentage = thumb.offset().top / (scrollBar.height() - thumb.height()); - var top = (chat.height() - chat.parent().height()) * percentage; - chat.css("top", -top + "px"); - } -}) - - diff --git a/src/main/resources/static/js/registered.js b/src/main/resources/static/js/registered.js deleted file mode 100644 index ebd3ea0..0000000 --- a/src/main/resources/static/js/registered.js +++ /dev/null @@ -1,72 +0,0 @@ -var register = $('.register_top').children().first(); -var register2 = $('.register_top').children().last(); -register.click(function () { - $(register).css('color', ' #303030'); - register2.css('color', '#999'); - $('.btn1').val('登陆'); - $('form').attr("action", "/susu/admin/toLogin"); -}); -register2.click(function () { - $(register2).css('color', ' #303030'); - register.css('color', '#999'); - $('.btn1').val('注册'); - $('form').attr("action", "/susu/admin/toRegister"); -}); -$('.btn3').click(function () { - $('.more').slideToggle(0); -}); - -/** - * 用户名验证 - */ -var input1 = $('.user_icon').children('input'); -var userName = /^[\u4e00-\u9fa5_a-zA-Z]{2,5}$/; -input1.blur(function () { - var val = input1.val().trim(); - if (val.length == 0) { - $($('.Span').children()[0]).css('display', 'block'); - $($('.Span').children()[0]).siblings().css('display', 'none'); - } else if (userName.test(val)) { - $($('.Span').children('span')).css('display', 'none'); - } else { - $($('.Span').children()[1]).css('display', 'block'); - $($('.Span').children()[1]).siblings().css('display', 'none'); - } -}); - -/** - * 密码验证 - */ -var input2 = $('.pass_icon').children('input'); -// 关于密码的正则表达式(6-16位数字和字母的组合) -var Password = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$/; -input2.blur(function () { - var val = input2.val().trim(); - if (val.length == 0) { - $($('.Span').children()[2]).css('display', 'block'); - $($('.Span').children()[2]).siblings().css('display', 'none'); - } else if (Password.test(val)) { - console.log(1); - $($('.Span').children('span')).css('display', 'none'); - } else { - $($('.Span').children()[3]).css('display', 'block'); - $($('.Span').children()[3]).siblings().css('display', 'none'); - } -}); - -//登录注册校验 -$('form').submit(function(e) { - var User = $('.user_icon').children('input'); - var Pass = $('.pass_icon').children('input'); - var md5_pwd= $('#md5_pwd'); - if (userName.test(User) && Password.test(Pass)) { - return true; - } -}) - -function MsgTo() { - $('.tips').fadeIn(10, function() { - $('.tips').fadeOut(1500); - }); -} - diff --git a/src/main/resources/templates/chat/allchat.ftl b/src/main/resources/templates/chat/allchat.ftl deleted file mode 100644 index 317cd89..0000000 --- a/src/main/resources/templates/chat/allchat.ftl +++ /dev/null @@ -1,49 +0,0 @@ - - -<#include "../common/header.ftl"> - - - -
-
- - - -

全体用户

-
- -
-
- -
-
- -
-
-
-
-
- -
- -
-
- <#--Image preview area...--> -
-
- - -<#include "../common/floor.ftl"> - - - \ No newline at end of file diff --git a/src/main/resources/templates/chat/chat.ftl b/src/main/resources/templates/chat/chat.ftl deleted file mode 100644 index f18b178..0000000 --- a/src/main/resources/templates/chat/chat.ftl +++ /dev/null @@ -1,33 +0,0 @@ - - -<#include "../common/header.ftl"> - - -<#include "../common/floor.ftl"> - - \ No newline at end of file diff --git a/src/main/resources/templates/common/floor.ftl b/src/main/resources/templates/common/floor.ftl deleted file mode 100644 index 9b8124e..0000000 --- a/src/main/resources/templates/common/floor.ftl +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/main/resources/templates/common/header.ftl b/src/main/resources/templates/common/header.ftl deleted file mode 100644 index d519ed4..0000000 --- a/src/main/resources/templates/common/header.ftl +++ /dev/null @@ -1,12 +0,0 @@ - - - - 酥酥 - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/find/find.ftl b/src/main/resources/templates/find/find.ftl deleted file mode 100644 index b0feb38..0000000 --- a/src/main/resources/templates/find/find.ftl +++ /dev/null @@ -1,41 +0,0 @@ - - -<#include "../common/header.ftl"> - -
-
-

酥酥

-
- -
-
-
    -
  • -
    -
    朋友圈
    -
    -
  • -
-
-
-
-<#include "../common/floor.ftl"> - - \ No newline at end of file diff --git a/src/main/resources/templates/h5.ftl b/src/main/resources/templates/h5.ftl deleted file mode 100644 index a650b30..0000000 --- a/src/main/resources/templates/h5.ftl +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - Su Su - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/home/home.ftl b/src/main/resources/templates/home/home.ftl deleted file mode 100644 index f76b3b4..0000000 --- a/src/main/resources/templates/home/home.ftl +++ /dev/null @@ -1,62 +0,0 @@ - - -<#include "../common/header.ftl"> - - -
-
-

酥酥

-
- -
- - - <#--
--> -
-
    -
  • -
    -
    全体用户
    -
    -
  • -
-
- <#-- - <#--
--> - <#--
--> - <#--
--> - <#--
--> -
-
-<#include "../common/floor.ftl"> - - - \ No newline at end of file diff --git a/src/main/resources/templates/login/login.ftl b/src/main/resources/templates/login/login.ftl deleted file mode 100644 index 17e5a2a..0000000 --- a/src/main/resources/templates/login/login.ftl +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - Su Su - - - - - - - -
-
- - - -
-
- -

“酥酥”聊天室

-
-
- 登录 - | - 注册 -
-
-
-
- - -
- 请填写用户名 - 2-5位中文字符和英文字符 - 请填写6-16位数字和字母的组合密码 - 密码格式有误 - <#--${msg!''}--> -
-
- - -
${msg!''}
- -
-
- - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/login/loginSui.ftl b/src/main/resources/templates/login/loginSui.ftl deleted file mode 100644 index 40a962c..0000000 --- a/src/main/resources/templates/login/loginSui.ftl +++ /dev/null @@ -1,124 +0,0 @@ - - -<#include "../common/header.ftl"> - -
-
- -
-

登录

-
- - - - -
-
-
    -
  • -
    -
    -
    -
    账号
    -
    - -
    -
    -
    -
  • -
  • -
    -
    -
    -
    密码
    -
    - -
    -
    -
    -
  • -
-
-
-
- - -
-
-
- -
- -
-
-

注册

-
-
-
- -
-
    - -
  • -
    -
    -
    -
    账号
    -
    - -
    -
    -
    -
  • -
  • -
    -
    -
    -
    密码
    -
    - -
    -
    -
    -
  • -
-
-
-
- - -
-
- -
-
-
- -
-<#include "../common/floor.ftl"> - - - \ No newline at end of file diff --git a/src/main/resources/templates/me/me.ftl b/src/main/resources/templates/me/me.ftl deleted file mode 100644 index 5997f0d..0000000 --- a/src/main/resources/templates/me/me.ftl +++ /dev/null @@ -1,78 +0,0 @@ - - -<#include "../common/header.ftl"> - -
-
-

酥酥

-
- -
-
-
    -
  • -
    -
    ${userName!''}
    -
    -
  • -
-
-
-
-
    -
  • -
    -
    收藏
    -
    -
  • -
-
-
-
    -
  • -
    -
    相册
    -
    -
  • -
-
-
-
    -
  • -
    -
    卡包
    -
    -
  • -
-
-
-
    -
  • -
    -
    表情
    -
    -
  • -
-
-
-
-<#include "../common/floor.ftl"> - - \ No newline at end of file From a570b3a0ddfb35b2a301ebf3b72458ea2d2f41fa Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sun, 28 Oct 2018 17:00:23 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + .../myself/nettychat/DefaultAutoService.java | 19 - .../com/myself/nettychat/auto/InitServer.java | 38 -- .../nettychat/auto/ServerAutoConfigure.java | 86 ---- .../bootstrap/AbstractBootstrapServer.java | 117 ----- .../myself/nettychat/bootstrap/BaseApi.java | 76 --- .../nettychat/bootstrap/BaseAuthService.java | 13 - .../nettychat/bootstrap/BootstrapServer.java | 18 - .../nettychat/bootstrap/ChannelService.java | 40 -- .../bootstrap/NettyBootstrapServer.java | 134 ----- .../nettychat/bootstrap/bean/MqttChannel.java | 115 ----- .../bootstrap/bean/RetainMessage.java | 18 - .../bootstrap/bean/SendMqttMessage.java | 37 -- .../bootstrap/bean/SessionMessage.java | 29 -- .../nettychat/bootstrap/bean/WillMeaasge.java | 23 - .../channel/AbstractChannelService.java | 112 ----- .../channel/ClientSessionService.java | 36 -- .../bootstrap/channel/MqttChannelService.java | 440 ----------------- .../bootstrap/channel/MqttHandlerService.java | 223 --------- .../bootstrap/channel/PublishApiSevice.java | 162 ------- .../bootstrap/channel/WillService.java | 52 -- .../bootstrap/channel/cache/CacheMap.java | 137 ------ .../coder/ByteBufToWebSocketFrameEncoder.java | 26 - .../coder/WebSocketFrameToByteBufDecoder.java | 25 - .../bootstrap/handler/DefaultMqttHandler.java | 105 ---- .../bootstrap/scan/SacnScheduled.java | 59 --- .../bootstrap/scan/ScanRunnable.java | 44 -- .../nettychat/common/enums/ConfirmStatus.java | 13 - .../nettychat/common/enums/ProtocolEnum.java | 14 - .../nettychat/common/enums/QosStatus.java | 14 - .../nettychat/common/enums/SessionStatus.java | 13 - .../nettychat/common/enums/SubStatus.java | 11 - .../mqtts/ClientMqttHandlerService.java | 27 -- .../nettychat/common/mqtts/MqttHander.java | 50 -- .../common/mqtts/MqttHandlerIntf.java | 26 - .../mqtts/ServerMqttHandlerService.java | 28 -- .../common/properties/InitNetty.java | 5 +- .../common/ssl/SecureSocketKeyStore.java | 456 ------------------ .../ssl/SecureSocketSslContextFactory.java | 89 ---- .../ssl/SecureSokcetTrustManagerFactory.java | 63 --- .../nettychat/common/ssl/StreamReader.java | 25 - .../nettychat/common/ssl/X509CertTool.java | 69 --- .../common/zookeeper/ZkStateListener.java | 22 - .../nettychat/common/zookeeper/ZkUtils.java | 366 -------------- .../nettychat/common/zookeeper/testZk.java | 256 ---------- 45 files changed, 4 insertions(+), 3730 deletions(-) delete mode 100644 src/main/java/com/myself/nettychat/DefaultAutoService.java delete mode 100644 src/main/java/com/myself/nettychat/auto/InitServer.java delete mode 100644 src/main/java/com/myself/nettychat/auto/ServerAutoConfigure.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/AbstractBootstrapServer.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/BaseApi.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/BaseAuthService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/BootstrapServer.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/ChannelService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/NettyBootstrapServer.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/bean/MqttChannel.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/bean/RetainMessage.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/bean/SendMqttMessage.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/bean/SessionMessage.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/bean/WillMeaasge.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/AbstractChannelService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/ClientSessionService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/MqttChannelService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/MqttHandlerService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/PublishApiSevice.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/WillService.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/channel/cache/CacheMap.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/coder/ByteBufToWebSocketFrameEncoder.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/coder/WebSocketFrameToByteBufDecoder.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/handler/DefaultMqttHandler.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/scan/SacnScheduled.java delete mode 100644 src/main/java/com/myself/nettychat/bootstrap/scan/ScanRunnable.java delete mode 100644 src/main/java/com/myself/nettychat/common/enums/ConfirmStatus.java delete mode 100644 src/main/java/com/myself/nettychat/common/enums/ProtocolEnum.java delete mode 100644 src/main/java/com/myself/nettychat/common/enums/QosStatus.java delete mode 100644 src/main/java/com/myself/nettychat/common/enums/SessionStatus.java delete mode 100644 src/main/java/com/myself/nettychat/common/enums/SubStatus.java delete mode 100644 src/main/java/com/myself/nettychat/common/mqtts/ClientMqttHandlerService.java delete mode 100644 src/main/java/com/myself/nettychat/common/mqtts/MqttHander.java delete mode 100644 src/main/java/com/myself/nettychat/common/mqtts/MqttHandlerIntf.java delete mode 100644 src/main/java/com/myself/nettychat/common/mqtts/ServerMqttHandlerService.java delete mode 100644 src/main/java/com/myself/nettychat/common/ssl/SecureSocketKeyStore.java delete mode 100644 src/main/java/com/myself/nettychat/common/ssl/SecureSocketSslContextFactory.java delete mode 100644 src/main/java/com/myself/nettychat/common/ssl/SecureSokcetTrustManagerFactory.java delete mode 100644 src/main/java/com/myself/nettychat/common/ssl/StreamReader.java delete mode 100644 src/main/java/com/myself/nettychat/common/ssl/X509CertTool.java delete mode 100644 src/main/java/com/myself/nettychat/common/zookeeper/ZkStateListener.java delete mode 100644 src/main/java/com/myself/nettychat/common/zookeeper/ZkUtils.java delete mode 100644 src/main/java/com/myself/nettychat/common/zookeeper/testZk.java diff --git a/README.md b/README.md index 62beccd..dc603a8 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,6 @@ 本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 +## 提示 + +暂未初始化,请勿使用 \ No newline at end of file diff --git a/src/main/java/com/myself/nettychat/DefaultAutoService.java b/src/main/java/com/myself/nettychat/DefaultAutoService.java deleted file mode 100644 index e4b4cc7..0000000 --- a/src/main/java/com/myself/nettychat/DefaultAutoService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.myself.nettychat; - -import com.myself.nettychat.bootstrap.BaseAuthService; -import org.springframework.stereotype.Service; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 默认权限 - **/ -@Service -public class DefaultAutoService implements BaseAuthService { - - @Override - public boolean authorized(String username, String password) { - return true; - } -} diff --git a/src/main/java/com/myself/nettychat/auto/InitServer.java b/src/main/java/com/myself/nettychat/auto/InitServer.java deleted file mode 100644 index 3729fc3..0000000 --- a/src/main/java/com/myself/nettychat/auto/InitServer.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.myself.nettychat.auto; - - -import com.myself.nettychat.bootstrap.BootstrapServer; -import com.myself.nettychat.bootstrap.NettyBootstrapServer; -import com.myself.nettychat.common.properties.InitNetty; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 初始化服务 - **/ -public class InitServer { - - private InitNetty serverBean; - - public InitServer(InitNetty serverBean) { - this.serverBean = serverBean; - } - - BootstrapServer bootstrapServer; - - public void open(){ - if(serverBean!=null){ - bootstrapServer = new NettyBootstrapServer(); - bootstrapServer.setServerBean(serverBean); - bootstrapServer.start(); - } - } - - - public void close(){ - if(bootstrapServer!=null){ - bootstrapServer.shutdown(); - } - } - -} diff --git a/src/main/java/com/myself/nettychat/auto/ServerAutoConfigure.java b/src/main/java/com/myself/nettychat/auto/ServerAutoConfigure.java deleted file mode 100644 index 961e458..0000000 --- a/src/main/java/com/myself/nettychat/auto/ServerAutoConfigure.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.myself.nettychat.auto; - -import com.myself.nettychat.bootstrap.scan.SacnScheduled; -import com.myself.nettychat.bootstrap.scan.ScanRunnable; -import com.myself.nettychat.common.enums.ProtocolEnum; -import com.myself.nettychat.common.properties.InitNetty; -import org.apache.commons.lang3.ObjectUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 自动化配置初始化服务 - **/ -@Configuration -@ConditionalOnClass -@EnableConfigurationProperties({InitNetty.class}) -public class ServerAutoConfigure { - - private static final int _BLACKLOG = 1024; - - private static final int CPU =Runtime.getRuntime().availableProcessors(); - - private static final int SEDU_DAY =10; - - private static final int TIMEOUT =120; - - private static final int BUF_SIZE=10*1024*1024; - - - public ServerAutoConfigure(){ - - } - - @Bean - @ConditionalOnMissingBean(name = "sacnScheduled") - public ScanRunnable initRunable(@Autowired InitNetty serverBean){ - long time =(serverBean==null || serverBean.getPeriod()<5)?10:serverBean.getPeriod(); - ScanRunnable sacnScheduled = new SacnScheduled(time); - Thread scanRunnable = new Thread(sacnScheduled); - scanRunnable.setDaemon(true); - scanRunnable.start(); - return sacnScheduled; - } - - - @Bean(initMethod = "open", destroyMethod = "close") - @ConditionalOnMissingBean - public InitServer initServer(InitNetty serverBean){ - if(!ObjectUtils.allNotNull(serverBean.getMqttport(),serverBean.getServerName())){ - throw new NullPointerException("not set port"); - } - if(serverBean.getBacklog()<1){ - serverBean.setBacklog(_BLACKLOG); - } - if(serverBean.getBossThread()<1){ - serverBean.setBossThread(CPU); - } - if(serverBean.getInitalDelay()<0){ - serverBean.setInitalDelay(SEDU_DAY); - } - if(serverBean.getPeriod()<1){ - serverBean.setPeriod(SEDU_DAY); - } - if(serverBean.getHeart()<1){ - serverBean.setHeart(TIMEOUT); - } - if(serverBean.getRevbuf()<1){ - serverBean.setRevbuf(BUF_SIZE); - } - if(serverBean.getWorkerThread()<1){ - serverBean.setWorkerThread(CPU*2); - } - if(serverBean.getProtocol()==null){ - serverBean.setProtocol(ProtocolEnum.MQTT); - } - return new InitServer(serverBean); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/AbstractBootstrapServer.java b/src/main/java/com/myself/nettychat/bootstrap/AbstractBootstrapServer.java deleted file mode 100644 index a647848..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/AbstractBootstrapServer.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.myself.nettychat.bootstrap; - - -import com.myself.nettychat.bootstrap.coder.ByteBufToWebSocketFrameEncoder; -import com.myself.nettychat.bootstrap.coder.WebSocketFrameToByteBufDecoder; -import com.myself.nettychat.common.properties.InitNetty; -import com.myself.nettychat.common.ssl.SecureSocketSslContextFactory; -import com.myself.nettychat.common.utils.SpringBeanUtils; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.handler.codec.mqtt.MqttDecoder; -import io.netty.handler.codec.mqtt.MqttEncoder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.timeout.IdleStateHandler; -import org.apache.commons.lang3.ObjectUtils; -import org.jboss.netty.util.internal.SystemPropertyUtil; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import java.security.KeyStore; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 抽象类,负责加载edec handler - **/ -public abstract class AbstractBootstrapServer implements BootstrapServer { - - private String PROTOCOL = "TLS"; - - private SSLContext SERVER_CONTEXT; - - private static final String MQTT_CSV_LIST = "mqtt, mqttv3.1, mqttv3.1.1"; - - - /** - * - * @param channelPipeline channelPipeline - * @param serverBean 服务配置参数 - */ - protected void initHandler(ChannelPipeline channelPipeline, InitNetty serverBean){ - if(serverBean.isSsl()){ - if(!ObjectUtils.allNotNull(serverBean.getJksCertificatePassword(),serverBean.getJksFile(),serverBean.getJksStorePassword())){ - throw new NullPointerException("SSL file and password is null"); - } - initSsl(serverBean); - SSLEngine engine = - SERVER_CONTEXT.createSSLEngine(); - engine.setUseClientMode(false); - channelPipeline.addLast("ssl", new SslHandler(engine)); - } - - intProtocolHandler(channelPipeline,serverBean); - channelPipeline.addLast(new IdleStateHandler(serverBean.getHeart(),0,0)); - channelPipeline.addLast( SpringBeanUtils.getBean(serverBean.getMqttHander())); - - } - - private void intProtocolHandler(ChannelPipeline channelPipeline,InitNetty serverBean){ - switch (serverBean.getProtocol()){ - case MQTT: - channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); - channelPipeline.addLast("decoder", new MqttDecoder()); - break; - case MQTT_WS_MQTT: - channelPipeline.addLast("httpCode", new HttpServerCodec()); - channelPipeline.addLast("aggregator", new HttpObjectAggregator(65536)); - channelPipeline.addLast("webSocketHandler", - new WebSocketServerProtocolHandler("/", MQTT_CSV_LIST)); - channelPipeline.addLast("wsDecoder", new WebSocketFrameToByteBufDecoder()); - channelPipeline.addLast("wsEncoder", new ByteBufToWebSocketFrameEncoder()); - channelPipeline.addLast("decoder", new MqttDecoder()); - channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); - break; - case MQTT_WS_PAHO: - channelPipeline.addLast("httpCode", new HttpServerCodec()); - channelPipeline.addLast("aggregator", new HttpObjectAggregator(65536)); - channelPipeline.addLast("webSocketHandler", - new WebSocketServerProtocolHandler("/mqtt", MQTT_CSV_LIST)); - channelPipeline.addLast("wsDecoder", new WebSocketFrameToByteBufDecoder()); - channelPipeline.addLast("wsEncoder", new ByteBufToWebSocketFrameEncoder()); - channelPipeline.addLast("decoder", new MqttDecoder()); - channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); - break; - } - } - - private void initSsl(InitNetty serverBean){ - ExecutorService executorService = Executors.newCachedThreadPool(); - executorService.submit(() -> {}); - String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm"); - if (algorithm == null) { - algorithm = "SunX509"; - } - SSLContext serverContext; - try { - // - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load( SecureSocketSslContextFactory.class.getResourceAsStream(serverBean.getJksFile()), - serverBean.getJksStorePassword().toCharArray()); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); - kmf.init(ks,serverBean.getJksCertificatePassword().toCharArray()); - serverContext = SSLContext.getInstance(PROTOCOL); - serverContext.init(kmf.getKeyManagers(), null, null); - } catch (Exception e) { - throw new Error( - "Failed to initialize the server-side SSLContext", e); - } - SERVER_CONTEXT = serverContext; - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/BaseApi.java b/src/main/java/com/myself/nettychat/bootstrap/BaseApi.java deleted file mode 100644 index edd8720..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/BaseApi.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.myself.nettychat.bootstrap; - -import javax.validation.constraints.NotNull; -import java.util.Arrays; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Predicate; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 逻辑操作封装 - **/ -public interface BaseApi { - - default void doIfElse(T t, Predicate predicate, Consumer consumer){ - if(t!=null){ - if(predicate.test(t)){ - consumer.accept(t); - } - } - } - - - default void doIfElse(T t, Predicate predicate, Consumer consumer, Consumer consumer2){ - if(t!=null){ - if(predicate.test(t)){ - consumer.accept(t); - } - else{ - consumer2.accept(t); - } - } - } - default boolean doIf(T t, Predicate... predicates){ - if(t!=null){ - for(Predicate p:predicates){ - if(!p.test(t)){ - return false; - } - } - return true; - } - return false; - } - - default void doIfAnd(T t, Consumer consumer2, Predicate... predicates){ - boolean flag =true; - if(t!=null){ - for(Predicate p:predicates){ - if(!p.test(t)){ - flag= false; - break; - } - } - } - if(flag){ - consumer2.accept(t); - } - } - - default void doIfAnd1(@NotNull T t, @NotNull Consumer consumer2, @NotNull Predicate... predicates){ - Predicate one = predicates[0]; - int l; - if((l=predicates.length)>1){ - for(int i=1;i topics); - - void loginSuccess(Channel channel, String deviceId, MqttConnectMessage mqttConnectMessage); - - void publishSuccess(Channel channel, MqttPublishMessage mqttPublishMessage); - - void closeSuccess(String deviceId,boolean isDisconnect); - - void sendWillMsg(WillMeaasge willMeaasge); - - String getDeviceId(Channel channel); - - void unsubscribe(String deviceId, List topics1); - - void doPubrel(Channel channel, int mqttMessage); - - void doPubrec(Channel channel, int mqttMessage); - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/NettyBootstrapServer.java b/src/main/java/com/myself/nettychat/bootstrap/NettyBootstrapServer.java deleted file mode 100644 index 816eea7..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/NettyBootstrapServer.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.myself.nettychat.bootstrap; - -import com.myself.nettychat.common.ip.IpUtils; -import com.myself.nettychat.common.properties.InitNetty; -import com.myself.nettychat.common.utils.RemotingUtil; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.Epoll; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.epoll.EpollServerSocketChannel; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc mtqq netty启动服务类 - **/ -@Slf4j -@Data -public class NettyBootstrapServer extends AbstractBootstrapServer { - - private InitNetty serverBean; - - public InitNetty getServerBean() { - return serverBean; - } - - public void setServerBean(InitNetty serverBean) { - this.serverBean = serverBean; - } - - private EventLoopGroup bossGroup; - - private EventLoopGroup workGroup; - - ServerBootstrap bootstrap=null ;// 启动辅助类 - - /** - * 服务开启 - */ - public void start() { - initEventPool(); - bootstrap.group(bossGroup, workGroup) - .channel(useEpoll()?EpollServerSocketChannel.class:NioServerSocketChannel.class) - .option(ChannelOption.SO_REUSEADDR, serverBean.isReuseaddr()) - .option(ChannelOption.SO_BACKLOG, serverBean.getBacklog()) - .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) - .option(ChannelOption.SO_RCVBUF, serverBean.getRevbuf()) - .childHandler(new ChannelInitializer() { - protected void initChannel(SocketChannel ch) throws Exception { - initHandler(ch.pipeline(),serverBean); - } - }) - .childOption(ChannelOption.TCP_NODELAY, serverBean.isNodelay()) - .childOption(ChannelOption.SO_KEEPALIVE, serverBean.isKeepalive()) - .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); - bootstrap.bind(IpUtils.getHost(),serverBean.getMqttport()).addListener((ChannelFutureListener) channelFuture -> { - if (channelFuture.isSuccess()) - log.info("服务端启动成功【" + IpUtils.getHost() + ":" + serverBean.getMqttport() + "】"); - else - log.info("服务端启动失败【" + IpUtils.getHost() + ":" + serverBean.getMqttport() + "】"); - }); - } - /** - * 初始化EnentPool 参数 - */ - private void initEventPool(){ - bootstrap= new ServerBootstrap(); - if(useEpoll()){ - bossGroup = new EpollEventLoopGroup(serverBean.getBossThread(), new ThreadFactory() { - private AtomicInteger index = new AtomicInteger(0); - - public Thread newThread(Runnable r) { - return new Thread(r, "LINUX_BOSS_" + index.incrementAndGet()); - } - }); - workGroup = new EpollEventLoopGroup(serverBean.getWorkerThread(), new ThreadFactory() { - private AtomicInteger index = new AtomicInteger(0); - - public Thread newThread(Runnable r) { - return new Thread(r, "LINUX_WORK_" + index.incrementAndGet()); - } - }); - - } - else { - bossGroup = new NioEventLoopGroup(serverBean.getBossThread(), new ThreadFactory() { - private AtomicInteger index = new AtomicInteger(0); - - public Thread newThread(Runnable r) { - return new Thread(r, "BOSS_" + index.incrementAndGet()); - } - }); - workGroup = new NioEventLoopGroup(serverBean.getWorkerThread(), new ThreadFactory() { - private AtomicInteger index = new AtomicInteger(0); - - public Thread newThread(Runnable r) { - return new Thread(r, "WORK_" + index.incrementAndGet()); - } - }); - } - } - - /** - * 关闭资源 - */ - public void shutdown() { - if(workGroup!=null && bossGroup!=null ){ - try { - bossGroup.shutdownGracefully().sync();// 优雅关闭 - workGroup.shutdownGracefully().sync(); - } catch (InterruptedException e) { - log.info("服务端关闭资源失败【" + IpUtils.getHost() + ":" + serverBean.getMqttport() + "】"); - } - } - } - - private boolean useEpoll() { - return RemotingUtil.isLinuxPlatform() - && Epoll.isAvailable(); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/bean/MqttChannel.java b/src/main/java/com/myself/nettychat/bootstrap/bean/MqttChannel.java deleted file mode 100644 index 9c830ce..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/bean/MqttChannel.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.myself.nettychat.bootstrap.bean; - -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; -import lombok.Builder; -import lombok.Data; - -import com.myself.nettychat.common.enums.SubStatus; -import com.myself.nettychat.common.enums.SessionStatus; - -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc channel 封装类 - **/ -@Builder -@Data -public class MqttChannel { - - private transient volatile Channel channel; - - - private String deviceId; - - - private boolean isWill; - - - private volatile SubStatus subStatus; // 是否订阅过主题 - - - private Set topic ; - - - - private volatile SessionStatus sessionStatus; // 在线 - 离线 - - - - private volatile boolean cleanSession; // 当为 true 时 channel close 时 从缓存中删除 此channel - - - - - private ConcurrentHashMap message ; // messageId - message(qos1) // 待确认消息 - - - private Set receive; - - public void addRecevice(int messageId){ - receive.add(messageId); - } - - public boolean checkRecevice(int messageId){ - return receive.contains(messageId); - } - - public boolean removeRecevice(int messageId){ - return receive.remove(messageId); - } - - - public void addSendMqttMessage(int messageId,SendMqttMessage msg){ - message.put(messageId,msg); - } - - - public SendMqttMessage getSendMqttMessage(int messageId){ - return message.get(messageId); - } - - - public void removeSendMqttMessage(int messageId){ - message.remove(messageId); - } - - - /** - * 判断当前channel 是否登录过 - * @return - */ - public boolean isLogin(){ - return Optional.ofNullable(this.channel).map(channel1 -> { - AttributeKey _login = AttributeKey.valueOf("login"); - return channel1.isActive() && channel1.hasAttr(_login); - }).orElse(false); - } - - /** - * 非正常关闭 - */ - public void close(){ - Optional.ofNullable(this.channel).ifPresent(channel1 -> channel1.close()); - } - - /** - * 通道是否活跃 - * @return - */ - public boolean isActive(){ - return channel!=null&&this.channel.isActive(); - } - - - - public boolean addTopic(Set topics){ - return topic.addAll(topics); - } - - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/bean/RetainMessage.java b/src/main/java/com/myself/nettychat/bootstrap/bean/RetainMessage.java deleted file mode 100644 index 814696a..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/bean/RetainMessage.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.myself.nettychat.bootstrap.bean; - -import io.netty.handler.codec.mqtt.MqttQoS; -import lombok.Builder; -import lombok.Data; - -@Builder -@Data -public class RetainMessage { - - private byte[] byteBuf; - - private MqttQoS qoS; - public String getString(){ - return new String(byteBuf); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/bean/SendMqttMessage.java b/src/main/java/com/myself/nettychat/bootstrap/bean/SendMqttMessage.java deleted file mode 100644 index ac5faef..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/bean/SendMqttMessage.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.myself.nettychat.bootstrap.bean; - - -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.MqttQoS; -import lombok.Builder; -import lombok.Data; - -import com.myself.nettychat.common.enums.ConfirmStatus; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc mqtts 消息 - **/ -@Builder -@Data -public class SendMqttMessage { - - - private int messageId; - - private Channel channel; - - private volatile ConfirmStatus confirmStatus; - - private long time; - - private byte[] byteBuf; - - private boolean isRetain; - - private MqttQoS qos; - - private String topic; - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/bean/SessionMessage.java b/src/main/java/com/myself/nettychat/bootstrap/bean/SessionMessage.java deleted file mode 100644 index 2613cd6..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/bean/SessionMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.myself.nettychat.bootstrap.bean; - - -import io.netty.handler.codec.mqtt.MqttQoS; -import lombok.Builder; -import lombok.Data; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc Session会话数据保存 - **/ -@Builder -@Data -public class SessionMessage { - - private byte[] byteBuf; - - private MqttQoS qoS; - - private String topic; - - - public String getString(){ - return new String(byteBuf); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/bean/WillMeaasge.java b/src/main/java/com/myself/nettychat/bootstrap/bean/WillMeaasge.java deleted file mode 100644 index 4022d28..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/bean/WillMeaasge.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.myself.nettychat.bootstrap.bean; - -import lombok.Builder; -import lombok.Data; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 遗嘱消息 - **/ -@Builder -@Data -public class WillMeaasge { - - private String willTopic; - - private String willMessage; - - private boolean isRetain; - - private int qos; - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/AbstractChannelService.java b/src/main/java/com/myself/nettychat/bootstrap/channel/AbstractChannelService.java deleted file mode 100644 index c0e51f2..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/AbstractChannelService.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.BaseApi; -import com.myself.nettychat.bootstrap.ChannelService; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.myself.nettychat.bootstrap.bean.MqttChannel; -import com.myself.nettychat.bootstrap.bean.RetainMessage; -import com.myself.nettychat.bootstrap.channel.cache.CacheMap; -import com.myself.nettychat.bootstrap.scan.ScanRunnable; -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -import java.util.Collection; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 抽象类 - **/ -@Slf4j -public abstract class AbstractChannelService extends PublishApiSevice implements ChannelService , BaseApi { - - protected AttributeKey _login = AttributeKey.valueOf("login"); - - protected AttributeKey _deviceId = AttributeKey.valueOf("deviceId"); - - protected static char SPLITOR = '/'; - - protected ExecutorService executorService =Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2); - - - protected static CacheMap cacheMap= new CacheMap<>(); - - - protected static ConcurrentHashMap mqttChannels = new ConcurrentHashMap<>(); // deviceId - mqChannel 登录 - - - protected static ConcurrentHashMap> retain = new ConcurrentHashMap<>(); // topic - 保留消息 - - - - protected static Cache> mqttChannelCache = CacheBuilder.newBuilder().maximumSize(100).build(); - - public AbstractChannelService(ScanRunnable scanRunnable) { - super(scanRunnable); - } - - - protected Collection getChannels(String topic,TopicFilter topicFilter){ - try { - return mqttChannelCache.get(topic, () -> topicFilter.filter(topic)); - } catch (Exception e) { - log.info(String.format("guava cache key topic【%s】 channel value== null ",topic)); - } - return null; - } - - - @FunctionalInterface - interface TopicFilter{ - Collection filter(String topic); - } - - protected boolean deleteChannel(String topic,MqttChannel mqttChannel){ - return Optional.ofNullable(topic).map(s -> { - mqttChannelCache.invalidate(s); - return cacheMap.delete(getTopic(s),mqttChannel); - }).orElse(false); - } - - protected boolean addChannel(String topic,MqttChannel mqttChannel) - { - return Optional.ofNullable(topic).map(s -> { - mqttChannelCache.invalidate(s); - return cacheMap.putData(getTopic(s),mqttChannel); - }).orElse(false); - } - - /** - * 获取channel - */ - public MqttChannel getMqttChannel(String deviceId){ - return Optional.ofNullable(deviceId).map(s -> mqttChannels.get(s)) - .orElse(null); - - } - - /** - * 获取channelId - */ - public String getDeviceId(Channel channel){ - return Optional.ofNullable(channel).map( channel1->channel1.attr(_deviceId).get()) - .orElse(null); - } - - - - protected String[] getTopic(String topic) { - return Optional.ofNullable(topic).map(s -> - StringUtils.split(topic,SPLITOR) - ).orElse(null); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/ClientSessionService.java b/src/main/java/com/myself/nettychat/bootstrap/channel/ClientSessionService.java deleted file mode 100644 index 9a23c8b..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/ClientSessionService.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.bean.SessionMessage; -import org.springframework.stereotype.Service; - -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 会话保留处理 - **/ -@Service -public class ClientSessionService { - - private static ConcurrentHashMap> queueSession = new ConcurrentHashMap<>(); // 连接关闭后 保留此session 数据 deviceId - - - public void saveSessionMsg(String deviceId, SessionMessage sessionMessage) { - ConcurrentLinkedQueue sessionMessages = queueSession.getOrDefault(deviceId, new ConcurrentLinkedQueue<>()); - boolean flag; - do{ - flag = sessionMessages.add(sessionMessage); - } - while (!flag); - queueSession.put(deviceId,sessionMessages); - } - - public ConcurrentLinkedQueue getByteBuf(String deviceId){ - return Optional.ofNullable(deviceId).map(s -> queueSession.get(s)) - .orElse(null); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/MqttChannelService.java b/src/main/java/com/myself/nettychat/bootstrap/channel/MqttChannelService.java deleted file mode 100644 index fa5ec21..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/MqttChannelService.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.bean.*; -import com.myself.nettychat.bootstrap.scan.ScanRunnable; -import com.myself.nettychat.common.enums.ConfirmStatus; -import com.myself.nettychat.common.enums.SessionStatus; -import com.myself.nettychat.common.enums.SubStatus; -import com.myself.nettychat.common.exception.ConnectionException; -import com.myself.nettychat.common.utils.ByteBufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArraySet; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc Channel事件处理service - **/ -@Slf4j -@Component -public class MqttChannelService extends AbstractChannelService { - - @Autowired - private ClientSessionService clientSessionService; - - @Autowired - private WillService willService; - - private final ScanRunnable scanRunnable; - - public MqttChannelService(ScanRunnable scanRunnable) { - super(scanRunnable); - this.scanRunnable = scanRunnable; - } - - - /** - * 取消订阅 - */ - @Override - public void unsubscribe(String deviceId, List topics1) { - Optional.ofNullable(mqttChannels.get(deviceId)).ifPresent(mqttChannel -> { - topics1.forEach(topic -> { - deleteChannel(topic,mqttChannel); - }); - }); - } - - /** - * 登录成功后 回复 - */ - private void replyLogin(Channel channel, MqttConnectMessage mqttConnectMessage) { - MqttFixedHeader mqttFixedHeader1 = mqttConnectMessage.fixedHeader(); - MqttConnectVariableHeader mqttConnectVariableHeader = mqttConnectMessage.variableHeader(); - final MqttConnectPayload payload = mqttConnectMessage.payload(); - String deviceId = getDeviceId(channel); - MqttChannel build = MqttChannel.builder().channel(channel).cleanSession(mqttConnectVariableHeader.isCleanSession()) - .deviceId(payload.clientIdentifier()) - .sessionStatus(SessionStatus.OPEN) - .isWill(mqttConnectVariableHeader.isWillFlag()) - .subStatus(SubStatus.NO) - .topic(new CopyOnWriteArraySet<>()) - .message(new ConcurrentHashMap<>()) - .receive(new CopyOnWriteArraySet<>()) - .build(); - if (connectSuccess(deviceId, build)) { // 初始化存储mqttchannel - if (mqttConnectVariableHeader.isWillFlag()) { // 遗嘱消息标志 - boolean b = doIf(mqttConnectVariableHeader, mqttConnectVariableHeader1 -> (payload.willMessage() != null) - , mqttConnectVariableHeader1 -> (payload.willTopic() != null)); - if (!b) { - throw new ConnectionException("will message and will topic is not null"); - } - // 处理遗嘱消息 - final WillMeaasge buildWill = WillMeaasge.builder(). - qos(mqttConnectVariableHeader.willQos()) - .willMessage(deviceId) - .willTopic(payload.willTopic()) - .isRetain(mqttConnectVariableHeader.isWillRetain()) - .build(); - willService.save(payload.clientIdentifier(), buildWill); - } else { - willService.del(payload.clientIdentifier()); - boolean b = doIf(mqttConnectVariableHeader, mqttConnectVariableHeader1 -> (!mqttConnectVariableHeader1.isWillRetain()), - mqttConnectVariableHeader1 -> (mqttConnectVariableHeader1.willQos() == 0)); - if (!b) { - throw new ConnectionException("will retain should be null and will QOS equal 0"); - } - } - doIfElse(mqttConnectVariableHeader, mqttConnectVariableHeader1 -> (mqttConnectVariableHeader1.isCleanSession()), mqttConnectVariableHeader1 -> { - MqttConnectReturnCode connectReturnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; - MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(connectReturnCode, false); - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader( - MqttMessageType.CONNACK, mqttFixedHeader1.isDup(), MqttQoS.AT_MOST_ONCE, mqttFixedHeader1.isRetain(), 0x02); - MqttConnAckMessage connAck = new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); - channel.writeAndFlush(connAck);// 清理会话 - }, mqttConnectVariableHeader1 -> { - MqttConnectReturnCode connectReturnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; - MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(connectReturnCode, true); - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader( - MqttMessageType.CONNACK, mqttFixedHeader1.isDup(), MqttQoS.AT_MOST_ONCE, mqttFixedHeader1.isRetain(), 0x02); - MqttConnAckMessage connAck = new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); - channel.writeAndFlush(connAck);// 非清理会话 - - }); //发送 session 数据 - ConcurrentLinkedQueue sessionMessages = clientSessionService.getByteBuf(payload.clientIdentifier()); - doIfElse(sessionMessages, messages -> messages != null && !messages.isEmpty(), byteBufs -> { - SessionMessage sessionMessage; - while ((sessionMessage = byteBufs.poll()) != null) { - switch (sessionMessage.getQoS()) { - case EXACTLY_ONCE: - sendQosConfirmMsg(MqttQoS.EXACTLY_ONCE,getMqttChannel(deviceId), sessionMessage.getTopic(), sessionMessage.getByteBuf()); - break; - case AT_MOST_ONCE: - sendQos0Msg(channel, sessionMessage.getTopic(), sessionMessage.getByteBuf()); - break; - case AT_LEAST_ONCE: - sendQosConfirmMsg(MqttQoS.AT_LEAST_ONCE,getMqttChannel(deviceId), sessionMessage.getTopic(), sessionMessage.getByteBuf()); - break; - } - } - - }); - } - } - - - - /** - * qos2 第二步 - */ - @Override - public void doPubrel(Channel channel, int messageId) { - MqttChannel mqttChannel = getMqttChannel(getDeviceId(channel)); - doIfElse(mqttChannel,mqttChannel1 ->mqttChannel1.isLogin(),mqttChannel1 -> { - mqttChannel1.removeRecevice(messageId); - sendToPubComp(channel,messageId); - }); - } - - - - /** - * qos2 第三步 - */ - @Override - public void doPubrec(Channel channel, int mqttMessage) { - sendPubRel(channel,false,mqttMessage); - } - - /** - * 连接成功后 - * @param deviceId - * @param build - */ - @Override - public boolean connectSuccess(String deviceId, MqttChannel build) { - return Optional.ofNullable(mqttChannels.get(deviceId)) - .map(mqttChannel -> { - switch (mqttChannel.getSessionStatus()){ - case OPEN: - return false; - case CLOSE: - switch (mqttChannel.getSubStatus()){ - case YES: // 清除订阅 topic - deleteSubTopic(mqttChannel).stream() - .forEach(s -> cacheMap.putData(getTopic(s),build)); - break; - } - } - mqttChannels.put(deviceId,build); - return true; - }).orElseGet(() -> { - mqttChannels.put(deviceId,build); - return true; - }); - } - - - /** - * 订阅成功后 (发送保留消息) - */ - public void suscribeSuccess(String deviceId, Set topics){ - doIfElse(topics,topics1->!CollectionUtils.isEmpty(topics1),strings -> { - MqttChannel mqttChannel = mqttChannels.get(deviceId); - mqttChannel.setSubStatus(SubStatus.YES); // 设置订阅主题标识 - mqttChannel.addTopic(strings); - executorService.execute(() -> { - Optional.ofNullable(mqttChannel).ifPresent(mqttChannel1 -> { - if(mqttChannel1.isLogin()){ - strings.parallelStream().forEach(topic -> { - addChannel(topic,mqttChannel); - sendRetain(topic,mqttChannel); // 发送保留消息 - }); - } - }); - }); - }); - } - - - /** - *成功登陆 (发送会话消息) - * @param channel - * @param deviceId - * @param mqttConnectMessage - */ - @Override - public void loginSuccess(Channel channel, String deviceId, MqttConnectMessage mqttConnectMessage) { - channel.attr(_login).set(true); - channel.attr(_deviceId).set(deviceId); - replyLogin(channel, mqttConnectMessage); - } - - - /** - * 发布消息成功 () - * @param channel - * @param mqttPublishMessage - */ - @Override - public void publishSuccess(Channel channel, MqttPublishMessage mqttPublishMessage) { - MqttFixedHeader mqttFixedHeader = mqttPublishMessage.fixedHeader(); - MqttPublishVariableHeader mqttPublishVariableHeader = mqttPublishMessage.variableHeader(); - MqttChannel mqttChannel = getMqttChannel(getDeviceId(channel)); - ByteBuf payload = mqttPublishMessage.payload(); - byte[] bytes = ByteBufUtil.copyByteBuf(payload); // - int messageId = mqttPublishVariableHeader.messageId(); - executorService.execute(() -> { - if (channel.hasAttr(_login) && mqttChannel != null) { - boolean isRetain; - switch (mqttFixedHeader.qosLevel()) { - case AT_MOST_ONCE: // 至多一次 - break; - case AT_LEAST_ONCE: - sendPubBack(channel, messageId); - break; - case EXACTLY_ONCE: - sendPubRec(mqttChannel, messageId); - break; - } - if ((isRetain=mqttFixedHeader.isRetain()) && mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE) { //是保留消息 qos >0 - saveRetain(mqttPublishVariableHeader.topicName(), - RetainMessage.builder() - .byteBuf(bytes) - .qoS(mqttFixedHeader.qosLevel()) - .build(), false); - } else if (mqttFixedHeader.isRetain() && mqttFixedHeader.qosLevel() == MqttQoS.AT_MOST_ONCE) { // 是保留消息 qos=0 清除之前保留消息 保留现在 - saveRetain(mqttPublishVariableHeader.topicName(), - RetainMessage.builder() - .byteBuf(bytes) - .qoS(mqttFixedHeader.qosLevel()) - .build(), true); - } - if (!mqttChannel.checkRecevice(messageId)) { - push(mqttPublishVariableHeader.topicName(), mqttFixedHeader.qosLevel(), bytes,isRetain); - mqttChannel.addRecevice(messageId); - } - } - }); - - } - /** - * 推送消息给订阅者 - */ - private void push(String topic, MqttQoS qos, byte[] bytes, boolean isRetain){ - Collection subChannels = getChannels(topic, topic1 -> cacheMap.getData(getTopic(topic1))); - if(!CollectionUtils.isEmpty(subChannels)){ - subChannels.parallelStream().forEach(subChannel -> { - switch (subChannel.getSessionStatus()){ - case OPEN: // 在线 - if(subChannel.isActive()){ // 防止channel失效 但是离线状态没更改 - switch (qos){ - case AT_LEAST_ONCE: - sendQosConfirmMsg(MqttQoS.AT_LEAST_ONCE,subChannel,topic,bytes); - break; - case AT_MOST_ONCE: - sendQos0Msg(subChannel.getChannel(),topic,bytes); - break; - case EXACTLY_ONCE: - sendQosConfirmMsg(MqttQoS.EXACTLY_ONCE,subChannel,topic,bytes); - break; - } - } - else{ - if(!subChannel.isCleanSession() & !isRetain){ - clientSessionService.saveSessionMsg(subChannel.getDeviceId(), - SessionMessage.builder().byteBuf(bytes).qoS(qos).topic(topic).build() ); - break; - } - } - break; - case CLOSE: // 连接 设置了 clean session =false - clientSessionService.saveSessionMsg(subChannel.getDeviceId(), - SessionMessage.builder().byteBuf(bytes).qoS(qos).topic(topic).build() ); - break; - } - }); - } - } - - /** - * 关闭channel 操作 - * @param deviceId - */ - @Override - public void closeSuccess(String deviceId,boolean isDisconnect) { - if(StringUtils.isNotBlank(deviceId)){ - executorService.execute(() -> { - MqttChannel mqttChannel = mqttChannels.get(deviceId); - Optional.ofNullable(mqttChannel).ifPresent(mqttChannel1 -> { - mqttChannel1.setSessionStatus(SessionStatus.CLOSE); // 设置关闭 - mqttChannel1.close(); // 关闭channel - mqttChannel1.setChannel(null); - if(!mqttChannel1.isCleanSession()){ // 保持会话 - // 处理 qos1 未确认数据 - ConcurrentHashMap message = mqttChannel1.getMessage(); - Optional.ofNullable(message).ifPresent(integerConfirmMessageConcurrentHashMap -> { - integerConfirmMessageConcurrentHashMap.forEach((integer, confirmMessage) -> doIfElse(confirmMessage, sendMqttMessage ->sendMqttMessage.getConfirmStatus()== ConfirmStatus.PUB, sendMqttMessage ->{ - clientSessionService.saveSessionMsg(mqttChannel.getDeviceId(), SessionMessage.builder() - .byteBuf(sendMqttMessage.getByteBuf()) - .qoS(sendMqttMessage.getQos()) - .topic(sendMqttMessage.getTopic()) - .build()); // 把待确认数据转入session中 - } - )); - - }); - } - else{ // 删除sub topic-消息 - mqttChannels.remove(deviceId); // 移除channelId 不保持会话 直接删除 保持会话 旧的在重新connect时替换 - switch (mqttChannel1.getSubStatus()){ - case YES: - deleteSubTopic(mqttChannel1); - break; - } - } - if(mqttChannel1.isWill()){ // 发送遗言 - if(!isDisconnect){ // 不是disconnection操作 - willService.doSend(deviceId); - } - } - }); - }); - } - } - - /** - * 清除channel 订阅主题 - * @param mqttChannel - */ - public Set deleteSubTopic(MqttChannel mqttChannel){ - Set topics = mqttChannel.getTopic(); - topics.parallelStream().forEach(topic -> cacheMap.delete(getTopic(topic),mqttChannel)); - return topics; - } - - /** - * 发送 遗嘱消息(有的channel 已经关闭 但是保持了 session 此时加入session 数据中 ) - * @param willMeaasge 遗嘱消息 - */ - public void sendWillMsg(WillMeaasge willMeaasge){ - Collection mqttChannels = getChannels(willMeaasge.getWillTopic(), topic -> cacheMap.getData(getTopic(topic))); - if(!CollectionUtils.isEmpty(mqttChannels)){ - mqttChannels.forEach(mqttChannel -> { - switch (mqttChannel.getSessionStatus()){ - case CLOSE: - clientSessionService.saveSessionMsg(mqttChannel.getDeviceId(), - SessionMessage.builder() - .topic(willMeaasge.getWillTopic()) - .qoS(MqttQoS.valueOf(willMeaasge.getQos())) - .byteBuf(willMeaasge.getWillMessage().getBytes()).build()); - break; - case OPEN: - writeWillMsg(mqttChannel,willMeaasge); - break; - } - }); - } - } - - /** - * 保存保留消息 - * @param topic 主题 - * @param retainMessage 信息 - */ - private void saveRetain(String topic, RetainMessage retainMessage, boolean isClean){ - ConcurrentLinkedQueue retainMessages = retain.getOrDefault(topic, new ConcurrentLinkedQueue<>()); - if(!retainMessages.isEmpty() && isClean){ - retainMessages.clear(); - } - boolean flag; - do{ - flag = retainMessages.add(retainMessage); - } - while (!flag); - retain.put(topic, retainMessages); - } - - /** - * 发送保留消息 - */ - public void sendRetain(String topic,MqttChannel mqttChannel){ - retain.forEach((_topic, retainMessages) -> { - if(StringUtils.startsWith(_topic,topic)){ - Optional.ofNullable(retainMessages).ifPresent(pubMessages1 -> { - retainMessages.parallelStream().forEach(retainMessage -> { - log.info("【发送保留消息】"+mqttChannel.getChannel().remoteAddress()+":"+retainMessage.getString()+"【成功】"); - switch (retainMessage.getQoS()){ - case AT_MOST_ONCE: - sendQos0Msg(mqttChannel.getChannel(),_topic,retainMessage.getByteBuf()); - break; - case AT_LEAST_ONCE: - sendQosConfirmMsg(MqttQoS.AT_LEAST_ONCE,mqttChannel,_topic,retainMessage.getByteBuf()); - break; - case EXACTLY_ONCE: - sendQosConfirmMsg(MqttQoS.EXACTLY_ONCE,mqttChannel,_topic,retainMessage.getByteBuf()); - break; - } - }); - }); - } - }); - - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/MqttHandlerService.java b/src/main/java/com/myself/nettychat/bootstrap/channel/MqttHandlerService.java deleted file mode 100644 index abc4d56..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/MqttHandlerService.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.BaseApi; -import com.myself.nettychat.bootstrap.BaseAuthService; -import com.myself.nettychat.bootstrap.ChannelService; -import com.myself.nettychat.bootstrap.bean.SendMqttMessage; -import com.myself.nettychat.common.enums.ConfirmStatus; -import com.myself.nettychat.common.mqtts.ServerMqttHandlerService; -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import io.netty.handler.timeout.IdleStateEvent; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -@Slf4j -@Component -public class MqttHandlerService extends ServerMqttHandlerService implements BaseApi { - - @Autowired - ChannelService mqttChannelService; - - private final BaseAuthService baseAuthService; - - public MqttHandlerService(BaseAuthService baseAuthService) { - this.baseAuthService = baseAuthService; - } - - /** - * 登录 - * - */ - @Override - public boolean login(Channel channel, MqttConnectMessage mqttConnectMessage) { -// 校验规则 自定义校验规则 - MqttConnectPayload payload = mqttConnectMessage.payload(); - String deviceId = payload.clientIdentifier(); - if (StringUtils.isBlank(deviceId)) { - MqttConnectReturnCode connectReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; - connectBack(channel,connectReturnCode); - return false; - } - - if(mqttConnectMessage.variableHeader().hasPassword() && mqttConnectMessage.variableHeader().hasUserName() - && !baseAuthService.authorized(payload.userName(),payload.password())){ - MqttConnectReturnCode connectReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; - connectBack(channel,connectReturnCode); - return false; - } - return Optional.ofNullable(mqttChannelService.getMqttChannel(deviceId)) - .map(mqttChannel -> { - switch (mqttChannel.getSessionStatus()){ - case OPEN: - return false; - } - mqttChannelService.loginSuccess(channel, deviceId, mqttConnectMessage); - return true; - }).orElseGet(() -> { - mqttChannelService.loginSuccess(channel, deviceId, mqttConnectMessage); - return true; - }); - - } - - private void connectBack(Channel channel, MqttConnectReturnCode connectReturnCode){ - MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(connectReturnCode, true); - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader( - MqttMessageType.CONNACK,false, MqttQoS.AT_MOST_ONCE, false, 0x02); - MqttConnAckMessage connAck = new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); - channel.writeAndFlush(connAck); - } - - - /** - * 发布 - */ - @Override - public void publish(Channel channel, MqttPublishMessage mqttPublishMessage) { - mqttChannelService.publishSuccess(channel, mqttPublishMessage); - } - - /** - * 订阅 - */ - @Override - public void subscribe(Channel channel, MqttSubscribeMessage mqttSubscribeMessage) { - Set topics = mqttSubscribeMessage.payload().topicSubscriptions().stream().map(mqttTopicSubscription -> - mqttTopicSubscription.topicName() - ).collect(Collectors.toSet()); - mqttChannelService.suscribeSuccess(mqttChannelService.getDeviceId(channel), topics); - subBack(channel, mqttSubscribeMessage, topics.size()); - } - - private void subBack(Channel channel, MqttSubscribeMessage mqttSubscribeMessage, int num) { - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(mqttSubscribeMessage.variableHeader().messageId()); - List grantedQoSLevels = new ArrayList<>(num); - for (int i = 0; i < num; i++) { - grantedQoSLevels.add(mqttSubscribeMessage.payload().topicSubscriptions().get(i).qualityOfService().value()); - } - MqttSubAckPayload payload = new MqttSubAckPayload(grantedQoSLevels); - MqttSubAckMessage mqttSubAckMessage = new MqttSubAckMessage(mqttFixedHeader, variableHeader, payload); - channel.writeAndFlush(mqttSubAckMessage); - } - - - /** - * 关闭通道 - */ - @Override - public void close(Channel channel) { - mqttChannelService.closeSuccess(mqttChannelService.getDeviceId(channel), false); - channel.close(); - } - - /** - * 回复pong消息 - */ - @Override - public void pong(Channel channel) { - if (channel.isOpen() && channel.isActive() && channel.isWritable()) { - log.info("收到来自:【" + channel.remoteAddress().toString() + "】心跳"); - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PINGRESP, false, MqttQoS.AT_MOST_ONCE, false, 0); - channel.writeAndFlush(new MqttMessage(fixedHeader)); - } - } - - /** - * 取消订阅 - */ - @Override - public void unsubscribe(Channel channel, MqttUnsubscribeMessage mqttMessage) { - List topics1 = mqttMessage.payload().topics(); - mqttChannelService.unsubscribe(mqttChannelService.getDeviceId(channel), topics1); - unSubBack(channel, mqttMessage.variableHeader().messageId()); - } - - /** - * 回复取消订阅 - */ - private void unSubBack(Channel channel, int messageId) { - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0x02); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId); - MqttUnsubAckMessage mqttUnsubAckMessage = new MqttUnsubAckMessage(mqttFixedHeader, variableHeader); - channel.writeAndFlush(mqttUnsubAckMessage); - } - - - /** - * 消息回复确认(qos1 级别 保证收到消息 但是可能会重复) - */ - @Override - public void puback(Channel channel, MqttMessage mqttMessage) { - MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); - int messageId = messageIdVariableHeader.messageId(); - mqttChannelService.getMqttChannel(mqttChannelService.getDeviceId(channel)).getSendMqttMessage(messageId).setConfirmStatus(ConfirmStatus.COMPLETE); // 复制为空 - } - - - /** - * disconnect 主动断线 - */ - @Override - public void disconnect(Channel channel) { - mqttChannelService.closeSuccess(mqttChannelService.getDeviceId(channel), true); - } - - - /** - * qos2 发布收到 - */ - @Override - public void pubrec(Channel channel, MqttMessage mqttMessage ) { - MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); - int messageId = messageIdVariableHeader.messageId(); - mqttChannelService.getMqttChannel(mqttChannelService.getDeviceId(channel)).getSendMqttMessage(messageId).setConfirmStatus(ConfirmStatus.PUBREL); // 复制为空 - mqttChannelService.doPubrec(channel, messageId); - } - - /** - * qos2 发布释放 - */ - @Override - public void pubrel(Channel channel, MqttMessage mqttMessage ) { - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); - int messageId = mqttMessageIdVariableHeader.messageId(); - mqttChannelService.getMqttChannel(mqttChannelService.getDeviceId(channel)).getSendMqttMessage(messageId).setConfirmStatus(ConfirmStatus.COMPLETE); // 复制为空 - mqttChannelService.doPubrel(channel, messageId); - - } - - /** - * qos2 发布完成 - */ - @Override - public void pubcomp(Channel channel, MqttMessage mqttMessage ) { - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); - int messageId = mqttMessageIdVariableHeader.messageId(); - SendMqttMessage sendMqttMessage = mqttChannelService.getMqttChannel(mqttChannelService.getDeviceId(channel)).getSendMqttMessage(messageId); - sendMqttMessage.setConfirmStatus(ConfirmStatus.COMPLETE); // 复制为空 - } - - @Override - public void doTimeOut(Channel channel, IdleStateEvent evt) { - log.info("【PingPongService:doTimeOut 心跳超时】" + channel.remoteAddress() + "【channel 关闭】"); - switch (evt.state()) { - case READER_IDLE: - close(channel); - case WRITER_IDLE: - close(channel); - case ALL_IDLE: - close(channel); - } - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/PublishApiSevice.java b/src/main/java/com/myself/nettychat/bootstrap/channel/PublishApiSevice.java deleted file mode 100644 index 4af733f..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/PublishApiSevice.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.bean.MqttChannel; -import com.myself.nettychat.bootstrap.bean.SendMqttMessage; -import com.myself.nettychat.bootstrap.bean.WillMeaasge; -import com.myself.nettychat.bootstrap.scan.ScanRunnable; -import com.myself.nettychat.common.utils.MessageId; -import com.myself.nettychat.common.enums.ConfirmStatus; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import lombok.extern.slf4j.Slf4j; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 发送消息以及确认 - **/ -@Slf4j -public class PublishApiSevice { - - private final ScanRunnable scanRunnable; - - public PublishApiSevice(ScanRunnable scanRunnable) { - this.scanRunnable = scanRunnable; - } - - - /** - * 写入遗嘱消息 - */ - protected void writeWillMsg(MqttChannel mqttChannel, WillMeaasge willMeaasge) { -// dup保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等 - switch (willMeaasge.getQos()){ - case 0: // qos0 - sendQos0Msg(mqttChannel.getChannel(),willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); - break; - case 1: // qos1 - sendQosConfirmMsg(MqttQoS.AT_LEAST_ONCE,mqttChannel,willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); - break; - case 2: // qos2 - sendQosConfirmMsg(MqttQoS.EXACTLY_ONCE,mqttChannel,willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); - break; - } - - - } - - protected void sendQosConfirmMsg(MqttQoS qos, MqttChannel mqttChannel, String topic, byte[] bytes) { - if(mqttChannel.isLogin()){ - int messageId = MessageId.messageId(); - switch (qos){ - case AT_LEAST_ONCE: - mqttChannel.addSendMqttMessage(messageId,sendQos1Msg(mqttChannel.getChannel(),topic,false,bytes,messageId)); - break; - case EXACTLY_ONCE: - mqttChannel.addSendMqttMessage(messageId,sendQos2Msg(mqttChannel.getChannel(),topic,false,bytes,messageId)); - break; - } - } - - } - - - /** - * 发送 qos1 类的消息 - */ - private SendMqttMessage sendQos1Msg(Channel channel, String topic, boolean isDup, byte[] byteBuf, int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,isDup, MqttQoS.AT_LEAST_ONCE,false,0); - MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); - MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(byteBuf)); - channel.writeAndFlush(mqttPublishMessage); - return addQueue(channel,messageId,topic,byteBuf,MqttQoS.AT_LEAST_ONCE, ConfirmStatus.PUB); - } - - - - /** - * 发送 qos0 类的消息 byte - */ - protected void sendQos0Msg(Channel channel, String topic, byte[] byteBuf){ - if(channel!=null){ - sendQos0Msg(channel,topic,byteBuf,0); - } - } - private void sendQos0Msg(Channel channel, String topic, byte[] byteBuf,int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,false, MqttQoS.AT_MOST_ONCE,false,0); - MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); - MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader,Unpooled.wrappedBuffer(byteBuf)); - channel.writeAndFlush(mqttPublishMessage); - } - - - - - private SendMqttMessage sendQos2Msg(Channel channel, String topic,boolean isDup, byte[] byteBuf, int messageId) { - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,isDup, MqttQoS.EXACTLY_ONCE,false,0); - MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); - MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(byteBuf)); - channel.writeAndFlush(mqttPublishMessage); - return addQueue(channel,messageId,topic,byteBuf,MqttQoS.EXACTLY_ONCE,ConfirmStatus.PUB); - } - - - /** - * 发送qos1 publish 确认消息 - */ - protected void sendPubBack(Channel channel,int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK,false, MqttQoS.AT_MOST_ONCE,false,0x02); - MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); - MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); - channel.writeAndFlush(mqttPubAckMessage); - } - - - /** - * 发送qos2 publish 确认消息 第一步 - */ - protected void sendPubRec( MqttChannel mqttChannel,int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC,false, MqttQoS.AT_LEAST_ONCE,false,0x02); - MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); - MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); - Channel channel = mqttChannel.getChannel(); - channel.writeAndFlush(mqttPubAckMessage); - SendMqttMessage sendMqttMessage = addQueue(channel, messageId, null, null, null, ConfirmStatus.PUBREC); - mqttChannel.addSendMqttMessage(messageId,sendMqttMessage); - } - - /** - * 发送qos2 publish 确认消息 第二步 - */ - protected void sendPubRel(Channel channel,boolean isDup,int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREL,isDup, MqttQoS.AT_LEAST_ONCE,false,0x02); - MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); - MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); - channel.writeAndFlush(mqttPubAckMessage); - } - - /** - * 发送qos2 publish 确认消息 第三步 - */ - protected void sendToPubComp(Channel channel,int messageId){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP,false, MqttQoS.AT_MOST_ONCE,false,0x02); - MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); - MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); - channel.writeAndFlush(mqttPubAckMessage); - } - - private SendMqttMessage addQueue(Channel channel,int messageId,String topic,byte[] datas,MqttQoS mqttQoS,ConfirmStatus confirmStatus){ - SendMqttMessage build = SendMqttMessage.builder(). - channel(channel). - confirmStatus(confirmStatus). - messageId(messageId) - .topic(topic) - .qos(mqttQoS) - .byteBuf(datas) - .time(System.currentTimeMillis()).build(); - scanRunnable.addQueue(build); - return build; - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/WillService.java b/src/main/java/com/myself/nettychat/bootstrap/channel/WillService.java deleted file mode 100644 index 3aa4213..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/WillService.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.myself.nettychat.bootstrap.channel; - -import com.myself.nettychat.bootstrap.BaseApi; -import com.myself.nettychat.bootstrap.ChannelService; -import com.myself.nettychat.bootstrap.bean.WillMeaasge; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -@Component -@Data -@NoArgsConstructor -public class WillService implements BaseApi { - - @Autowired - ChannelService channelService; - - private static ConcurrentHashMap willMeaasges = new ConcurrentHashMap<>(); // deviceid -WillMeaasge - - - - /** - * 保存遗嘱消息 - */ - public void save(String deviceid, WillMeaasge build) { - willMeaasges.put(deviceid,build); // 替换旧的 - } - - - public void doSend( String deviceId) { // 客户端断开连接后 开启遗嘱消息发送 - if(StringUtils.isNotBlank(deviceId)&&(willMeaasges.get(deviceId))!=null){ - WillMeaasge willMeaasge = willMeaasges.get(deviceId); - channelService.sendWillMsg(willMeaasge); // 发送遗嘱消息 - if(!willMeaasge.isRetain()){ // 移除 - willMeaasges.remove(deviceId); - log.info("deviceId will message["+willMeaasge.getWillMessage()+"] is removed"); - } - } - } - - /** - * 删除遗嘱消息 - */ - public void del(String deviceid ) {willMeaasges.remove(deviceid);} - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/channel/cache/CacheMap.java b/src/main/java/com/myself/nettychat/bootstrap/channel/cache/CacheMap.java deleted file mode 100644 index 68f805e..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/channel/cache/CacheMap.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.myself.nettychat.bootstrap.channel.cache; - -import lombok.extern.slf4j.Slf4j; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -/** - * @author MySelf - * @create 2018/9/22 - * @desc 缓存操作 - **/ -@Slf4j -public class CacheMap { - - private ConcurrentHashMap> datas = new ConcurrentHashMap<>(); - - public boolean putData(K[] topic, V v){ - if(topic.length==1){ - Node kvNode = buildOne(topic[0], v); - if(kvNode!=null && kvNode.topic.equals(topic[0])){ - return true; - } - } - else{ - Node kvNode = buildOne(topic[0], null); - for(int i=1;i kvNode = datas.get(ks[0]); - for(int i=1;i getData(K[] ks){ - if(ks.length==1){ - return datas.get(ks[0]).get(); - } - else{ - Node node = datas.get(ks[0]); - if(node!=null){ - List all = new ArrayList<>(); - all.addAll(node.get()); - for(int i=1;i buildOne(K k,V v){ - - Node node = this.datas.computeIfAbsent(k, key -> { - Node kObjectNode = new Node<>(k); - return kObjectNode; - }); - if(v!=null){ - node.put(v); - } - return node; - } - - - - class Node{ - - private final K topic; - - - private volatile ConcurrentHashMap> map =new ConcurrentHashMap<>() ; - - - List vs = new CopyOnWriteArrayList<>(); - - - public K getTopic() {return topic;} - - Node(K topic) { - this.topic = topic; - } - - public boolean delValue(V v){ - return vs.remove(v); - } - - public Node putNextValue(K k,V v){ - Node kvNode = map.computeIfAbsent(k, key -> { - Node node = new Node<>(k); - return node; - }); - if(v!=null){ - kvNode.put(v); - } - return kvNode; - } - - - public Node getNext(K k){ - return map.get(k); - } - - - public boolean put(V v){ - return vs.add(v); - } - - - public List get(){ - return vs; - } - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/coder/ByteBufToWebSocketFrameEncoder.java b/src/main/java/com/myself/nettychat/bootstrap/coder/ByteBufToWebSocketFrameEncoder.java deleted file mode 100644 index 6c34606..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/coder/ByteBufToWebSocketFrameEncoder.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.myself.nettychat.bootstrap.coder; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; - -import java.util.List; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 转换 - **/ -public class ByteBufToWebSocketFrameEncoder extends MessageToMessageEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { - if (byteBuf == null) { - return; - } - BinaryWebSocketFrame result = new BinaryWebSocketFrame(); - result.content().writeBytes(byteBuf); - out.add(result); - } -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/coder/WebSocketFrameToByteBufDecoder.java b/src/main/java/com/myself/nettychat/bootstrap/coder/WebSocketFrameToByteBufDecoder.java deleted file mode 100644 index 032d657..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/coder/WebSocketFrameToByteBufDecoder.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.myself.nettychat.bootstrap.coder; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; - -import java.util.List; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 转换 - **/ -public class WebSocketFrameToByteBufDecoder extends MessageToMessageDecoder { - - - @Override - protected void decode(ChannelHandlerContext ctx, BinaryWebSocketFrame wsFrame, List out) throws Exception { - ByteBuf buf = wsFrame.content(); - //避免计数器为0,报错 - buf.retain(); - out.add(buf); - } -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/handler/DefaultMqttHandler.java b/src/main/java/com/myself/nettychat/bootstrap/handler/DefaultMqttHandler.java deleted file mode 100644 index 8c4fbee..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/handler/DefaultMqttHandler.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.myself.nettychat.bootstrap.handler; - -import com.myself.nettychat.bootstrap.ChannelService; -import com.myself.nettychat.bootstrap.bean.MqttChannel; -import com.myself.nettychat.common.mqtts.MqttHandlerIntf; -import com.myself.nettychat.common.mqtts.MqttHander; -import com.myself.nettychat.common.mqtts.ServerMqttHandlerService; -import com.myself.nettychat.common.exception.NoFindHandlerException; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.mqtt.*; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 默认MQTTHandler处理 - **/ -@Slf4j -@Component -@ChannelHandler.Sharable -public class DefaultMqttHandler extends MqttHander { - - private final MqttHandlerIntf mqttHandlerApi; - - @Autowired - ChannelService channelService; - - - public DefaultMqttHandler(MqttHandlerIntf mqttHandlerApi) { - super(mqttHandlerApi); - this.mqttHandlerApi = mqttHandlerApi; - } - - @Override - public void doMessage(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) { - Channel channel = channelHandlerContext.channel(); - ServerMqttHandlerService serverMqttHandlerService; - if(mqttHandlerApi instanceof ServerMqttHandlerService){ - serverMqttHandlerService =(ServerMqttHandlerService)mqttHandlerApi; - } - else{ - throw new NoFindHandlerException("server handler 不匹配"); - } - MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); - if(mqttFixedHeader.messageType().equals(MqttMessageType.CONNECT)){ - if(!serverMqttHandlerService.login(channel, (MqttConnectMessage) mqttMessage)){ - channel.close(); - } - return ; - } - MqttChannel mqttChannel = channelService.getMqttChannel(channelService.getDeviceId(channel)); - if(mqttChannel!=null && mqttChannel.isLogin()){ - switch (mqttFixedHeader.messageType()){ - case PUBLISH: - serverMqttHandlerService.publish(channel, (MqttPublishMessage) mqttMessage); - break; - case SUBSCRIBE: - serverMqttHandlerService.subscribe(channel, (MqttSubscribeMessage) mqttMessage); - break; - case PINGREQ: - serverMqttHandlerService.pong(channel); - break; - case DISCONNECT: - serverMqttHandlerService.disconnect(channel); - break; - case UNSUBSCRIBE: - serverMqttHandlerService.unsubscribe(channel,(MqttUnsubscribeMessage)mqttMessage); - break; - case PUBACK: - mqttHandlerApi.puback(channel,mqttMessage); - break; - case PUBREC: - mqttHandlerApi.pubrec(channel,mqttMessage); - break; - case PUBREL: - mqttHandlerApi.pubrel(channel,mqttMessage); - break; - case PUBCOMP: - mqttHandlerApi.pubcomp(channel,mqttMessage); - break; - default: - break; - } - } - } - - - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - log.info("【DefaultMqttHandler:channelActive】"+ctx.channel().remoteAddress().toString()+"链接成功"); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - log.error("exception",cause); - mqttHandlerApi.close(ctx.channel()); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/scan/SacnScheduled.java b/src/main/java/com/myself/nettychat/bootstrap/scan/SacnScheduled.java deleted file mode 100644 index 15cc0ce..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/scan/SacnScheduled.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.myself.nettychat.bootstrap.scan; - -import com.myself.nettychat.bootstrap.bean.SendMqttMessage; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import lombok.extern.slf4j.Slf4j; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 扫描消息确认 - **/ -@Slf4j -public class SacnScheduled extends ScanRunnable { - - private final long time; - - public SacnScheduled(long time) { - this.time = time; - } - - private boolean checkTime(long time) { - return System.currentTimeMillis()-time>=10*1000; - } - - @Override - public void doInfo(SendMqttMessage poll) { - if(checkTime(poll.getTime()) && poll.getChannel().isActive()){ - poll.setTime(System.currentTimeMillis()); - switch (poll.getConfirmStatus()){ - case PUB: - pubMessage(poll.getChannel(),poll); - break; - case PUBREL: - sendAck(MqttMessageType.PUBREL,poll); - break; - case PUBREC: - sendAck(MqttMessageType.PUBREC,poll); - break; - } - } - } - - private void pubMessage(Channel channel, SendMqttMessage mqttMessage){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,true, mqttMessage.getQos(),mqttMessage.isRetain(),0); - MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(mqttMessage.getTopic(),mqttMessage.getMessageId()); - MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(mqttMessage.getByteBuf())); - channel.writeAndFlush(mqttPublishMessage); - } - - protected void sendAck(MqttMessageType type,SendMqttMessage mqttMessage){ - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(type,true, MqttQoS.AT_LEAST_ONCE,false,0x02); - MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(mqttMessage.getMessageId()); - MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); - mqttMessage.getChannel().writeAndFlush(mqttPubAckMessage); - } - -} diff --git a/src/main/java/com/myself/nettychat/bootstrap/scan/ScanRunnable.java b/src/main/java/com/myself/nettychat/bootstrap/scan/ScanRunnable.java deleted file mode 100644 index 4d4cc8c..0000000 --- a/src/main/java/com/myself/nettychat/bootstrap/scan/ScanRunnable.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.myself.nettychat.bootstrap.scan; - -import com.myself.nettychat.bootstrap.bean.SendMqttMessage; -import lombok.extern.slf4j.Slf4j; -import com.myself.nettychat.common.enums.ConfirmStatus; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 扫描未确认的消息 - **/ -@Slf4j -public abstract class ScanRunnable implements Runnable{ - - LinkedBlockingQueue queue =new LinkedBlockingQueue(); - - public boolean addQueue(SendMqttMessage t){ - return queue.add(t); - } - - public boolean addQueues(List ts){ - return queue.addAll(ts); - } - - - @Override - public void run() { - for(;;){ - try { - SendMqttMessage poll= queue.take(); - if(poll.getConfirmStatus()!= ConfirmStatus.COMPLETE){ - doInfo(poll); - queue.offer(poll); - } - } catch (InterruptedException e) { - log.error("scan InterruptedException",e); - } - } - } - public abstract void doInfo( SendMqttMessage poll); - -} diff --git a/src/main/java/com/myself/nettychat/common/enums/ConfirmStatus.java b/src/main/java/com/myself/nettychat/common/enums/ConfirmStatus.java deleted file mode 100644 index 968b807..0000000 --- a/src/main/java/com/myself/nettychat/common/enums/ConfirmStatus.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.myself.nettychat.common.enums; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 确认状态 - **/ -public enum ConfirmStatus { - PUB, - PUBREC, - PUBREL, - COMPLETE, -} diff --git a/src/main/java/com/myself/nettychat/common/enums/ProtocolEnum.java b/src/main/java/com/myself/nettychat/common/enums/ProtocolEnum.java deleted file mode 100644 index 19855ea..0000000 --- a/src/main/java/com/myself/nettychat/common/enums/ProtocolEnum.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.myself.nettychat.common.enums; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 协议 - **/ -public enum ProtocolEnum { - MQTT, - - MQTT_WS_MQTT, - - MQTT_WS_PAHO, -} diff --git a/src/main/java/com/myself/nettychat/common/enums/QosStatus.java b/src/main/java/com/myself/nettychat/common/enums/QosStatus.java deleted file mode 100644 index 0a3643a..0000000 --- a/src/main/java/com/myself/nettychat/common/enums/QosStatus.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.myself.nettychat.common.enums; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc Qos确认状态 - **/ -public enum QosStatus { - - PUBD, // 已发送 没收到RECD (发送) - - RECD, //publish 推送回复过(发送) - -} diff --git a/src/main/java/com/myself/nettychat/common/enums/SessionStatus.java b/src/main/java/com/myself/nettychat/common/enums/SessionStatus.java deleted file mode 100644 index 593d63f..0000000 --- a/src/main/java/com/myself/nettychat/common/enums/SessionStatus.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.myself.nettychat.common.enums; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc Channel 会话状态 - **/ -public enum SessionStatus { - - OPEN, - CLOSE - -} diff --git a/src/main/java/com/myself/nettychat/common/enums/SubStatus.java b/src/main/java/com/myself/nettychat/common/enums/SubStatus.java deleted file mode 100644 index 2ab833c..0000000 --- a/src/main/java/com/myself/nettychat/common/enums/SubStatus.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.myself.nettychat.common.enums; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 是否订阅过主题 - **/ -public enum SubStatus { - YES, - NO -} diff --git a/src/main/java/com/myself/nettychat/common/mqtts/ClientMqttHandlerService.java b/src/main/java/com/myself/nettychat/common/mqtts/ClientMqttHandlerService.java deleted file mode 100644 index 56f9fda..0000000 --- a/src/main/java/com/myself/nettychat/common/mqtts/ClientMqttHandlerService.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.myself.nettychat.common.mqtts; - -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import io.netty.handler.timeout.IdleStateEvent; - - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 抽象出客户端的事件 - **/ -public abstract class ClientMqttHandlerService implements MqttHandlerIntf { - - @Override - public void doTimeOut(Channel channel, IdleStateEvent evt) { - heart(channel,evt); - } - - public abstract void heart(Channel channel, IdleStateEvent evt); - - public abstract void suback(Channel channel,MqttSubAckMessage mqttMessage) ; - - public abstract void pubBackMessage(Channel channel, int i); - - public abstract void unsubBack(Channel channel, MqttMessage mqttMessage); -} diff --git a/src/main/java/com/myself/nettychat/common/mqtts/MqttHander.java b/src/main/java/com/myself/nettychat/common/mqtts/MqttHander.java deleted file mode 100644 index 061dd77..0000000 --- a/src/main/java/com/myself/nettychat/common/mqtts/MqttHander.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.myself.nettychat.common.mqtts; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.mqtt.MqttFixedHeader; -import io.netty.handler.codec.mqtt.MqttMessage; -import io.netty.handler.timeout.IdleStateEvent; -import lombok.extern.slf4j.Slf4j; - -import java.util.Optional; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc mtqq协议处理器 - **/ -@Slf4j -public abstract class MqttHander extends SimpleChannelInboundHandler { - - MqttHandlerIntf mqttHandlerApi; - - public MqttHander(MqttHandlerIntf mqttHandlerIntf){ - this.mqttHandlerApi=mqttHandlerIntf; - } - - @Override - protected void channelRead0(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) throws Exception { - MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); - Optional.ofNullable(mqttFixedHeader) - .ifPresent(mqttFixedHeader1 -> doMessage(channelHandlerContext,mqttMessage)); - } - - - public abstract void doMessage(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage); - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - log.info("【DefaultMqttHandler:channelInactive】"+ctx.channel().localAddress().toString()+"关闭成功"); - mqttHandlerApi.close(ctx.channel()); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if(evt instanceof IdleStateEvent){ - mqttHandlerApi.doTimeOut(ctx.channel(),(IdleStateEvent)evt); - } - super.userEventTriggered(ctx, evt); - } - -} diff --git a/src/main/java/com/myself/nettychat/common/mqtts/MqttHandlerIntf.java b/src/main/java/com/myself/nettychat/common/mqtts/MqttHandlerIntf.java deleted file mode 100644 index 4f274ee..0000000 --- a/src/main/java/com/myself/nettychat/common/mqtts/MqttHandlerIntf.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.myself.nettychat.common.mqtts; - -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.MqttMessage; -import io.netty.handler.timeout.IdleStateEvent; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 自定义 对外暴露,消息处理API - **/ -public interface MqttHandlerIntf { - - void close(Channel channel); - - void puback(Channel channel, MqttMessage mqttMessage); - - void pubrec(Channel channel, MqttMessage mqttMessage); - - void pubrel(Channel channel, MqttMessage mqttMessage); - - void pubcomp(Channel channel, MqttMessage mqttMessage); - - void doTimeOut(Channel channel, IdleStateEvent evt); - -} diff --git a/src/main/java/com/myself/nettychat/common/mqtts/ServerMqttHandlerService.java b/src/main/java/com/myself/nettychat/common/mqtts/ServerMqttHandlerService.java deleted file mode 100644 index fd62ee5..0000000 --- a/src/main/java/com/myself/nettychat/common/mqtts/ServerMqttHandlerService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.myself.nettychat.common.mqtts; - -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.*; -import io.netty.handler.timeout.IdleStateEvent; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 抽象出服务端的事件 - **/ -public abstract class ServerMqttHandlerService implements MqttHandlerIntf { - - public abstract boolean login(Channel channel, MqttConnectMessage mqttConnectMessage); - - public abstract void publish(Channel channel, MqttPublishMessage mqttPublishMessage); - - public abstract void subscribe(Channel channel, MqttSubscribeMessage mqttSubscribeMessage); - - public abstract void pong(Channel channel); - - public abstract void unsubscribe(Channel channel, MqttUnsubscribeMessage mqttMessage); - - public abstract void disconnect(Channel channel); - - public abstract void doTimeOut(Channel channel, IdleStateEvent evt); - -} diff --git a/src/main/java/com/myself/nettychat/common/properties/InitNetty.java b/src/main/java/com/myself/nettychat/common/properties/InitNetty.java index ab6ad0c..25efbad 100644 --- a/src/main/java/com/myself/nettychat/common/properties/InitNetty.java +++ b/src/main/java/com/myself/nettychat/common/properties/InitNetty.java @@ -3,8 +3,6 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import com.myself.nettychat.common.enums.ProtocolEnum; -import com.myself.nettychat.common.mqtts.MqttHander; /** * @Author:UncleCatMySelf @@ -13,10 +11,10 @@ * @Date:Created in 10:54 2018\8\14 0014 */ @Data +@Component @ConfigurationProperties(prefix = "netty") public class InitNetty { - private ProtocolEnum protocol; private int webport; @@ -52,7 +50,6 @@ public class InitNetty { private String jksCertificatePassword; - private Class mqttHander ; private int initalDelay ; diff --git a/src/main/java/com/myself/nettychat/common/ssl/SecureSocketKeyStore.java b/src/main/java/com/myself/nettychat/common/ssl/SecureSocketKeyStore.java deleted file mode 100644 index 3bae691..0000000 --- a/src/main/java/com/myself/nettychat/common/ssl/SecureSocketKeyStore.java +++ /dev/null @@ -1,456 +0,0 @@ -package com.myself.nettychat.common.ssl; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.security.KeyStore; - -/** - * A bogus key store which provides all the required information to create an - * example SSL connection. - * - * To generate a bogus key store: - * - *
- * keytool -genkey -alias securesocket \ -keysize 2048 -validity 36500 \  -keyalg RSA -dname "CN=securesocket" \   -keypass inc0rrect -storepass mu$tch8ng3 \  -keystore cert.jks
- *
- *
- *
- *
- * 
- */ -public class SecureSocketKeyStore { - - private static final byte[] CERT_BYTES = { (byte) 254, (byte) 237, - (byte) 254, (byte) 237, (byte) 0, (byte) 0, (byte) 0, (byte) 2, - (byte) 0, (byte) 0, (byte) 0, (byte) 1, (byte) 0, (byte) 0, - (byte) 0, (byte) 1, (byte) 0, (byte) 12, (byte) 115, (byte) 101, - (byte) 99, (byte) 117, (byte) 114, (byte) 101, (byte) 115, - (byte) 111, (byte) 99, (byte) 107, (byte) 101, (byte) 116, - (byte) 0, (byte) 0, (byte) 1, (byte) 69, (byte) 231, (byte) 201, - (byte) 156, (byte) 140, (byte) 0, (byte) 0, (byte) 5, (byte) 0, - (byte) 48, (byte) 130, (byte) 4, (byte) 252, (byte) 48, (byte) 14, - (byte) 6, (byte) 10, (byte) 43, (byte) 6, (byte) 1, (byte) 4, - (byte) 1, (byte) 42, (byte) 2, (byte) 17, (byte) 1, (byte) 1, - (byte) 5, (byte) 0, (byte) 4, (byte) 130, (byte) 4, (byte) 232, - (byte) 221, (byte) 18, (byte) 203, (byte) 171, (byte) 175, - (byte) 82, (byte) 132, (byte) 227, (byte) 115, (byte) 143, - (byte) 38, (byte) 191, (byte) 42, (byte) 202, (byte) 130, - (byte) 171, (byte) 75, (byte) 6, (byte) 161, (byte) 120, - (byte) 204, (byte) 61, (byte) 106, (byte) 160, (byte) 81, (byte) 9, - (byte) 204, (byte) 153, (byte) 166, (byte) 38, (byte) 246, - (byte) 13, (byte) 43, (byte) 19, (byte) 100, (byte) 132, (byte) 45, - (byte) 90, (byte) 143, (byte) 1, (byte) 231, (byte) 182, (byte) 89, - (byte) 228, (byte) 183, (byte) 17, (byte) 95, (byte) 129, - (byte) 229, (byte) 42, (byte) 182, (byte) 126, (byte) 114, - (byte) 76, (byte) 124, (byte) 123, (byte) 246, (byte) 152, - (byte) 0, (byte) 141, (byte) 212, (byte) 111, (byte) 52, - (byte) 243, (byte) 112, (byte) 31, (byte) 117, (byte) 124, - (byte) 142, (byte) 24, (byte) 59, (byte) 198, (byte) 164, - (byte) 253, (byte) 21, (byte) 177, (byte) 189, (byte) 74, - (byte) 218, (byte) 110, (byte) 83, (byte) 154, (byte) 49, - (byte) 186, (byte) 159, (byte) 173, (byte) 202, (byte) 94, - (byte) 174, (byte) 183, (byte) 223, (byte) 119, (byte) 109, - (byte) 110, (byte) 72, (byte) 93, (byte) 208, (byte) 195, - (byte) 19, (byte) 89, (byte) 33, (byte) 34, (byte) 186, (byte) 12, - (byte) 86, (byte) 156, (byte) 156, (byte) 210, (byte) 111, - (byte) 110, (byte) 44, (byte) 106, (byte) 36, (byte) 67, - (byte) 168, (byte) 7, (byte) 179, (byte) 244, (byte) 53, - (byte) 134, (byte) 10, (byte) 86, (byte) 179, (byte) 34, (byte) 60, - (byte) 184, (byte) 179, (byte) 162, (byte) 69, (byte) 24, - (byte) 168, (byte) 100, (byte) 183, (byte) 206, (byte) 64, - (byte) 4, (byte) 32, (byte) 66, (byte) 237, (byte) 228, (byte) 92, - (byte) 6, (byte) 213, (byte) 141, (byte) 147, (byte) 198, - (byte) 141, (byte) 216, (byte) 41, (byte) 0, (byte) 101, (byte) 65, - (byte) 41, (byte) 185, (byte) 128, (byte) 229, (byte) 107, - (byte) 25, (byte) 89, (byte) 148, (byte) 16, (byte) 194, - (byte) 101, (byte) 100, (byte) 243, (byte) 147, (byte) 77, - (byte) 230, (byte) 11, (byte) 151, (byte) 99, (byte) 124, - (byte) 55, (byte) 195, (byte) 185, (byte) 30, (byte) 234, - (byte) 83, (byte) 61, (byte) 109, (byte) 131, (byte) 156, - (byte) 244, (byte) 133, (byte) 66, (byte) 39, (byte) 153, (byte) 9, - (byte) 34, (byte) 218, (byte) 201, (byte) 143, (byte) 190, - (byte) 127, (byte) 119, (byte) 102, (byte) 6, (byte) 83, - (byte) 134, (byte) 96, (byte) 170, (byte) 79, (byte) 196, - (byte) 214, (byte) 47, (byte) 215, (byte) 37, (byte) 250, - (byte) 64, (byte) 8, (byte) 165, (byte) 203, (byte) 44, (byte) 53, - (byte) 113, (byte) 147, (byte) 251, (byte) 29, (byte) 26, - (byte) 38, (byte) 193, (byte) 11, (byte) 223, (byte) 212, - (byte) 114, (byte) 96, (byte) 162, (byte) 39, (byte) 48, - (byte) 200, (byte) 172, (byte) 182, (byte) 254, (byte) 180, - (byte) 198, (byte) 11, (byte) 128, (byte) 75, (byte) 74, (byte) 93, - (byte) 226, (byte) 157, (byte) 80, (byte) 14, (byte) 9, (byte) 217, - (byte) 236, (byte) 205, (byte) 153, (byte) 35, (byte) 242, - (byte) 130, (byte) 140, (byte) 25, (byte) 16, (byte) 156, - (byte) 247, (byte) 230, (byte) 5, (byte) 247, (byte) 0, (byte) 34, - (byte) 196, (byte) 15, (byte) 118, (byte) 255, (byte) 185, - (byte) 199, (byte) 59, (byte) 99, (byte) 27, (byte) 187, (byte) 83, - (byte) 81, (byte) 12, (byte) 71, (byte) 69, (byte) 127, (byte) 130, - (byte) 164, (byte) 97, (byte) 195, (byte) 216, (byte) 215, - (byte) 61, (byte) 29, (byte) 196, (byte) 62, (byte) 160, - (byte) 188, (byte) 209, (byte) 173, (byte) 230, (byte) 0, - (byte) 204, (byte) 225, (byte) 1, (byte) 5, (byte) 42, (byte) 223, - (byte) 232, (byte) 187, (byte) 190, (byte) 67, (byte) 126, - (byte) 235, (byte) 178, (byte) 218, (byte) 179, (byte) 46, - (byte) 186, (byte) 156, (byte) 186, (byte) 6, (byte) 191, - (byte) 68, (byte) 239, (byte) 31, (byte) 16, (byte) 204, (byte) 24, - (byte) 68, (byte) 164, (byte) 88, (byte) 10, (byte) 174, (byte) 26, - (byte) 54, (byte) 187, (byte) 149, (byte) 132, (byte) 128, - (byte) 173, (byte) 165, (byte) 8, (byte) 69, (byte) 96, (byte) 49, - (byte) 57, (byte) 223, (byte) 110, (byte) 29, (byte) 215, - (byte) 98, (byte) 42, (byte) 15, (byte) 153, (byte) 228, - (byte) 216, (byte) 61, (byte) 160, (byte) 230, (byte) 34, - (byte) 40, (byte) 232, (byte) 136, (byte) 139, (byte) 140, - (byte) 236, (byte) 251, (byte) 119, (byte) 242, (byte) 199, - (byte) 167, (byte) 61, (byte) 141, (byte) 89, (byte) 29, (byte) 82, - (byte) 114, (byte) 229, (byte) 198, (byte) 27, (byte) 133, - (byte) 87, (byte) 0, (byte) 53, (byte) 69, (byte) 42, (byte) 91, - (byte) 174, (byte) 82, (byte) 244, (byte) 160, (byte) 82, - (byte) 142, (byte) 221, (byte) 106, (byte) 151, (byte) 241, - (byte) 214, (byte) 64, (byte) 14, (byte) 28, (byte) 2, (byte) 3, - (byte) 145, (byte) 143, (byte) 18, (byte) 165, (byte) 247, - (byte) 178, (byte) 211, (byte) 16, (byte) 222, (byte) 76, - (byte) 60, (byte) 119, (byte) 130, (byte) 199, (byte) 230, - (byte) 229, (byte) 3, (byte) 22, (byte) 100, (byte) 135, - (byte) 103, (byte) 60, (byte) 181, (byte) 191, (byte) 56, - (byte) 249, (byte) 181, (byte) 169, (byte) 210, (byte) 25, - (byte) 152, (byte) 201, (byte) 226, (byte) 119, (byte) 71, - (byte) 204, (byte) 70, (byte) 220, (byte) 103, (byte) 46, - (byte) 166, (byte) 125, (byte) 40, (byte) 86, (byte) 208, - (byte) 114, (byte) 138, (byte) 24, (byte) 27, (byte) 219, - (byte) 123, (byte) 161, (byte) 52, (byte) 14, (byte) 38, - (byte) 244, (byte) 112, (byte) 238, (byte) 121, (byte) 90, - (byte) 34, (byte) 157, (byte) 131, (byte) 53, (byte) 245, - (byte) 162, (byte) 89, (byte) 188, (byte) 6, (byte) 202, - (byte) 164, (byte) 130, (byte) 34, (byte) 232, (byte) 74, - (byte) 45, (byte) 137, (byte) 164, (byte) 200, (byte) 197, - (byte) 247, (byte) 64, (byte) 110, (byte) 122, (byte) 49, - (byte) 116, (byte) 137, (byte) 253, (byte) 170, (byte) 232, - (byte) 120, (byte) 26, (byte) 171, (byte) 228, (byte) 229, - (byte) 49, (byte) 56, (byte) 56, (byte) 106, (byte) 110, (byte) 12, - (byte) 109, (byte) 93, (byte) 105, (byte) 241, (byte) 196, - (byte) 11, (byte) 18, (byte) 89, (byte) 108, (byte) 146, - (byte) 224, (byte) 161, (byte) 181, (byte) 236, (byte) 74, - (byte) 128, (byte) 24, (byte) 239, (byte) 22, (byte) 146, (byte) 0, - (byte) 69, (byte) 182, (byte) 246, (byte) 43, (byte) 59, - (byte) 208, (byte) 33, (byte) 48, (byte) 81, (byte) 0, (byte) 70, - (byte) 225, (byte) 222, (byte) 122, (byte) 178, (byte) 138, - (byte) 12, (byte) 207, (byte) 233, (byte) 164, (byte) 13, - (byte) 176, (byte) 123, (byte) 95, (byte) 68, (byte) 238, - (byte) 134, (byte) 66, (byte) 95, (byte) 194, (byte) 192, - (byte) 225, (byte) 244, (byte) 14, (byte) 78, (byte) 53, - (byte) 189, (byte) 217, (byte) 229, (byte) 203, (byte) 192, - (byte) 34, (byte) 38, (byte) 169, (byte) 63, (byte) 239, - (byte) 128, (byte) 172, (byte) 143, (byte) 75, (byte) 7, - (byte) 237, (byte) 125, (byte) 179, (byte) 235, (byte) 229, - (byte) 98, (byte) 8, (byte) 211, (byte) 237, (byte) 116, (byte) 75, - (byte) 27, (byte) 211, (byte) 131, (byte) 245, (byte) 89, - (byte) 150, (byte) 35, (byte) 49, (byte) 207, (byte) 113, - (byte) 237, (byte) 114, (byte) 125, (byte) 134, (byte) 191, - (byte) 110, (byte) 30, (byte) 119, (byte) 131, (byte) 175, - (byte) 166, (byte) 201, (byte) 255, (byte) 200, (byte) 1, - (byte) 126, (byte) 163, (byte) 172, (byte) 52, (byte) 118, - (byte) 184, (byte) 221, (byte) 165, (byte) 167, (byte) 165, - (byte) 20, (byte) 135, (byte) 32, (byte) 222, (byte) 188, - (byte) 250, (byte) 64, (byte) 161, (byte) 67, (byte) 236, - (byte) 212, (byte) 131, (byte) 44, (byte) 32, (byte) 70, (byte) 0, - (byte) 24, (byte) 178, (byte) 83, (byte) 155, (byte) 145, - (byte) 136, (byte) 131, (byte) 120, (byte) 181, (byte) 164, - (byte) 155, (byte) 172, (byte) 41, (byte) 213, (byte) 164, - (byte) 98, (byte) 169, (byte) 152, (byte) 184, (byte) 170, - (byte) 107, (byte) 7, (byte) 21, (byte) 228, (byte) 175, - (byte) 192, (byte) 238, (byte) 68, (byte) 197, (byte) 119, - (byte) 228, (byte) 225, (byte) 156, (byte) 235, (byte) 241, - (byte) 172, (byte) 171, (byte) 236, (byte) 128, (byte) 78, - (byte) 117, (byte) 152, (byte) 123, (byte) 93, (byte) 156, - (byte) 57, (byte) 238, (byte) 211, (byte) 188, (byte) 47, - (byte) 62, (byte) 45, (byte) 127, (byte) 58, (byte) 38, (byte) 29, - (byte) 131, (byte) 95, (byte) 85, (byte) 149, (byte) 112, - (byte) 215, (byte) 207, (byte) 41, (byte) 201, (byte) 30, - (byte) 149, (byte) 73, (byte) 245, (byte) 179, (byte) 176, - (byte) 246, (byte) 203, (byte) 204, (byte) 252, (byte) 13, - (byte) 98, (byte) 151, (byte) 93, (byte) 87, (byte) 241, - (byte) 166, (byte) 46, (byte) 249, (byte) 148, (byte) 49, - (byte) 141, (byte) 136, (byte) 49, (byte) 77, (byte) 250, - (byte) 191, (byte) 157, (byte) 90, (byte) 84, (byte) 51, - (byte) 129, (byte) 133, (byte) 66, (byte) 253, (byte) 99, - (byte) 243, (byte) 34, (byte) 142, (byte) 197, (byte) 4, - (byte) 126, (byte) 7, (byte) 217, (byte) 126, (byte) 205, - (byte) 250, (byte) 141, (byte) 231, (byte) 225, (byte) 203, - (byte) 171, (byte) 246, (byte) 201, (byte) 48, (byte) 96, - (byte) 207, (byte) 74, (byte) 253, (byte) 120, (byte) 114, - (byte) 163, (byte) 192, (byte) 24, (byte) 12, (byte) 10, - (byte) 210, (byte) 94, (byte) 136, (byte) 152, (byte) 185, - (byte) 109, (byte) 87, (byte) 35, (byte) 159, (byte) 238, - (byte) 122, (byte) 200, (byte) 107, (byte) 103, (byte) 243, - (byte) 250, (byte) 152, (byte) 68, (byte) 66, (byte) 170, (byte) 0, - (byte) 134, (byte) 229, (byte) 168, (byte) 182, (byte) 30, - (byte) 89, (byte) 240, (byte) 121, (byte) 106, (byte) 148, - (byte) 142, (byte) 49, (byte) 242, (byte) 215, (byte) 233, - (byte) 57, (byte) 120, (byte) 204, (byte) 180, (byte) 239, - (byte) 199, (byte) 133, (byte) 255, (byte) 71, (byte) 3, - (byte) 132, (byte) 228, (byte) 110, (byte) 66, (byte) 227, - (byte) 122, (byte) 82, (byte) 118, (byte) 173, (byte) 218, - (byte) 54, (byte) 99, (byte) 167, (byte) 154, (byte) 3, (byte) 189, - (byte) 25, (byte) 123, (byte) 169, (byte) 42, (byte) 184, - (byte) 59, (byte) 36, (byte) 131, (byte) 206, (byte) 248, - (byte) 90, (byte) 32, (byte) 183, (byte) 86, (byte) 62, (byte) 149, - (byte) 107, (byte) 243, (byte) 71, (byte) 197, (byte) 124, - (byte) 155, (byte) 214, (byte) 91, (byte) 29, (byte) 81, (byte) 28, - (byte) 115, (byte) 98, (byte) 130, (byte) 184, (byte) 135, - (byte) 13, (byte) 191, (byte) 147, (byte) 43, (byte) 10, - (byte) 178, (byte) 99, (byte) 165, (byte) 210, (byte) 87, - (byte) 87, (byte) 148, (byte) 31, (byte) 198, (byte) 129, - (byte) 32, (byte) 181, (byte) 3, (byte) 144, (byte) 61, (byte) 5, - (byte) 166, (byte) 252, (byte) 73, (byte) 205, (byte) 230, - (byte) 178, (byte) 162, (byte) 46, (byte) 56, (byte) 99, (byte) 77, - (byte) 97, (byte) 236, (byte) 121, (byte) 157, (byte) 139, - (byte) 153, (byte) 217, (byte) 171, (byte) 19, (byte) 68, - (byte) 36, (byte) 14, (byte) 123, (byte) 249, (byte) 101, - (byte) 127, (byte) 184, (byte) 123, (byte) 7, (byte) 124, - (byte) 68, (byte) 98, (byte) 34, (byte) 139, (byte) 224, - (byte) 173, (byte) 246, (byte) 196, (byte) 180, (byte) 70, - (byte) 207, (byte) 168, (byte) 211, (byte) 255, (byte) 84, - (byte) 0, (byte) 174, (byte) 11, (byte) 160, (byte) 155, - (byte) 127, (byte) 228, (byte) 81, (byte) 226, (byte) 115, - (byte) 142, (byte) 200, (byte) 107, (byte) 4, (byte) 204, - (byte) 219, (byte) 192, (byte) 189, (byte) 56, (byte) 127, - (byte) 184, (byte) 187, (byte) 161, (byte) 106, (byte) 62, - (byte) 225, (byte) 211, (byte) 115, (byte) 30, (byte) 172, - (byte) 191, (byte) 66, (byte) 25, (byte) 66, (byte) 235, - (byte) 107, (byte) 41, (byte) 186, (byte) 40, (byte) 239, - (byte) 173, (byte) 11, (byte) 247, (byte) 89, (byte) 79, - (byte) 135, (byte) 86, (byte) 73, (byte) 77, (byte) 164, (byte) 34, - (byte) 109, (byte) 236, (byte) 56, (byte) 198, (byte) 141, - (byte) 87, (byte) 74, (byte) 172, (byte) 56, (byte) 24, (byte) 150, - (byte) 233, (byte) 233, (byte) 165, (byte) 122, (byte) 201, - (byte) 112, (byte) 232, (byte) 23, (byte) 12, (byte) 166, - (byte) 128, (byte) 114, (byte) 139, (byte) 207, (byte) 233, - (byte) 47, (byte) 220, (byte) 172, (byte) 175, (byte) 40, - (byte) 109, (byte) 82, (byte) 142, (byte) 130, (byte) 177, - (byte) 50, (byte) 127, (byte) 196, (byte) 106, (byte) 172, - (byte) 178, (byte) 71, (byte) 178, (byte) 204, (byte) 99, - (byte) 113, (byte) 33, (byte) 189, (byte) 188, (byte) 168, - (byte) 76, (byte) 92, (byte) 230, (byte) 211, (byte) 239, - (byte) 75, (byte) 71, (byte) 64, (byte) 197, (byte) 26, (byte) 222, - (byte) 19, (byte) 213, (byte) 161, (byte) 144, (byte) 20, - (byte) 126, (byte) 192, (byte) 156, (byte) 15, (byte) 113, - (byte) 64, (byte) 73, (byte) 7, (byte) 241, (byte) 217, (byte) 127, - (byte) 171, (byte) 199, (byte) 66, (byte) 32, (byte) 179, (byte) 4, - (byte) 181, (byte) 93, (byte) 121, (byte) 193, (byte) 10, - (byte) 169, (byte) 255, (byte) 152, (byte) 199, (byte) 95, - (byte) 177, (byte) 227, (byte) 135, (byte) 21, (byte) 64, - (byte) 203, (byte) 9, (byte) 79, (byte) 243, (byte) 114, (byte) 2, - (byte) 201, (byte) 157, (byte) 180, (byte) 52, (byte) 193, - (byte) 66, (byte) 34, (byte) 155, (byte) 52, (byte) 35, (byte) 93, - (byte) 31, (byte) 96, (byte) 77, (byte) 12, (byte) 80, (byte) 195, - (byte) 96, (byte) 247, (byte) 251, (byte) 237, (byte) 36, - (byte) 170, (byte) 7, (byte) 3, (byte) 251, (byte) 243, (byte) 47, - (byte) 180, (byte) 98, (byte) 207, (byte) 176, (byte) 106, - (byte) 237, (byte) 114, (byte) 91, (byte) 229, (byte) 56, - (byte) 94, (byte) 154, (byte) 32, (byte) 62, (byte) 240, - (byte) 132, (byte) 4, (byte) 144, (byte) 227, (byte) 140, - (byte) 137, (byte) 76, (byte) 15, (byte) 117, (byte) 82, - (byte) 223, (byte) 168, (byte) 135, (byte) 33, (byte) 91, - (byte) 173, (byte) 4, (byte) 245, (byte) 192, (byte) 95, - (byte) 135, (byte) 22, (byte) 138, (byte) 89, (byte) 1, (byte) 14, - (byte) 230, (byte) 143, (byte) 195, (byte) 93, (byte) 133, - (byte) 194, (byte) 252, (byte) 188, (byte) 31, (byte) 39, - (byte) 162, (byte) 59, (byte) 148, (byte) 219, (byte) 213, - (byte) 179, (byte) 195, (byte) 165, (byte) 67, (byte) 68, - (byte) 39, (byte) 178, (byte) 143, (byte) 192, (byte) 177, - (byte) 221, (byte) 236, (byte) 63, (byte) 40, (byte) 205, - (byte) 26, (byte) 81, (byte) 127, (byte) 5, (byte) 213, (byte) 192, - (byte) 22, (byte) 147, (byte) 98, (byte) 207, (byte) 153, (byte) 8, - (byte) 108, (byte) 75, (byte) 182, (byte) 148, (byte) 0, - (byte) 151, (byte) 15, (byte) 178, (byte) 98, (byte) 145, - (byte) 255, (byte) 213, (byte) 142, (byte) 63, (byte) 247, - (byte) 42, (byte) 161, (byte) 246, (byte) 21, (byte) 128, - (byte) 47, (byte) 248, (byte) 217, (byte) 70, (byte) 195, - (byte) 151, (byte) 236, (byte) 73, (byte) 153, (byte) 230, - (byte) 152, (byte) 217, (byte) 12, (byte) 189, (byte) 65, - (byte) 85, (byte) 189, (byte) 204, (byte) 212, (byte) 161, - (byte) 210, (byte) 217, (byte) 74, (byte) 75, (byte) 186, - (byte) 122, (byte) 167, (byte) 149, (byte) 178, (byte) 202, - (byte) 205, (byte) 246, (byte) 225, (byte) 225, (byte) 190, - (byte) 56, (byte) 42, (byte) 162, (byte) 215, (byte) 107, - (byte) 45, (byte) 121, (byte) 235, (byte) 195, (byte) 219, - (byte) 22, (byte) 0, (byte) 0, (byte) 0, (byte) 1, (byte) 0, - (byte) 5, (byte) 88, (byte) 46, (byte) 53, (byte) 48, (byte) 57, - (byte) 0, (byte) 0, (byte) 2, (byte) 211, (byte) 48, (byte) 130, - (byte) 2, (byte) 207, (byte) 48, (byte) 130, (byte) 1, (byte) 183, - (byte) 160, (byte) 3, (byte) 2, (byte) 1, (byte) 2, (byte) 2, - (byte) 4, (byte) 58, (byte) 247, (byte) 71, (byte) 185, (byte) 48, - (byte) 13, (byte) 6, (byte) 9, (byte) 42, (byte) 134, (byte) 72, - (byte) 134, (byte) 247, (byte) 13, (byte) 1, (byte) 1, (byte) 11, - (byte) 5, (byte) 0, (byte) 48, (byte) 23, (byte) 49, (byte) 21, - (byte) 48, (byte) 19, (byte) 6, (byte) 3, (byte) 85, (byte) 4, - (byte) 3, (byte) 19, (byte) 12, (byte) 115, (byte) 101, (byte) 99, - (byte) 117, (byte) 114, (byte) 101, (byte) 115, (byte) 111, - (byte) 99, (byte) 107, (byte) 101, (byte) 116, (byte) 48, - (byte) 32, (byte) 23, (byte) 13, (byte) 49, (byte) 52, (byte) 48, - (byte) 53, (byte) 49, (byte) 48, (byte) 50, (byte) 48, (byte) 49, - (byte) 56, (byte) 52, (byte) 48, (byte) 90, (byte) 24, (byte) 15, - (byte) 50, (byte) 49, (byte) 49, (byte) 52, (byte) 48, (byte) 52, - (byte) 49, (byte) 54, (byte) 50, (byte) 48, (byte) 49, (byte) 56, - (byte) 52, (byte) 48, (byte) 90, (byte) 48, (byte) 23, (byte) 49, - (byte) 21, (byte) 48, (byte) 19, (byte) 6, (byte) 3, (byte) 85, - (byte) 4, (byte) 3, (byte) 19, (byte) 12, (byte) 115, (byte) 101, - (byte) 99, (byte) 117, (byte) 114, (byte) 101, (byte) 115, - (byte) 111, (byte) 99, (byte) 107, (byte) 101, (byte) 116, - (byte) 48, (byte) 130, (byte) 1, (byte) 34, (byte) 48, (byte) 13, - (byte) 6, (byte) 9, (byte) 42, (byte) 134, (byte) 72, (byte) 134, - (byte) 247, (byte) 13, (byte) 1, (byte) 1, (byte) 1, (byte) 5, - (byte) 0, (byte) 3, (byte) 130, (byte) 1, (byte) 15, (byte) 0, - (byte) 48, (byte) 130, (byte) 1, (byte) 10, (byte) 2, (byte) 130, - (byte) 1, (byte) 1, (byte) 0, (byte) 153, (byte) 113, (byte) 7, - (byte) 44, (byte) 219, (byte) 76, (byte) 101, (byte) 226, - (byte) 138, (byte) 96, (byte) 219, (byte) 60, (byte) 167, - (byte) 138, (byte) 222, (byte) 6, (byte) 78, (byte) 169, (byte) 64, - (byte) 188, (byte) 156, (byte) 190, (byte) 119, (byte) 16, - (byte) 34, (byte) 228, (byte) 250, (byte) 253, (byte) 119, - (byte) 75, (byte) 240, (byte) 60, (byte) 242, (byte) 52, - (byte) 137, (byte) 146, (byte) 20, (byte) 130, (byte) 202, - (byte) 226, (byte) 125, (byte) 19, (byte) 7, (byte) 34, (byte) 8, - (byte) 61, (byte) 243, (byte) 202, (byte) 225, (byte) 206, - (byte) 223, (byte) 53, (byte) 74, (byte) 56, (byte) 222, (byte) 47, - (byte) 99, (byte) 235, (byte) 57, (byte) 73, (byte) 90, (byte) 198, - (byte) 109, (byte) 104, (byte) 36, (byte) 255, (byte) 124, - (byte) 57, (byte) 155, (byte) 248, (byte) 120, (byte) 56, - (byte) 56, (byte) 38, (byte) 41, (byte) 216, (byte) 1, (byte) 216, - (byte) 216, (byte) 100, (byte) 239, (byte) 79, (byte) 222, - (byte) 34, (byte) 21, (byte) 182, (byte) 112, (byte) 136, - (byte) 137, (byte) 16, (byte) 141, (byte) 15, (byte) 83, (byte) 94, - (byte) 245, (byte) 36, (byte) 203, (byte) 178, (byte) 137, - (byte) 159, (byte) 86, (byte) 220, (byte) 253, (byte) 112, - (byte) 200, (byte) 50, (byte) 135, (byte) 215, (byte) 190, - (byte) 21, (byte) 186, (byte) 84, (byte) 21, (byte) 96, (byte) 126, - (byte) 253, (byte) 115, (byte) 209, (byte) 241, (byte) 94, - (byte) 115, (byte) 219, (byte) 0, (byte) 25, (byte) 253, - (byte) 209, (byte) 182, (byte) 118, (byte) 230, (byte) 10, - (byte) 50, (byte) 131, (byte) 39, (byte) 249, (byte) 136, - (byte) 11, (byte) 101, (byte) 192, (byte) 12, (byte) 210, - (byte) 179, (byte) 237, (byte) 213, (byte) 68, (byte) 101, - (byte) 58, (byte) 187, (byte) 255, (byte) 240, (byte) 164, - (byte) 147, (byte) 72, (byte) 148, (byte) 227, (byte) 155, - (byte) 88, (byte) 250, (byte) 101, (byte) 253, (byte) 87, - (byte) 140, (byte) 168, (byte) 39, (byte) 163, (byte) 133, - (byte) 150, (byte) 252, (byte) 226, (byte) 234, (byte) 52, - (byte) 88, (byte) 40, (byte) 56, (byte) 23, (byte) 105, (byte) 236, - (byte) 4, (byte) 113, (byte) 98, (byte) 4, (byte) 0, (byte) 117, - (byte) 59, (byte) 77, (byte) 236, (byte) 135, (byte) 93, (byte) 54, - (byte) 30, (byte) 6, (byte) 126, (byte) 90, (byte) 15, (byte) 105, - (byte) 89, (byte) 216, (byte) 154, (byte) 72, (byte) 134, - (byte) 209, (byte) 74, (byte) 197, (byte) 237, (byte) 51, - (byte) 37, (byte) 33, (byte) 106, (byte) 50, (byte) 71, (byte) 134, - (byte) 169, (byte) 173, (byte) 88, (byte) 111, (byte) 217, - (byte) 117, (byte) 184, (byte) 97, (byte) 1, (byte) 38, (byte) 76, - (byte) 112, (byte) 170, (byte) 190, (byte) 250, (byte) 96, - (byte) 17, (byte) 45, (byte) 117, (byte) 183, (byte) 82, - (byte) 155, (byte) 10, (byte) 53, (byte) 15, (byte) 214, (byte) 36, - (byte) 134, (byte) 249, (byte) 146, (byte) 98, (byte) 99, - (byte) 64, (byte) 158, (byte) 99, (byte) 227, (byte) 21, (byte) 92, - (byte) 98, (byte) 90, (byte) 202, (byte) 214, (byte) 134, - (byte) 233, (byte) 212, (byte) 149, (byte) 2, (byte) 3, (byte) 1, - (byte) 0, (byte) 1, (byte) 163, (byte) 33, (byte) 48, (byte) 31, - (byte) 48, (byte) 29, (byte) 6, (byte) 3, (byte) 85, (byte) 29, - (byte) 14, (byte) 4, (byte) 22, (byte) 4, (byte) 20, (byte) 115, - (byte) 110, (byte) 177, (byte) 165, (byte) 41, (byte) 26, - (byte) 142, (byte) 198, (byte) 221, (byte) 63, (byte) 79, - (byte) 252, (byte) 219, (byte) 159, (byte) 68, (byte) 102, - (byte) 76, (byte) 153, (byte) 128, (byte) 164, (byte) 48, - (byte) 13, (byte) 6, (byte) 9, (byte) 42, (byte) 134, (byte) 72, - (byte) 134, (byte) 247, (byte) 13, (byte) 1, (byte) 1, (byte) 11, - (byte) 5, (byte) 0, (byte) 3, (byte) 130, (byte) 1, (byte) 1, - (byte) 0, (byte) 118, (byte) 55, (byte) 245, (byte) 122, - (byte) 159, (byte) 155, (byte) 98, (byte) 122, (byte) 229, - (byte) 186, (byte) 23, (byte) 207, (byte) 109, (byte) 225, - (byte) 220, (byte) 74, (byte) 51, (byte) 218, (byte) 10, - (byte) 115, (byte) 137, (byte) 103, (byte) 127, (byte) 28, - (byte) 30, (byte) 184, (byte) 149, (byte) 249, (byte) 193, - (byte) 206, (byte) 208, (byte) 181, (byte) 191, (byte) 128, - (byte) 18, (byte) 208, (byte) 24, (byte) 132, (byte) 147, - (byte) 184, (byte) 198, (byte) 82, (byte) 204, (byte) 183, - (byte) 127, (byte) 87, (byte) 234, (byte) 136, (byte) 197, - (byte) 34, (byte) 232, (byte) 124, (byte) 210, (byte) 2, - (byte) 192, (byte) 69, (byte) 246, (byte) 25, (byte) 232, - (byte) 162, (byte) 0, (byte) 157, (byte) 216, (byte) 194, - (byte) 26, (byte) 207, (byte) 225, (byte) 169, (byte) 59, - (byte) 246, (byte) 52, (byte) 51, (byte) 150, (byte) 210, - (byte) 50, (byte) 118, (byte) 58, (byte) 154, (byte) 45, - (byte) 128, (byte) 138, (byte) 47, (byte) 174, (byte) 83, - (byte) 117, (byte) 18, (byte) 224, (byte) 9, (byte) 146, - (byte) 180, (byte) 178, (byte) 22, (byte) 76, (byte) 82, - (byte) 229, (byte) 16, (byte) 150, (byte) 127, (byte) 13, - (byte) 122, (byte) 218, (byte) 159, (byte) 195, (byte) 232, - (byte) 168, (byte) 206, (byte) 105, (byte) 82, (byte) 37, - (byte) 252, (byte) 186, (byte) 223, (byte) 222, (byte) 7, - (byte) 106, (byte) 87, (byte) 218, (byte) 89, (byte) 22, - (byte) 252, (byte) 7, (byte) 177, (byte) 52, (byte) 180, (byte) 9, - (byte) 16, (byte) 29, (byte) 57, (byte) 192, (byte) 209, - (byte) 225, (byte) 155, (byte) 16, (byte) 219, (byte) 38, - (byte) 90, (byte) 174, (byte) 152, (byte) 140, (byte) 252, - (byte) 114, (byte) 133, (byte) 106, (byte) 24, (byte) 107, - (byte) 227, (byte) 80, (byte) 166, (byte) 63, (byte) 47, (byte) 16, - (byte) 15, (byte) 89, (byte) 242, (byte) 19, (byte) 87, (byte) 193, - (byte) 250, (byte) 222, (byte) 223, (byte) 183, (byte) 61, - (byte) 91, (byte) 17, (byte) 92, (byte) 35, (byte) 142, (byte) 44, - (byte) 153, (byte) 135, (byte) 86, (byte) 97, (byte) 70, - (byte) 205, (byte) 38, (byte) 192, (byte) 18, (byte) 244, - (byte) 61, (byte) 46, (byte) 21, (byte) 145, (byte) 99, (byte) 72, - (byte) 142, (byte) 37, (byte) 19, (byte) 219, (byte) 167, - (byte) 62, (byte) 71, (byte) 197, (byte) 86, (byte) 152, - (byte) 139, (byte) 122, (byte) 231, (byte) 122, (byte) 206, - (byte) 42, (byte) 142, (byte) 164, (byte) 237, (byte) 19, - (byte) 60, (byte) 95, (byte) 239, (byte) 191, (byte) 64, - (byte) 188, (byte) 94, (byte) 154, (byte) 199, (byte) 252, - (byte) 62, (byte) 26, (byte) 181, (byte) 194, (byte) 141, - (byte) 13, (byte) 1, (byte) 112, (byte) 161, (byte) 195, - (byte) 149, (byte) 116, (byte) 57, (byte) 118, (byte) 114, - (byte) 248, (byte) 235, (byte) 54, (byte) 229, (byte) 48, - (byte) 53, (byte) 30, (byte) 145, (byte) 199, (byte) 207, - (byte) 49, (byte) 175, (byte) 44, (byte) 172, (byte) 120, - (byte) 254, (byte) 181, (byte) 100, (byte) 113, (byte) 191, - (byte) 64, (byte) 131, (byte) 125, (byte) 80, (byte) 180, - (byte) 229, (byte) 109, (byte) 97, (byte) 8, (byte) 166, - (byte) 155, (byte) 72, (byte) 252, (byte) 84, (byte) 62, (byte) 97, - (byte) 80, (byte) 26, (byte) 17, (byte) 143, (byte) 96, (byte) 16, - (byte) 204, (byte) 86, (byte) 61, (byte) 226, (byte) 149 }; - - - public static KeyStore getKeyStore() - { - KeyStore ks = null; - try{ - ks = KeyStore.getInstance("JKS"); - ks.load(asInputStream(), getKeyStorePassword()); - }catch(Exception ex){ - throw new RuntimeException("Failed to load SSL key store.", ex); - } - return ks; - } - - public static InputStream asInputStream() { - return new ByteArrayInputStream(CERT_BYTES); - } - - public static char[] getCertificatePassword() { - return "inc0rrect".toCharArray(); - } - - public static char[] getKeyStorePassword() { - return "mu$tch8ng3".toCharArray(); - } - - public static String getCertificatePasswordString() { - return "inc0rrect"; - } - - public static String getKeyStorePasswordString() { - return "mu$tch8ng3"; - } - - private SecureSocketKeyStore() { - - } - -} diff --git a/src/main/java/com/myself/nettychat/common/ssl/SecureSocketSslContextFactory.java b/src/main/java/com/myself/nettychat/common/ssl/SecureSocketSslContextFactory.java deleted file mode 100644 index d4a0bf7..0000000 --- a/src/main/java/com/myself/nettychat/common/ssl/SecureSocketSslContextFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.myself.nettychat.common.ssl; - -import io.netty.util.internal.SystemPropertyUtil; - -import javax.net.ssl.*; -import java.security.KeyStore; -import java.security.SecureRandom; - -/** - * Creates a bogus {@link SSLContext}. A client-side context created by this - * factory accepts any certificate even if it is invalid. A server-side context - * created by this factory sends a bogus certificate defined in {@link }. - *

- * You will have to create your context differently in a real world application. - * - *

Client Certificate Authentication

- * - * To enable client certificate authentication: - *
    - *
  • Enable client authentication on the server side by calling - * {@link SSLEngine#setNeedClientAuth(boolean)} before creating - * {@link }.
  • - *
  • When initializing an {@link SSLContext} on the client side, - * specify the {@link KeyManager} that contains the client certificate as - * the first argument of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}.
  • - *
  • When initializing an {@link SSLContext} on the server side, - * specify the proper {@link TrustManager} as the second argument of - * {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} - * to validate the client certificate.
  • - *
- */ -public final class SecureSocketSslContextFactory { - - private static final String PROTOCOL = "TLS"; - private static final SSLContext SERVER_CONTEXT; - private static final SSLContext CLIENT_CONTEXT; - - static { - String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm"); - if (algorithm == null) { - algorithm = "SunX509"; - } - - SSLContext serverContext; - SSLContext clientContext; - try { - // - //SecureSocketSslContextFactory.class.getResourceAsStream("/securesocket.jks") - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(SecureSocketKeyStore.asInputStream(), - SecureSocketKeyStore.getKeyStorePassword()); - - // Set up key manager factory to use our key store - KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); - kmf.init(ks, SecureSocketKeyStore.getCertificatePassword()); - - // Initialize the SSLContext to work with our key managers. - serverContext = SSLContext.getInstance(PROTOCOL); - serverContext.init(kmf.getKeyManagers(), null, null); - } catch (Exception e) { - throw new Error( - "Failed to initialize the server-side SSLContext", e); - } - - try { - clientContext = SSLContext.getInstance(PROTOCOL); - clientContext.init(null, SecureSokcetTrustManagerFactory.getTrustManagers(), null); - } catch (Exception e) { - throw new Error( - "Failed to initialize the client-side SSLContext", e); - } - - SERVER_CONTEXT = serverContext; - CLIENT_CONTEXT = clientContext; - } - - public static SSLContext getServerContext() { - return SERVER_CONTEXT; - } - - public static SSLContext getClientContext() { - return CLIENT_CONTEXT; - } - - private SecureSocketSslContextFactory() { - // Unused - } - -} diff --git a/src/main/java/com/myself/nettychat/common/ssl/SecureSokcetTrustManagerFactory.java b/src/main/java/com/myself/nettychat/common/ssl/SecureSokcetTrustManagerFactory.java deleted file mode 100644 index fdc57a1..0000000 --- a/src/main/java/com/myself/nettychat/common/ssl/SecureSokcetTrustManagerFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.myself.nettychat.common.ssl; - -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactorySpi; -import javax.net.ssl.X509TrustManager; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.cert.X509Certificate; - -/** - * Bogus {@link TrustManagerFactorySpi} which accepts any certificate - * even if it is invalid. - */ -public class SecureSokcetTrustManagerFactory extends TrustManagerFactorySpi { - - private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - // Always trust - it is an example. - // You should do something in the real world. - // You will reach here only if you enabled client certificate auth, - // as described in SecureChatSslContextFactory. - System.err.println( - "UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN()); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - // Always trust - it is an example. - // You should do something in the real world. - System.err.println( - "UNKNOWN SERVER CERTIFICATE: 222 " + chain[0].getSubjectDN()); - } - }; - - public static TrustManager[] getTrustManagers() { - return new TrustManager[] { DUMMY_TRUST_MANAGER }; - } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return getTrustManagers(); - } - - @Override - protected void engineInit(KeyStore keystore) throws KeyStoreException { - // Unused - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) - throws InvalidAlgorithmParameterException { - // Unused - } - -} diff --git a/src/main/java/com/myself/nettychat/common/ssl/StreamReader.java b/src/main/java/com/myself/nettychat/common/ssl/StreamReader.java deleted file mode 100644 index 25cf5b7..0000000 --- a/src/main/java/com/myself/nettychat/common/ssl/StreamReader.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.myself.nettychat.common.ssl; - -import java.io.InputStream; - -public class StreamReader { - - public String toByteArray(InputStream fin) - { - int i = -1; - StringBuilder buf = new StringBuilder(); - try{ - while((i=fin.read())!=-1){ - if(buf.length()>0) buf.append(","); - buf.append("(byte)"); - buf.append(i); - } - - }catch(Throwable e){ - ; - } - - return buf.toString(); - } - -} diff --git a/src/main/java/com/myself/nettychat/common/ssl/X509CertTool.java b/src/main/java/com/myself/nettychat/common/ssl/X509CertTool.java deleted file mode 100644 index be36d26..0000000 --- a/src/main/java/com/myself/nettychat/common/ssl/X509CertTool.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.myself.nettychat.common.ssl; - - - - -import sun.security.x509.*; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.Date; - - -/** - * This class would require rt.jar in the class path in order to - * generated it alternative is using keytool. - */ -public class X509CertTool { - - /** - * Create a self-signed X.509 Certificate - * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" - * @param pair the KeyPair - * @param days how many days from now the Certificate is valid for - * @param algorithm the signing algorithm, eg "SHA1withRSA" - */ - @SuppressWarnings("restriction") - X509Certificate generateCertificate(String dn, KeyPair pair, int days, - String algorithm) throws GeneralSecurityException, IOException { - PrivateKey privkey = pair.getPrivate(); - X509CertInfo info = new X509CertInfo(); - Date from = new Date(); - Date to = new Date(from.getTime() + days * 86400000l); - CertificateValidity interval = new CertificateValidity(from, to); - BigInteger sn = new BigInteger(64, new SecureRandom()); - X500Name owner = new X500Name(dn); - - info.set(X509CertInfo.VALIDITY, interval); - info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); - info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); - info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); - info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); - info.set(X509CertInfo.VERSION, new CertificateVersion( - CertificateVersion.V3)); - AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); - info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); - - // Sign the cert to identify the algorithm that's used. - X509CertImpl cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - - // Update the algorith, and resign. - algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); - info.set(CertificateAlgorithmId.NAME + "." - + CertificateAlgorithmId.ALGORITHM, algo); - cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - return cert; - } - - public static void main(String[] args) { - - } - -} diff --git a/src/main/java/com/myself/nettychat/common/zookeeper/ZkStateListener.java b/src/main/java/com/myself/nettychat/common/zookeeper/ZkStateListener.java deleted file mode 100644 index b2658fa..0000000 --- a/src/main/java/com/myself/nettychat/common/zookeeper/ZkStateListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.myself.nettychat.common.zookeeper; - -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.state.ConnectionState; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc zookeeper 监听 - **/ -public interface ZkStateListener { - - default void connectedEvent(CuratorFramework curator, ConnectionState state) { - } - - default void ReconnectedEvent(CuratorFramework curator, ConnectionState state) { - } - - default void lostEvent(CuratorFramework curator, ConnectionState state) { - } - -} diff --git a/src/main/java/com/myself/nettychat/common/zookeeper/ZkUtils.java b/src/main/java/com/myself/nettychat/common/zookeeper/ZkUtils.java deleted file mode 100644 index 1cc853b..0000000 --- a/src/main/java/com/myself/nettychat/common/zookeeper/ZkUtils.java +++ /dev/null @@ -1,366 +0,0 @@ -package com.myself.nettychat.common.zookeeper; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import com.google.common.base.Charsets; -import com.google.common.base.Objects; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.framework.api.GetDataBuilder; -import org.apache.curator.framework.recipes.cache.NodeCache; -import org.apache.curator.framework.recipes.cache.PathChildrenCache; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.apache.curator.framework.recipes.cache.TreeCache; -import org.apache.curator.framework.state.ConnectionState; -import org.apache.curator.retry.RetryNTimes; -import org.apache.curator.utils.CloseableUtils; -import org.apache.curator.utils.ZKPaths; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.data.Stat; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Slf4j -@Data -@NoArgsConstructor -public class ZkUtils { - - private CuratorFramework zkClient = null; - - ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); - - List pathChildrenCaches = new CopyOnWriteArrayList<>(); - - List nodeCaches = new CopyOnWriteArrayList<>(); - - List treeCaches = new CopyOnWriteArrayList<>(); - - - /** - * 初始化zk链接 - * - * @param zookeeperServer - * @param connectionTimeout - * @param sessionTimeout - * @param maxRetries - * @param retriesSleepTime - * @param namespace - * @param listener - */ - public void init(String zookeeperServer, - int connectionTimeout, - int sessionTimeout, - int maxRetries, int retriesSleepTime, - String namespace, - ZkStateListener listener) { - if (zkClient == null) { - zkClient = CuratorFrameworkFactory.builder() - .connectString(zookeeperServer) - .connectionTimeoutMs(connectionTimeout) - .sessionTimeoutMs(sessionTimeout) - .namespace(namespace) - .retryPolicy(new RetryNTimes(maxRetries, retriesSleepTime)) - .build(); - } - zkClient.getConnectionStateListenable().addListener((curatorFramework, connectionState) -> { - pathChildrenCaches.clear(); - nodeCaches.clear(); - treeCaches.clear(); - if (connectionState == ConnectionState.CONNECTED) { - listener.connectedEvent(curatorFramework, connectionState); - } else if (connectionState == ConnectionState.RECONNECTED) { - listener.ReconnectedEvent(curatorFramework, connectionState); - } else if (connectionState == ConnectionState.LOST) { - listener.lostEvent(curatorFramework, connectionState); - } - }); - zkClient.start(); - } - /** - * 销毁所有 - */ - public void destory() { - pathChildrenCaches.stream().forEach(cache -> CloseableUtils.closeQuietly(cache)); - pathChildrenCaches.clear(); - pathChildrenCaches = null; - nodeCaches.stream().forEach(cache -> CloseableUtils.closeQuietly(cache)); - nodeCaches.clear(); - nodeCaches = null; - treeCaches.stream().forEach(cache -> CloseableUtils.closeQuietly(cache)); - treeCaches.clear(); - treeCaches = null; - if (zkClient != null) { - CloseableUtils.closeQuietly(zkClient); - } - } - - - /** - * 创建节点 - * - * @param path - * @param data - * @param mode - * @return - */ - public boolean createNode(String path, String data, CreateMode mode) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return Boolean.FALSE; - } - try { - Stat stat = exists(path); - if (stat == null) { - mode = mode == null ? CreateMode.PERSISTENT : mode; - String opResult; - if (ObjectUtils.allNotNull(data)) { - opResult = zkClient.create().creatingParentContainersIfNeeded().withMode(mode).forPath(path, data.getBytes(Charsets.UTF_8)); - } else { - opResult = zkClient.create().creatingParentContainersIfNeeded().withMode(mode).forPath(path); - } - return Objects.equal(opResult, path); - } - return Boolean.TRUE; - } catch (Exception e) { - log.error("create node fail! path: {}, error: {}", path, e); - } - return Boolean.FALSE; - } - - - /** - * 删除节点 递归删除子节点 - * - * @param path - * @param version - * @return - */ - public boolean deleteNode(String path, Integer version) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return Boolean.FALSE; - } - try { - Stat stat = exists(path); - if (stat != null) { - if (version == null) { - zkClient.delete().deletingChildrenIfNeeded().forPath(path); - } else { - zkClient.delete().deletingChildrenIfNeeded().withVersion(version).forPath(path); - } - } - return Boolean.TRUE; - } catch (Exception e) { - log.error("delete node fail! path: {}, error: {}", path, e); - } - return Boolean.FALSE; - } - - /** - * 删除节点 - * - * @param path - * @return - */ - public boolean deleteNode(String path) { - return deleteNode(path, null); - } - - /** - * 获取指定节点的值 - * - * @param path - * @return - */ - public byte[] getNodeData(String path) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return null; - } - try { - Stat stat = exists(path); - if (stat != null) { - return zkClient.getData().forPath(path); - } - } catch (Exception e) { - log.error("get node data fail! path: {}, error: {}", path, e); - } - return null; - } - - /** - * 获取指定节点的值 - * - * @param path - * @return - */ - public String getNodeDataStr(String path) { - byte[] val = getNodeData(path); - return val == null ? null : new String(val, Charsets.UTF_8); - } - - /** - * 检查节点是否存在 - * - * @param path - * @return - */ - public Stat exists(String path) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return null; - } - try { - return zkClient.checkExists().forPath(path); - } catch (Exception e) { - log.error("check node exists fail! path: {}, error: {}", path, e); - } - return null; - } - - /** - * 检查节点是否存在 - * - * @param path - * @return - */ - public boolean checkExists(String path) { - return exists(path) == null ? Boolean.FALSE : Boolean.TRUE; - } - - /** - * 设置节点数据 - * - * @param path - * @param data - * @return - */ - public boolean setNodeData(String path, String data) { - return setNodeData(path, data.getBytes(Charsets.UTF_8), null); - } - - /** - * 设置节点数据 - * - * @param path - * @param data - * @param version - * @return - */ - public boolean setNodeData(String path, byte[] data, Integer version) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return Boolean.FALSE; - } - try { - Stat stat = exists(path); - if (stat != null) { - if (version == null) { - zkClient.setData().forPath(path, data); - } else { - zkClient.setData().withVersion(version).forPath(path, data); - } - return Boolean.TRUE; - } - } catch (Exception e) { - log.error("set node data fail! path: {}, error: {}", path, e); - } - return Boolean.FALSE; - } - - /** - * 设置子节点更改监听 - * - * @param path - * @throws Exception - */ - public boolean listenerPathChildrenCache(String path, BiConsumer biConsumer) { - - if (!ObjectUtils.allNotNull(zkClient, path, biConsumer)) { - return Boolean.FALSE; - } - try { - Stat stat = exists(path); - if (stat != null) { - PathChildrenCache watcher = new PathChildrenCache(zkClient, path, true); - watcher.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); - //该模式下 watcher在重连的时候会自动 rebuild - watcher.getListenable().addListener(biConsumer::accept, pool); - if (!pathChildrenCaches.contains(watcher)) { - pathChildrenCaches.add(watcher); - } -// else{ -// watcher.rebuild(); -// } - return Boolean.TRUE; - } - } catch (Exception e) { - log.error("listen path children cache fail! path:{} , error:{}", path, e); - } - return Boolean.FALSE; - } - - - /** - * 读取指定节点的子菜单的值 - * - * @param path - * @return - */ - public Map readTargetChildsData(String path) { - if (!ObjectUtils.allNotNull(zkClient, path)) { - return null; - } - Map map = null; - try { - Stat stat = exists(path); - if (stat != null) { - List childrens = zkClient.getChildren().forPath(path); - GetDataBuilder dataBuilder = zkClient.getData(); - if (childrens != null) { - map = childrens.stream().collect(Collectors.toMap(Function.identity(), (child) -> { - try { - return new String(dataBuilder.forPath(ZKPaths.makePath(path, child)), Charsets.UTF_8); - } catch (Exception e1) { - return null; - } - })); - } - } - } catch (Exception e) { - log.error("get target childs data fail!, path:{} , error:{}", path, e); - } - return map; - - } - - - public static void main(String[] a) throws Exception { - ZkUtils zkUtils = new ZkUtils(); - zkUtils.init("127.0.0.1:2181", 1000, 2000, 5, 1000, "test", new ZkStateListener() { - @Override - public void connectedEvent(CuratorFramework curator, ConnectionState state) { - - } - - @Override - public void ReconnectedEvent(CuratorFramework curator, ConnectionState state) { - - } - - @Override - public void lostEvent(CuratorFramework curator, ConnectionState state) { - - } - }); - zkUtils.getZkClient().create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/yy/uu/ii"); - - Thread.sleep(Long.MAX_VALUE); - } - -} diff --git a/src/main/java/com/myself/nettychat/common/zookeeper/testZk.java b/src/main/java/com/myself/nettychat/common/zookeeper/testZk.java deleted file mode 100644 index e51292b..0000000 --- a/src/main/java/com/myself/nettychat/common/zookeeper/testZk.java +++ /dev/null @@ -1,256 +0,0 @@ -package com.myself.nettychat.common.zookeeper; - -import org.apache.curator.RetryPolicy; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.framework.api.ACLProvider; -import org.apache.curator.framework.api.CuratorEvent; -import org.apache.curator.framework.api.CuratorListener; -import org.apache.curator.framework.recipes.cache.*; -import org.apache.curator.retry.ExponentialBackoffRetry; -import org.apache.curator.retry.RetryNTimes; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Id; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * @author MySelf - * @create 2018/9/22 - * @desc 测试 - **/ -public class testZk { - - /** - * 先测试玩玩 - * @描述:XXXXXXX - * @param args - * @return void - * @exception - * @createTime:2016年5月17日 - * @author: songqinghu - * @throws Exception - */ - public static void main(String[] args) throws Exception { - CuratorFramework client = clientTwo(); - //setListenterDateNode(); - //setListenterThreeOne(client); - // setListenterThreeTwo(client); - setListenterThreeThree(client); - // getDataNode(client, "/two"); - // setDataNode(client, "/two", "sss"); - - } - - /** - * - * @描述:创建一个zookeeper连接---连接方式一: 最简单的连接 - * @return void - * @exception - * @createTime:2016年5月17日 - * @author: songqinghu - */ - private static CuratorFramework clientOne(){ - //zk 地址 - String connectString = "10.125.2.44:2181"; - // 连接时间 和重试次数 - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, retryPolicy); - client.start(); - return client; - } - - /** - * - * @描述:创建一个zookeeper连接---连接方式二:优选这个 - * @return void - * @exception - * @createTime:2016年5月17日 - * @author: songqinghu - */ - private static CuratorFramework clientTwo(){ - - //默认创建的根节点是没有做权限控制的--需要自己手动加权限???---- - ACLProvider aclProvider = new ACLProvider() { - private List acl ; - @Override - public List getDefaultAcl() { - if(acl ==null){ - ArrayList acl = ZooDefs.Ids.CREATOR_ALL_ACL; - acl.clear(); - acl.add(new ACL(ZooDefs.Perms.ALL, new Id("auth", "admin:admin") )); - this.acl = acl; - } - return acl; - } - @Override - public List getAclForPath(String path) { - return acl; - } - }; - String scheme = "digest"; - byte[] auth = "admin:admin".getBytes(); - int connectionTimeoutMs = 5000; - String connectString = "127.0.0.1:2181"; - String namespace = "testnamespace"; - CuratorFramework client = CuratorFrameworkFactory.builder().aclProvider(aclProvider). - authorization(scheme, auth). - connectionTimeoutMs(connectionTimeoutMs). - connectString(connectString). - namespace(namespace). - retryPolicy(new RetryNTimes(Integer.MAX_VALUE, 1000)).build(); - client.start(); - return client; - } - - - /** - * - * @描述:第一种监听器的添加方式: 对指定的节点进行添加操作 - * 仅仅能监控指定的本节点的数据修改,删除 操作 并且只能监听一次 --->不好 - * @return void - * @exception - * @createTime:2016年5月18日 - * @author: songqinghu - * @throws Exception - */ - private static void setListenterOne(CuratorFramework client) throws Exception{ - // 注册观察者,当节点变动时触发 - byte[] data = client.getData().usingWatcher(new Watcher() { - @Override - public void process(WatchedEvent event) { - System.out.println("获取 two 节点 监听器 : " + event); - } - }).forPath("/two"); - System.out.println("two 节点数据: "+ new String(data)); - } - /** - * - * @描述:第二种监听器的添加方式: - * 也是一次性的监听操作,使用后就无法在继续监听了 - * @return void - * @exception - * @createTime:2016年5月18日 - * @author: songqinghu - * @throws Exception - */ - private static void setListenterTwo(CuratorFramework client) throws Exception{ - - ExecutorService pool = Executors.newCachedThreadPool(); - - CuratorListener listener = new CuratorListener() { - @Override - public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { - System.out.println("监听器 : "+ event.toString()); - } - }; - client.getCuratorListenable().addListener(listener,pool); - client.getData().inBackground().forPath("/two"); - client.getData().inBackground().forPath("/two"); - client.getData().inBackground().forPath("/two"); - client.getData().inBackground().forPath("/two"); - Thread.sleep(Long.MAX_VALUE ); - } - /** - * - * @描述:第三种监听器的添加方式: Cache 的三种实现 实践 - * Path Cache:监视一个路径下1)孩子结点的创建、2)删除,3)以及结点数据的更新。 - * 产生的事件会传递给注册的PathChildrenCacheListener。 - * Node Cache:监视一个结点的创建、更新、删除,并将结点的数据缓存在本地。 - * Tree Cache:Path Cache和Node Cache的“合体”,监视路径下的创建、更新、删除事件,并缓存路径下所有孩子结点的数据。 - * @return void - * @exception - * @createTime:2016年5月18日 - * @author: songqinghu - * @throws Exception - */ - //1.path Cache 连接 路径 是否获取数据 - //能监听所有的字节点 且是无限监听的模式 但是 指定目录下节点的子节点不再监听 - private static void setListenterThreeOne(CuratorFramework client) throws Exception{ - ExecutorService pool = Executors.newCachedThreadPool(); - PathChildrenCache childrenCache = new PathChildrenCache(client, "/test", true); - PathChildrenCacheListener childrenCacheListener = new PathChildrenCacheListener() { - @Override - public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { - System.out.println("开始进行事件分析:-----"); - ChildData data = event.getData(); - switch (event.getType()) { - case CHILD_ADDED: - System.out.println("CHILD_ADDED : "+ data.getPath() +" 数据:"+ data.getData()); - break; - case CHILD_REMOVED: - System.out.println("CHILD_REMOVED : "+ data.getPath() +" 数据:"+ data.getData()); - break; - case CHILD_UPDATED: - System.out.println("CHILD_UPDATED : "+ data.getPath() +" 数据:"+ data.getData()); - break; - default: - break; - } - } - }; - childrenCache.getListenable().addListener(childrenCacheListener); - System.out.println("Register zk watcher successfully!"); - childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); - } - - //2.Node Cache 监控本节点的变化情况 连接 目录 是否压缩 - //监听本节点的变化 节点可以进行修改操作 删除节点后会再次创建(空节点) - private static void setListenterThreeTwo(CuratorFramework client) throws Exception{ - ExecutorService pool = Executors.newCachedThreadPool(); - //设置节点的cache - final NodeCache nodeCache = new NodeCache(client, "/test", false); - nodeCache.getListenable().addListener(new NodeCacheListener() { - @Override - public void nodeChanged() throws Exception { - System.out.println("the test node is change and result is :"); - System.out.println("path : "+nodeCache.getCurrentData().getPath()); - System.out.println("data : "+new String(nodeCache.getCurrentData().getData())); - System.out.println("stat : "+nodeCache.getCurrentData().getStat()); - } - }); - nodeCache.start(); - } - //3.Tree Cache - // 监控 指定节点和节点下的所有的节点的变化--无限监听 可以进行本节点的删除(不在创建) - private static void setListenterThreeThree(CuratorFramework client) throws Exception{ - ExecutorService pool = Executors.newCachedThreadPool(); - //设置节点的cache - TreeCache treeCache = new TreeCache(client, "/test"); - //设置监听器和处理过程 - treeCache.getListenable().addListener(new TreeCacheListener() { - @Override - public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { - ChildData data = event.getData(); - if(data !=null){ - switch (event.getType()) { - case NODE_ADDED: - System.out.println("NODE_ADDED : "+ data.getPath() +" 数据:"+ new String(data.getData())); - break; - case NODE_REMOVED: - System.out.println("NODE_REMOVED : "+ data.getPath() +" 数据:"+ new String(data.getData())); - break; - case NODE_UPDATED: - System.out.println("NODE_UPDATED : "+ data.getPath() +" 数据:"+ new String(data.getData())); - break; - - default: - break; - } - }else{ - System.out.println( "data is null : "+ event.getType()); - } - } - }); - //开始监听 - treeCache.start(); - - } - -} From 790f2b1e7a49846737ab96c7116607edb76f358e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=AE=8F=E9=9F=AC?= <3183764662@qq.com> Date: Fri, 2 Nov 2018 16:24:20 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E3=80=90=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E6=B3=A8=E5=86=8C=E3=80=91API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 +++++ .../nettychat/constont/ResultConstant.java | 16 ++++ .../controller/NcChangeController.java | 88 ------------------- .../controller/NcLoginController.java | 75 ++++------------ .../com/myself/nettychat/dataobject/User.java | 4 + src/main/resources/application.yml | 4 +- 6 files changed, 64 insertions(+), 146 deletions(-) create mode 100644 src/main/java/com/myself/nettychat/constont/ResultConstant.java delete mode 100644 src/main/java/com/myself/nettychat/controller/NcChangeController.java diff --git a/README.md b/README.md index dc603a8..1c8a24f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,29 @@ 本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 +## swagger-ui + +前端对接公告,目前推出API,请均已此文档说明的为主,其余API非正式版或测试版,误用 +查看API列表 +> http://localhost:8080/susu/swagger-ui.html + +## API列表详情 + +* 1、账号注册 +> POST http://loclhost:8080/susu/user/to_register +- username(用户名) +- password(密码) +- 前端Tip:传值判断,参数均不能为空,密码限制在前端做判断 + + +## 返回码与信息值 + +| 返回码 | 信息内容 | 备注 | +|------|---------|------| +| 200 | 成功 | | +| 555 | 参数错误| | +| 556 | 用户名存在| | + ## 提示 暂未初始化,请勿使用 \ No newline at end of file diff --git a/src/main/java/com/myself/nettychat/constont/ResultConstant.java b/src/main/java/com/myself/nettychat/constont/ResultConstant.java new file mode 100644 index 0000000..2edc421 --- /dev/null +++ b/src/main/java/com/myself/nettychat/constont/ResultConstant.java @@ -0,0 +1,16 @@ +package com.myself.nettychat.constont; + +/** + * Created by MySelf on 2018/11/2. + */ +public interface ResultConstant { + + Integer PARAMS_CODE = 555; + + String PARAMS_MSG = "【参数错误】"; + + Integer USER_NAME_REPART_CODE = 556; + + String USER_NAME_REPART_MSG = "【用户名存在】"; + +} diff --git a/src/main/java/com/myself/nettychat/controller/NcChangeController.java b/src/main/java/com/myself/nettychat/controller/NcChangeController.java deleted file mode 100644 index feb492b..0000000 --- a/src/main/java/com/myself/nettychat/controller/NcChangeController.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.myself.nettychat.controller; - -import com.myself.nettychat.constont.CookieConstant; -import com.myself.nettychat.constont.H5Constant; -import com.myself.nettychat.dataobject.User; -import com.myself.nettychat.service.UserService; -import com.myself.nettychat.store.TokenStore; -import com.myself.nettychat.common.utils.CookieUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import java.util.Map; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 15:59 2018\9\5 0005 - */ -@Controller -@RequestMapping("/su") -public class NcChangeController { - - @Autowired - private UserService userService; - - /** - * 我的中心界面 - * @param map - * @return - */ - @GetMapping("/me") - public ModelAndView Me(Map map){ - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - HttpServletRequest request = attributes.getRequest(); - Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN); - if (cookie == null){ - map.put("msg","cookie中不存在token"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - Integer userId = (Integer) TokenStore.get(cookie.getValue()); - if (userId == null){ - map.put("msg","用户信息不存在"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - User user = userService.findOne(userId); - map.put("userName",user.getUserName()); - return new ModelAndView(H5Constant.ME,map); - } - - /** - * 发现 - * @param map - * @return - */ - @GetMapping("/find") - public ModelAndView find(Map map){ - return new ModelAndView(H5Constant.FIND); - } - - /** - * 聊天 - * @param map - * @return - */ - @GetMapping("/chat") - public ModelAndView chat(Map map){ - return new ModelAndView(H5Constant.CHAT); - } - - /** - * 主页 - * @param map - * @return - */ - @GetMapping("/home") - public ModelAndView home(Map map){ - return new ModelAndView(H5Constant.HOME); - } - -} diff --git a/src/main/java/com/myself/nettychat/controller/NcLoginController.java b/src/main/java/com/myself/nettychat/controller/NcLoginController.java index 2db26fb..800ec6a 100644 --- a/src/main/java/com/myself/nettychat/controller/NcLoginController.java +++ b/src/main/java/com/myself/nettychat/controller/NcLoginController.java @@ -1,21 +1,21 @@ package com.myself.nettychat.controller; +import com.myself.nettychat.common.utils.ResultVOUtil; import com.myself.nettychat.constont.CookieConstant; import com.myself.nettychat.constont.H5Constant; +import com.myself.nettychat.constont.ResultConstant; import com.myself.nettychat.dataobject.User; import com.myself.nettychat.form.LoginForm; import com.myself.nettychat.repository.UserMsgRepository; import com.myself.nettychat.service.UserService; import com.myself.nettychat.store.TokenStore; import com.myself.nettychat.common.utils.CookieUtil; +import com.myself.nettychat.vo.ResultVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletResponse; @@ -30,73 +30,36 @@ * @QQ:1341933031 * @Date:Created in 16:01 2018\8\18 0018 */ -@Controller -@RequestMapping("/admin") +@RestController +@RequestMapping("/user") public class NcLoginController { @Autowired private UserService userService; - @Autowired - private UserMsgRepository userMsgRepository; - - /** - * 登录页面 - * @return - */ -// @GetMapping("/login") -// public ModelAndView login(Map map){ -// return new ModelAndView(H5Constant.LOGIN); -// } - - - /** - * 登录页面SUI - * @return - */ - @GetMapping("/loginsui") - public ModelAndView loginSui(Map map){ - return new ModelAndView(H5Constant.LOGIN_SUI); - } /** - * 注册页面 + * + * @param username + * @param password * @return */ - @GetMapping("/regis") - public ModelAndView register(){ - return new ModelAndView(H5Constant.LOGIN_SUI); - } - - - - /** - * 执行注册 - * @param form - * @param bindingResult - * @param response - * @param map - * @return - */ - @PostMapping("/toRegister") - public ModelAndView toRegister(@Valid LoginForm form, BindingResult bindingResult , HttpServletResponse response, - Map map){ - if (bindingResult.hasErrors()){ - map.put("msg",bindingResult.getFieldError().getDefaultMessage()); - return new ModelAndView(H5Constant.LOGIN_SUI,map); + @PostMapping(value = "/to_register") + public ResultVo toRegister(@RequestParam("username") String username,@RequestParam("password") String password){ + if (username.isEmpty() | password.isEmpty()){ + return ResultVOUtil.error(ResultConstant.PARAMS_CODE,ResultConstant.PARAMS_MSG); } + //TODO 用户过多,需优化 List userList = userService.findAll(); for (User item:userList){ - if (item.getUserName().equals(form.getFUserName())){ - map.put("msg","用户名已存在,请重新填写唯一用户名"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); + if (item.getUserName().equals(username)){ + return ResultVOUtil.error(ResultConstant.USER_NAME_REPART_CODE,ResultConstant.USER_NAME_REPART_MSG); } } User user = new User(); - BeanUtils.copyProperties(form,user); + user.setUserName(username); + user.setPassWord(password); userService.save(user); - map.put("userName",user.getUserName()); - map.put("passWord",user.getPassWord()); - return new ModelAndView(H5Constant.LOGIN_SUI,map); + return ResultVOUtil.success("【账号注册成功】"); } /** diff --git a/src/main/java/com/myself/nettychat/dataobject/User.java b/src/main/java/com/myself/nettychat/dataobject/User.java index d00bbad..3d63dc5 100644 --- a/src/main/java/com/myself/nettychat/dataobject/User.java +++ b/src/main/java/com/myself/nettychat/dataobject/User.java @@ -1,6 +1,8 @@ package com.myself.nettychat.dataobject; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicUpdate; import javax.persistence.Entity; @@ -20,6 +22,8 @@ @Data @Entity @DynamicUpdate +@NoArgsConstructor +@AllArgsConstructor public class User implements Serializable { private static final long serialVersionUID = 8143981246513357880L; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6da58e9..5a1dd6b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,8 +2,8 @@ spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root - password: root - url: jdbc:mysql://localhost:3306/nettychat?characterEncoding=utf-8&useSSL=false + password: 123456 + url: jdbc:mysql://192.168.1.207:3306/nettychat?characterEncoding=utf-8&useSSL=false jpa: show-sql: true server: From 4b77f2e5be1e75f3aee96072f161f5a069a49153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=AE=8F=E9=9F=AC?= <3183764662@qq.com> Date: Fri, 2 Nov 2018 16:26:40 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=E4=BF=AE=E6=94=B9README=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c8a24f..51da07d 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,4 @@ ## 提示 -暂未初始化,请勿使用 \ No newline at end of file +仅API列表详情中的API处于可用状态,其余API请勿使用,暂未基本完成,使用请详看文档 \ No newline at end of file From a2b6a20e596c42b18f080fd5451e22f9a00e4d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=AE=8F=E9=9F=AC?= <3183764662@qq.com> Date: Fri, 2 Nov 2018 17:02:58 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E5=AF=B9=E3=80=90=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E3=80=91API=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E4=B8=8E=E7=9B=B8=E5=85=B3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NcLoginController.java | 80 +++++++++---------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/myself/nettychat/controller/NcLoginController.java b/src/main/java/com/myself/nettychat/controller/NcLoginController.java index 800ec6a..a99fdb3 100644 --- a/src/main/java/com/myself/nettychat/controller/NcLoginController.java +++ b/src/main/java/com/myself/nettychat/controller/NcLoginController.java @@ -11,6 +11,7 @@ import com.myself.nettychat.store.TokenStore; import com.myself.nettychat.common.utils.CookieUtil; import com.myself.nettychat.vo.ResultVo; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -25,11 +26,13 @@ import java.util.UUID; /** + * 用户账号模块 * @Author:UncleCatMySelf * @Email:zhupeijie_java@126.com * @QQ:1341933031 * @Date:Created in 16:01 2018\8\18 0018 */ +@Slf4j @RestController @RequestMapping("/user") public class NcLoginController { @@ -38,20 +41,23 @@ public class NcLoginController { private UserService userService; /** - * - * @param username - * @param password - * @return + * 【账号注册】-2018-11-2 + * @param username 用户名 + * @param password 密码 + * @return {@link ResultVo} */ @PostMapping(value = "/to_register") public ResultVo toRegister(@RequestParam("username") String username,@RequestParam("password") String password){ + //参数非空判断,如前端实现,可省略 if (username.isEmpty() | password.isEmpty()){ + log.info("user-【参数为空】"); return ResultVOUtil.error(ResultConstant.PARAMS_CODE,ResultConstant.PARAMS_MSG); } //TODO 用户过多,需优化 List userList = userService.findAll(); for (User item:userList){ if (item.getUserName().equals(username)){ + log.info("user-【用户名已存在】-"+username); return ResultVOUtil.error(ResultConstant.USER_NAME_REPART_CODE,ResultConstant.USER_NAME_REPART_MSG); } } @@ -59,50 +65,36 @@ public ResultVo toRegister(@RequestParam("username") String username,@RequestPar user.setUserName(username); user.setPassWord(password); userService.save(user); + log.info("user-【账号注册成功】-"+username); return ResultVOUtil.success("【账号注册成功】"); } - /** - * 登录判断 - * @return - */ - @PostMapping("/toLogin") - public ModelAndView toLogin(@RequestParam(value = "page",defaultValue = "1") Integer page, - @RequestParam(value = "size",defaultValue = "10") Integer size, - @Valid LoginForm form, BindingResult bindingResult , HttpServletResponse response, - Map map){ - if (bindingResult.hasErrors()){ - map.put("msg",bindingResult.getFieldError().getDefaultMessage()); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - try { - User user = userService.findByUserName(form.getFUserName()); - if (user.getPassWord().equals(form.getFPassWord())){ - //登录成功 - String token = UUID.randomUUID().toString(); - //将token信息添加到系统缓存中 - TokenStore.add(token,user.getId()); - //将Token信息添加到Cookie中 - CookieUtil.set(response, CookieConstant.TOKEN,token,CookieConstant.EXPIRE); -// Sort sort = new Sort(Sort.Direction.DESC,"id"); -// Pageable pageable = new PageRequest(page-1,size,sort); -// Page userMsgPage = userMsgRepository.findAll(pageable); -// //日期颠倒 -// List userMsgList = new ArrayList<>(); -// for (int i = 0,j = userMsgPage.getContent().size()-1; i < userMsgPage.getContent().size();i++,j--){ -// userMsgList.add(userMsgPage.getContent().get(j)); -// } -// map.put("userMsgList",userMsgList); -// map.put("userName",user.getUserName()); - return new ModelAndView(H5Constant.HOME); - }else{ - map.put("msg","密码错误"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - }catch (Exception e){ - map.put("msg","用户不存在"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); + + @PostMapping(value = "/to_login") + public ResultVo toLogin(@RequestParam("username") String username,@RequestParam("password") String password){ + //参数非空判断,如前端实现,可省略 + if (username.isEmpty() | password.isEmpty()){ + return ResultVOUtil.error(ResultConstant.PARAMS_CODE,ResultConstant.PARAMS_MSG); } +// try { +// User user = userService.findByUserName(form.getFUserName()); +// if (user.getPassWord().equals(form.getFPassWord())){ +// //登录成功 +// String token = UUID.randomUUID().toString(); +// //将token信息添加到系统缓存中 +// TokenStore.add(token,user.getId()); +// //将Token信息添加到Cookie中 +// CookieUtil.set(response, CookieConstant.TOKEN,token,CookieConstant.EXPIRE); +// return new ModelAndView(H5Constant.HOME); +// }else{ +// map.put("msg","密码错误"); +// return new ModelAndView(H5Constant.LOGIN_SUI,map); +// } +// }catch (Exception e){ +// +// return new ModelAndView(H5Constant.LOGIN_SUI,map); +// } + return ResultVOUtil.success(); } } From d32603e3e25f9f1c9606411eb52511501b5662dc Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sat, 17 Nov 2018 14:19:28 +0800 Subject: [PATCH 09/10] updateMD --- README.md | 38 +++++++++++++++++++++----------------- doc/doc.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 doc/doc.md diff --git a/README.md b/README.md index 51da07d..ad308da 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,36 @@ -# InChat-IM-API(当前版本0.0.2) +# InChat-IM-API(0.0.2 Version) -## 简介 +## Summery -本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 +This project is Inchat core project, server-side project, in the form of APIs as external functions, similar to Tencent IM service-side role, this article will also focus on the various APIs of this project, there is no embedded IoT communication module, only websocket chat room as the initial development, +Friends who need to use IoT can first go to the [Master](Https://github.com/UncleCatMySelf/InChat/tree/master) project to learn. + +## 中文文档 + +* [中文文档](doc/doc.md) ## swagger-ui -前端对接公告,目前推出API,请均已此文档说明的为主,其余API非正式版或测试版,误用 -查看API列表 +Front end docking announcement, currently launched API, please have this document description of the main, the rest of the API informal or beta version, misuse View the list of APIs > http://localhost:8080/susu/swagger-ui.html -## API列表详情 +## API List Details -* 1、账号注册 +* 1、Account Registration > POST http://loclhost:8080/susu/user/to_register -- username(用户名) -- password(密码) -- 前端Tip:传值判断,参数均不能为空,密码限制在前端做判断 +- Params:username +- Params:password +- Front End tip: Pass value judgment, parameters can not be empty, password limit in the front end to do judgment -## 返回码与信息值 +## Return code and information values -| 返回码 | 信息内容 | 备注 | +| Return code | Content of information | Note | |------|---------|------| -| 200 | 成功 | | -| 555 | 参数错误| | -| 556 | 用户名存在| | +| 200 | Success | | +| 555 | Parameter error| | +| 556 | User name exists| | -## 提示 +## Tips -仅API列表详情中的API处于可用状态,其余API请勿使用,暂未基本完成,使用请详看文档 \ No newline at end of file +Only the APIs in the API list details are available, and the remaining APIs are not used, are not basically complete, please look at the documentation in detail \ No newline at end of file diff --git a/doc/doc.md b/doc/doc.md new file mode 100644 index 0000000..684d80f --- /dev/null +++ b/doc/doc.md @@ -0,0 +1,32 @@ +# InChat-IM-API(当前版本0.0.2) + +## 简介 + +本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 + +## swagger-ui + +前端对接公告,目前推出API,请均已此文档说明的为主,其余API非正式版或测试版,误用 +查看API列表 +> http://localhost:8080/susu/swagger-ui.html + +## API列表详情 + +* 1、账号注册 +> POST http://loclhost:8080/susu/user/to_register +- 参数:username(用户名) +- 参数:password(密码) +- 前端Tip:传值判断,参数均不能为空,密码限制在前端做判断 + + +## 返回码与信息值 + +| 返回码 | 信息内容 | 备注 | +|------|---------|------| +| 200 | 成功 | | +| 555 | 参数错误| | +| 556 | 用户名存在| | + +## 提示 + +仅API列表详情中的API处于可用状态,其余API请勿使用,暂未基本完成,使用请详看文档 \ No newline at end of file From 5afa65f2deaad2ab15cf3400e5c6d9f7e9bd2a52 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Thu, 6 Dec 2018 23:13:16 +0800 Subject: [PATCH 10/10] update --- README.md | 38 +++++++++++++++++--------------------- doc/doc.md | 32 -------------------------------- 2 files changed, 17 insertions(+), 53 deletions(-) delete mode 100644 doc/doc.md diff --git a/README.md b/README.md index ad308da..684d80f 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,32 @@ -# InChat-IM-API(0.0.2 Version) +# InChat-IM-API(当前版本0.0.2) -## Summery +## 简介 -This project is Inchat core project, server-side project, in the form of APIs as external functions, similar to Tencent IM service-side role, this article will also focus on the various APIs of this project, there is no embedded IoT communication module, only websocket chat room as the initial development, -Friends who need to use IoT can first go to the [Master](Https://github.com/UncleCatMySelf/InChat/tree/master) project to learn. - -## 中文文档 - -* [中文文档](doc/doc.md) +本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 ## swagger-ui -Front end docking announcement, currently launched API, please have this document description of the main, the rest of the API informal or beta version, misuse View the list of APIs +前端对接公告,目前推出API,请均已此文档说明的为主,其余API非正式版或测试版,误用 +查看API列表 > http://localhost:8080/susu/swagger-ui.html -## API List Details +## API列表详情 -* 1、Account Registration +* 1、账号注册 > POST http://loclhost:8080/susu/user/to_register -- Params:username -- Params:password -- Front End tip: Pass value judgment, parameters can not be empty, password limit in the front end to do judgment +- 参数:username(用户名) +- 参数:password(密码) +- 前端Tip:传值判断,参数均不能为空,密码限制在前端做判断 -## Return code and information values +## 返回码与信息值 -| Return code | Content of information | Note | +| 返回码 | 信息内容 | 备注 | |------|---------|------| -| 200 | Success | | -| 555 | Parameter error| | -| 556 | User name exists| | +| 200 | 成功 | | +| 555 | 参数错误| | +| 556 | 用户名存在| | -## Tips +## 提示 -Only the APIs in the API list details are available, and the remaining APIs are not used, are not basically complete, please look at the documentation in detail \ No newline at end of file +仅API列表详情中的API处于可用状态,其余API请勿使用,暂未基本完成,使用请详看文档 \ No newline at end of file diff --git a/doc/doc.md b/doc/doc.md deleted file mode 100644 index 684d80f..0000000 --- a/doc/doc.md +++ /dev/null @@ -1,32 +0,0 @@ -# InChat-IM-API(当前版本0.0.2) - -## 简介 - -本项目为InChat核心项目,服务端项目,以API形式作为对外功能,类似腾讯IM的服务端作用,本文也将着重讲解本项目的各个API,目前还没有嵌入Iot通信模块,仅以WebSocket的聊天室作为初期发展,需要使用到Iot的朋友可以先去[Master](https://github.com/UncleCatMySelf/InChat/tree/master)项目了解。 - -## swagger-ui - -前端对接公告,目前推出API,请均已此文档说明的为主,其余API非正式版或测试版,误用 -查看API列表 -> http://localhost:8080/susu/swagger-ui.html - -## API列表详情 - -* 1、账号注册 -> POST http://loclhost:8080/susu/user/to_register -- 参数:username(用户名) -- 参数:password(密码) -- 前端Tip:传值判断,参数均不能为空,密码限制在前端做判断 - - -## 返回码与信息值 - -| 返回码 | 信息内容 | 备注 | -|------|---------|------| -| 200 | 成功 | | -| 555 | 参数错误| | -| 556 | 用户名存在| | - -## 提示 - -仅API列表详情中的API处于可用状态,其余API请勿使用,暂未基本完成,使用请详看文档 \ No newline at end of file