diff --git a/README.md b/README.md index c98ce64..f230b7c 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,54 @@ -# InChat(当前版本1.6.0) +## paho-mqtt -## 分支介绍 im-api +本Demo是小程序端的Iot案例简单实现。 -腾讯IM(云通信)后端模仿项目,均以API形式对接,如果有前端想要对接的可以运行本分支,本分支预计终版为一个单服务并发30万用户的IM后台项目 +### 服务端配置 -## 简介 +首先是配置修改,你可以在本分支的yml配置文件进行mqtt的配置,核心的参数是: ->(InChat)Iot Netty Chat +> ssl: false # 使用ssl加密 +> +> protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) -仿微信聊天应用,一步一步更新,基于SpringBoot-WebSocket通用框架,结合Netty进行聊天社交,并记录聊天日志, -异步存储,前端暂用SUI Mobile,添加实现TCP/IP后端通信端口(MQTT协议、可实时与单片机等TCP硬件通信)、加入图片处理流, -聊天实现文字与图片发送功能、API调用Netty长链接执行发送消息(在线数、用户列表) +本项目使用的paho.js的mqtt连接形式,所以protocol要选择MQTT_WS_PAHO。项目目前是未加密的,启动ssl本案例暂时不能通讯。 +默认直接启动项目就好。 -## 基本架构图(1.5.2版) +> 项目启动后的地址 :ws://192.168.1.121:8094/mqtt -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/nettychat/ggg1.png) +ws、与后缀mqtt是com.myself.nettychat.bootstrap.AbstractBootstrapServer.java中的配置 -## 功能 +### 小程序配置 ->实时聊天 ->异步CRUD处理消息日志 ->获取聊天历史 ->用户登录、记录登录用户聊天历史 ->防止二次登录 ->SUI Mobile仿微信样式 ->TCP/IP软硬件通信(8092) ->MQTT协议下的Iot物联网通信(8094) ->图片发送聊天功能 ->API调用Netty长链接执行发送消息(在线用户数、用户列表) ->下版(1.7.0):好友功能等 +你需要小程序开发者工具,并默认认定你是具备基本的小程序开发经验的开发者,这里省略部分的基本配置,你只需要将本分支中**wechat-client**文件夹中的文件完全复制到你新建的小程序项目即可,调试情况下无需AppID +你需要注意的是pages/connect/connect.js中的第78行 -## 版本迭代介绍 - -* 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文档 - -## 效果图 +```javascript +var client = new MQTT.Client("ws://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); +``` -![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) +这里就是小程序的连接地址配置,默认和项目启动的一致,你需要在小程序的连接页面填写你的 +**IP:端口** -## 预留BUG +然后就连接成功了,接着你可以在subscribe页面订阅一个主题,本Demo是订阅TEST。 -``` -io.netty.handler.codec.CorruptedFrameException: Max frame length of 65536 has been exceeded. -图片过大,需要在前端做图片上传压缩 +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151707.png) -Uncaught TypeError: msg.substring is not a function at WebSocket.socket.onmessage (newChat.js:38) -前端代码的一点问题,不影响项目正常运行 +### Java模拟MQtt客户端 -java.io.IOException: 远程主机强迫关闭了一个现有的连接。 -TCP客户端连接主动关闭,不影响,良性报错 -``` +运行test中的com.myself.nettychat.MqttPublishSample,你需要修改成本机的配置,类似连接地址等 -## 下载地址 +> String broker = "ws://192.168.1.121:8094/mqtt";//地址 -下载地址:https://github.com/UncleCatMySelf/SBToNettyChat/releases +需要注意的是,你的topic也要与小程序订阅的主题一致哦! -## 交流与提问 +运行测试用例,模拟硬件发送信息 -提问与Bug上报:https://github.com/UncleCatMySelf/SBToNettyChat/issues +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151715.png) +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151719.png) -QQ群:628793702(仅供交流,不提供问题解答) -## 关于作者 +### 测试 -个人公众号:UncleCatMySelf +回到小程序的message页面,你可以看到接收到了消息 -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/%E5%85%AC%E4%BC%97%E5%8F%B7.png) +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151723.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 08b5d51..0000000 Binary files a/h5/logoSmall.png and /dev/null differ 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 9cc84ea..0000000 Binary files a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar and /dev/null differ 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/pom.xml b/pom.xml index 458d0cf..9972826 100644 --- a/pom.xml +++ b/pom.xml @@ -29,9 +29,25 @@ org.springframework.boot spring-boot-starter-tomcat + - org.springframework.boot - spring-boot-starter-data-jpa + org.springframework + spring-aop + + + org.springframework + spring-aspects + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.0 + + + + org.springframework + spring-context mysql diff --git a/sql/nettychat.sql b/sql/nettychat.sql deleted file mode 100644 index 452c711..0000000 --- a/sql/nettychat.sql +++ /dev/null @@ -1,71 +0,0 @@ -/* -Navicat MySQL Data Transfer - -Source Server : mypc -Source Server Version : 50717 -Source Host : localhost:3306 -Source Database : nettychat - -Target Server Type : MYSQL -Target Server Version : 50717 -File Encoding : 65001 - -Date: 2018-08-23 10:32:48 -*/ - -SET FOREIGN_KEY_CHECKS=0; - --- ---------------------------- --- Table structure for user --- ---------------------------- -DROP TABLE IF EXISTS `user`; -CREATE TABLE `user` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_name` varchar(255) DEFAULT NULL, - `pass_word` varchar(255) DEFAULT NULL, - `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; - --- ---------------------------- --- Records of user --- ---------------------------- -INSERT INTO `user` VALUES ('2', 'Myself', '123456', '2018-08-14 19:47:49', '2018-08-14 19:47:49'); -INSERT INTO `user` VALUES ('3', 'Chen', '123456abc', '2018-08-20 16:31:49', '2018-08-20 16:31:49'); - --- ---------------------------- --- Table structure for user_msg --- ---------------------------- -DROP TABLE IF EXISTS `user_msg`; -CREATE TABLE `user_msg` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) DEFAULT NULL, - `msg` varchar(255) DEFAULT NULL, - `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4; - --- ---------------------------- --- Records of user_msg --- ---------------------------- -INSERT INTO `user_msg` VALUES ('8', 'Myself', '你好呀', '2018-08-20 17:26:58', '2018-08-20 17:26:58'); -INSERT INTO `user_msg` VALUES ('9', 'Myself', '你是谁?', '2018-08-20 17:27:13', '2018-08-20 17:27:13'); -INSERT INTO `user_msg` VALUES ('10', 'Myself', '在吗?', '2018-08-21 17:54:12', '2018-08-21 17:54:12'); -INSERT INTO `user_msg` VALUES ('11', 'Chen', '嗯呢', '2018-08-21 17:54:12', '2018-08-21 17:54:12'); -INSERT INTO `user_msg` VALUES ('13', 'Myself', 'yo', '2018-08-21 18:01:26', '2018-08-21 18:01:26'); -INSERT INTO `user_msg` VALUES ('14', 'Myself', '你好', '2018-08-22 16:24:22', '2018-08-22 16:24:22'); -INSERT INTO `user_msg` VALUES ('30', 'Myself', '你好呀!', '2018-08-22 17:03:42', '2018-08-22 17:03:42'); -INSERT INTO `user_msg` VALUES ('31', 'Myself', '我很好!', '2018-08-22 17:03:42', '2018-08-22 17:03:42'); -INSERT INTO `user_msg` VALUES ('32', 'Myself', '哈哈哈哈', '2018-08-22 17:11:56', '2018-08-22 17:11:56'); -INSERT INTO `user_msg` VALUES ('33', 'Myself', '哇哈哈哈哈', '2018-08-22 17:11:56', '2018-08-22 17:11:56'); -INSERT INTO `user_msg` VALUES ('34', 'Myself', 'asdf', '2018-08-22 17:22:19', '2018-08-22 17:22:19'); -INSERT INTO `user_msg` VALUES ('35', 'Myself', '厉害 厉害', '2018-08-22 17:23:20', '2018-08-22 17:23:20'); -INSERT INTO `user_msg` VALUES ('36', 'Myself', '哈哈', '2018-08-22 17:23:43', '2018-08-22 17:23:43'); -INSERT INTO `user_msg` VALUES ('37', 'Myself', '你好!', '2018-08-23 10:19:14', '2018-08-23 10:19:14'); -INSERT INTO `user_msg` VALUES ('38', 'Chen', '收到。', '2018-08-23 10:19:15', '2018-08-23 10:19:15'); -INSERT INTO `user_msg` VALUES ('39', 'Myself', '前端框架用Vue?', '2018-08-23 10:19:15', '2018-08-23 10:19:15'); -INSERT INTO `user_msg` VALUES ('40', 'Chen', '可以试试', '2018-08-23 10:19:15', '2018-08-23 10:19:15'); -INSERT INTO `user_msg` VALUES ('41', 'Myself', '下版再加一些新的功能', '2018-08-23 10:19:15', '2018-08-23 10:19:15'); -INSERT INTO `user_msg` VALUES ('42', 'Chen', 'okay', '2018-08-23 10:19:15', '2018-08-23 10:19:15'); diff --git a/src/main/java/com/myself/nettychat/NettychatApplication.java b/src/main/java/com/myself/nettychat/NettychatApplication.java index 9c679bd..b8659c1 100644 --- a/src/main/java/com/myself/nettychat/NettychatApplication.java +++ b/src/main/java/com/myself/nettychat/NettychatApplication.java @@ -1,11 +1,8 @@ package com.myself.nettychat; -import com.myself.nettychat.config.NettyConfig; -import com.myself.nettychat.config.NettyTcpConfig; -import com.myself.nettychat.config.TCPServer; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableScheduling; import springfox.documentation.swagger2.annotations.EnableSwagger2; @@ -17,33 +14,7 @@ public class NettychatApplication { public static void main(String[] args) throws Exception{ -// SpringApplication.run(NettychatApplication.class, args); - ConfigurableApplicationContext context = SpringApplication.run(NettychatApplication.class, args); - NettyConfig nettyConfig = context.getBean(NettyConfig.class); - NettyTcpConfig nettyTcpConfig = context.getBean(NettyTcpConfig.class); - TCPServer tcpServer = context.getBean(TCPServer.class); - new Thread(new Runnable() { - @Override - public void run() { - try { - System.out.println("Web端Netty通信服务端启动成功!端口:8090"); - tcpServer.startWeb(); - }catch (Exception e){ - e.printStackTrace(); - } - } - }).start(); - new Thread(new Runnable() { - @Override - public void run() { - try { - System.out.println("TCP端Netty通信服务端启动成功!端口:8092"); - tcpServer.startTcp(); - }catch (Exception e){ - e.printStackTrace(); - } - } - }).start(); + SpringApplication.run(NettychatApplication.class, args); } } diff --git a/src/main/java/com/myself/nettychat/common/utils/ResultVOUtil.java b/src/main/java/com/myself/nettychat/common/utils/ResultVOUtil.java deleted file mode 100644 index b2b678d..0000000 --- a/src/main/java/com/myself/nettychat/common/utils/ResultVOUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.myself.nettychat.common.utils; - -import com.myself.nettychat.vo.ResultVo; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 20:59 2018\10\7 0007 - */ -public class ResultVOUtil { - public static ResultVo success(Object object){ - ResultVo resultVO = new ResultVo(); - resultVO.setData(object); - resultVO.setCode(200); - resultVO.setMsg("成功"); - return resultVO; - } - - public static ResultVo success(){ - return success(null); - } - - public static ResultVo error(Integer code, String msg){ - ResultVo resultVO = new ResultVo(); - resultVO.setCode(code); - resultVO.setMsg(msg); - return resultVO; - } -} diff --git a/src/main/java/com/myself/nettychat/config/NettyConfig.java b/src/main/java/com/myself/nettychat/config/NettyConfig.java deleted file mode 100644 index 2648087..0000000 --- a/src/main/java/com/myself/nettychat/config/NettyConfig.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.myself.nettychat.config; - -import com.myself.nettychat.common.properties.InitNetty; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 11:00 2018\8\14 0014 - */ -@Component -public class NettyConfig { - - @Autowired - private InitNetty nettyAccountConfig; - - @Bean(name = "bossGroup", destroyMethod = "shutdownGracefully") - public NioEventLoopGroup bossGroup(){ - return new NioEventLoopGroup(nettyAccountConfig.getBossThread()); - } - - @Bean(name = "workerGroup", destroyMethod = "shutdownGracefully") - public NioEventLoopGroup workerGroup(){ - return new NioEventLoopGroup(nettyAccountConfig.getWorkerThread()); - } - - @Bean(name = "webSocketAddress") - public InetSocketAddress tcpPost(){ - return new InetSocketAddress(nettyAccountConfig.getWebport()); - } - - @Bean(name = "tcpChannelOptions") - public Map, Object> tcpChannelOptions(){ - Map, Object> options = new HashMap, Object>(); - options.put(ChannelOption.TCP_NODELAY,nettyAccountConfig.isNodelay()); - options.put(ChannelOption.SO_KEEPALIVE, nettyAccountConfig.isKeepalive()); - options.put(ChannelOption.SO_BACKLOG, nettyAccountConfig.getBacklog()); - options.put(ChannelOption.SO_REUSEADDR,nettyAccountConfig.isReuseaddr()); - return options; - } - - @Autowired - @Qualifier("somethingChannelInitializer") - private NettyWebSocketChannelInitializer nettyWebSocketChannelInitializer; - - @Bean(name = "serverBootstrap") - public ServerBootstrap bootstrap(){ - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup(), workerGroup()) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(nettyWebSocketChannelInitializer); - Map, Object> tcpChannelOptions = tcpChannelOptions(); - Set> keySet = tcpChannelOptions.keySet(); - for (@SuppressWarnings("rawtypes") ChannelOption option : keySet) { - b.option(option, tcpChannelOptions.get(option)); - } - return b; - } -} diff --git a/src/main/java/com/myself/nettychat/config/NettyTcpChannelInitializer.java b/src/main/java/com/myself/nettychat/config/NettyTcpChannelInitializer.java deleted file mode 100644 index ceb8ed9..0000000 --- a/src/main/java/com/myself/nettychat/config/NettyTcpChannelInitializer.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.myself.nettychat.config; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringDecoder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 19:26 2018\9\20 0020 - */ -@Component -@Qualifier("tcpChannelInitializer") -public class NettyTcpChannelInitializer extends ChannelInitializer { - - @Autowired - @Qualifier("tcpServerHandler") - private TCPServerHandler tcpServerHandler; - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(new LineBasedFrameDecoder(1024)); - pipeline.addLast(new StringDecoder()); - pipeline.addLast(tcpServerHandler); - } - -} \ No newline at end of file diff --git a/src/main/java/com/myself/nettychat/config/NettyTcpConfig.java b/src/main/java/com/myself/nettychat/config/NettyTcpConfig.java deleted file mode 100644 index f6ce2d2..0000000 --- a/src/main/java/com/myself/nettychat/config/NettyTcpConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.myself.nettychat.config; - -import com.myself.nettychat.common.properties.InitNetty; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 19:27 2018\9\20 0020 - */ -@Component -public class NettyTcpConfig { - - @Autowired - private InitNetty nettyAccountConfig; - - @Bean(name = "bossGroup", destroyMethod = "shutdownGracefully") - public NioEventLoopGroup bossGroup(){ - return new NioEventLoopGroup(nettyAccountConfig.getBossThread()); - } - - @Bean(name = "workerGroup", destroyMethod = "shutdownGracefully") - public NioEventLoopGroup workerGroup(){ - return new NioEventLoopGroup(nettyAccountConfig.getWorkerThread()); - } - - @Bean(name = "tcpSocketAddress") - public InetSocketAddress tcpPost(){ - return new InetSocketAddress(nettyAccountConfig.getTcpport()); - } - - @Bean(name = "tcpChannelOptions") - public Map, Object> tcpChannelOptions(){ - Map, Object> options = new HashMap<>(); - options.put(ChannelOption.TCP_NODELAY,nettyAccountConfig.isNodelay()); - options.put(ChannelOption.SO_KEEPALIVE, nettyAccountConfig.isKeepalive()); - options.put(ChannelOption.SO_BACKLOG, nettyAccountConfig.getBacklog()); - options.put(ChannelOption.SO_REUSEADDR,nettyAccountConfig.isReuseaddr()); - return options; - } - - @Autowired - @Qualifier("tcpChannelInitializer") - private NettyTcpChannelInitializer nettyTcpChannelInitializer; - - @Bean(name = "tcpServerBootstrap") - public ServerBootstrap bootstrap(){ - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup(), workerGroup()) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(nettyTcpChannelInitializer); - Map, Object> tcpChannelOptions = tcpChannelOptions(); - Set> keySet = tcpChannelOptions.keySet(); - for (@SuppressWarnings("rawtypes") ChannelOption option : keySet) { - b.option(option, tcpChannelOptions.get(option)); - } - return b; - } - -} diff --git a/src/main/java/com/myself/nettychat/config/NettyWebSocketChannelInitializer.java b/src/main/java/com/myself/nettychat/config/NettyWebSocketChannelInitializer.java deleted file mode 100644 index a2f2eb4..0000000 --- a/src/main/java/com/myself/nettychat/config/NettyWebSocketChannelInitializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.myself.nettychat.config; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.handler.stream.ChunkedWriteHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 11:00 2018\8\14 0014 - */ -@Component -@Qualifier("somethingChannelInitializer") -public class NettyWebSocketChannelInitializer extends ChannelInitializer { - - @Autowired - private TextWebSocketFrameHandler textWebSocketFrameHandler; - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - - pipeline.addLast(new HttpServerCodec()); - pipeline.addLast(new HttpObjectAggregator(65536)); - pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); - pipeline.addLast(textWebSocketFrameHandler); //这里不能使用new,不然在handler中不能注入依赖 - - } - -} diff --git a/src/main/java/com/myself/nettychat/config/TCPServer.java b/src/main/java/com/myself/nettychat/config/TCPServer.java deleted file mode 100644 index ae478f4..0000000 --- a/src/main/java/com/myself/nettychat/config/TCPServer.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.myself.nettychat.config; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import lombok.Data; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import javax.annotation.PreDestroy; -import java.net.InetSocketAddress; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 11:00 2018\8\14 0014 - */ -@Data -@Component -public class TCPServer { - - @Autowired - @Qualifier("serverBootstrap") - private ServerBootstrap serverBootstrap; - - @Autowired - @Qualifier("tcpServerBootstrap") - private ServerBootstrap tcpServerBootstrap; - - @Autowired - @Qualifier("webSocketAddress") - private InetSocketAddress webPort; - - @Autowired - @Qualifier("tcpSocketAddress") - private InetSocketAddress tcpTcpPort; - - private Channel serverChannel; - - private Channel tcpServerChannel; - - public void startWeb() throws Exception { - serverChannel = serverBootstrap.bind(webPort).sync().channel().closeFuture().sync().channel(); - } - - public void startTcp() throws Exception { - tcpServerChannel = tcpServerBootstrap.bind(tcpTcpPort).sync().channel().closeFuture().sync().channel(); - } - - @PreDestroy - public void stop() throws Exception { - serverChannel.close(); - serverChannel.parent().close(); - tcpServerChannel.close(); - tcpServerChannel.parent().close(); - } -} diff --git a/src/main/java/com/myself/nettychat/config/TCPServerHandler.java b/src/main/java/com/myself/nettychat/config/TCPServerHandler.java deleted file mode 100644 index f5afdfc..0000000 --- a/src/main/java/com/myself/nettychat/config/TCPServerHandler.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.myself.nettychat.config; - -import com.myself.nettychat.common.utils.*; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.GlobalEventExecutor; -import io.netty.util.concurrent.ScheduledFuture; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 19:29 2018\9\20 0020 - */ -@Component -@Qualifier("tcpServerHandler") -@ChannelHandler.Sharable -public class TCPServerHandler extends ChannelInboundHandlerAdapter { - - static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - String ChannelID = null; - try{ - String data = (String)msg; - System.out.println(data); - if (DataValida.ValidateHeadAndFeet(data)){ - data = DataResction.ResctionHeadAndFeet(data); - if (DataValida.ValidateCRCCode(DataResction.ResctionData(data),DataResction.ResctionCRCCode(data))){ - data = DataResction.ResctionData(data); - ChannelID = DataResction.ResctionID(data); - System.out.println("Const.hasChannelID(ChannelID):"+ Const.hasChannelID(ChannelID)); - //更换连接ID - if (!Const.hasChannelID(ChannelID)){ - String realChannelID = Const.isChannel(ctx.channel()); - System.out.println(realChannelID); - Const.ChangeClientId(realChannelID,ChannelID); - } - //检查重复链接ID 不同实例 切换实例 - if(Const.hasChannelID(ChannelID)){ - Const.changeChannel(ChannelID,ctx.channel()); - } - data = DataResction.ResctionDataNoID(data); - String type = DataResction.ResctionType(data); - String RealData = DataResction.ResctionRealData(data); - //数据类型判断 - switch (type){ - case "s": - //控制类型 - futureByController(ctx,RealData,ChannelID); - break; - case "g": - //经纬度传输 - futureByLoLa(ctx,RealData,ChannelID); - break; - case "v": - //设备电量信息 - RealData = DataResction.ResctionPower(RealData); - futureByCharge(ctx,RealData,ChannelID); - break; - case "p": - //设备检测物体信息 - futureByPStates(ctx,RealData,ChannelID); - break; - case "r": - //设备开关异常 - futureByException(ctx,RealData,ChannelID); - break; - case "j": - //客户端执行结果 - futureByChlientResult(ctx,RealData,ChannelID); - break; - case "t": - futureBYTesting(ctx,RealData,ChannelID); - break; - default: - //其他类型 - ctx.writeAndFlush(CallBackMessage.sendString( - CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.ERROR))); - break; - } - } else { - ctx.writeAndFlush(CallBackMessage.ERROR.duplicate()); - ctx.close(); - } - } else { - ctx.writeAndFlush(CallBackMessage.ERROR.duplicate()); - ctx.close(); - } - }finally { - ReferenceCountUtil.release(msg); - } - } - - - /** - * 客户端执行开锁测试执行方法 - * @param ctx - * @param realData - * @param ChannelID - */ - private void futureBYTesting(ChannelHandlerContext ctx, String realData, String ChannelID) { - Set ids = Const.getIdList(); - System.out.println("测试广播事件执行"); - for (String item : ids){ - SendUtil sendUtil = new SendUtil(); - Channel channel = Const.get(item); - if (channel != null){ - sendUtil.sendAll(realData,channel,item,Const.RESULT_TEXT); - } - } - } - - /** - * 客户端执行结果执行方法 - * @param ctx - * @param realData - */ - private void futureByChlientResult(ChannelHandlerContext ctx, String realData,String ChannelID) { - //测试方法 - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------客户端执行结果"); - } - },0, TimeUnit.SECONDS); - ctx.writeAndFlush(CallBackMessage.sendString( - CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.SUCCESS))); - } - - /** - * 开关异常执行方法 - * @param ctx - * @param realData - */ - private void futureByException(ChannelHandlerContext ctx,final String realData,final String ChannelID) { - //测试方法 - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------开关异常"); - } - },0,TimeUnit.SECONDS); - ctx.writeAndFlush(CallBackMessage.sendString( - CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.SUCCESS))); - } - - /** - * 设备电量执行方法 - * @param ctx - * @param realData - */ - private void futureByCharge(ChannelHandlerContext ctx,final String realData,final String ChannelID) { - //测试方法 - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------设备电量"); - } - },0,TimeUnit.SECONDS); - ctx.writeAndFlush(CallBackMessage.sendString( - CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.SUCCESS))); - } - - /** - * 经纬度传输执行方法 - * @param ctx - * @param realData - */ - private void futureByLoLa(ChannelHandlerContext ctx, String realData, final String ChannelID) { - final String Longitude = DataResction.ResctionLongitude(realData); - final String Latitude = DataResction.ResctionLatitude(realData); - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------经纬度传输"); - } - },0,TimeUnit.SECONDS); - ctx.writeAndFlush(CallBackMessage.sendString( - CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.SUCCESS))); - } - - /** - * 控制类执行方法 - * @param ctx - * @param realData - */ - private void futureByController(ChannelHandlerContext ctx,final String realData, final String ChannelID) { - //SQL入库操作 - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------控制类型"); -// - } - },0,TimeUnit.SECONDS); -// ctx.writeAndFlush(CallBackMessage.sendString( -// CRC16MySelf.getAllString(ChannelID,Const.RESULT_TYPE,Const.SUCCESS))); - ctx.writeAndFlush(CallBackMessage.Check1_test.duplicate()); - } - - private void futureByPStates(ChannelHandlerContext ctx,final String realData,final String channelID) { - System.out.println("检测物体事件执行"); - ScheduledFuture future = ctx.channel().eventLoop().schedule( - new Runnable() { - @Override - public void run() { - System.out.println("-------尝试执行SQL操作--------物体检测类型"); - } - },0,TimeUnit.SECONDS); - } - - private String getUpdateKey(String channelID, String pstates, String realData) { - Integer openid = null; - for (int i = 0; i < realData.length(); i++){ - if(pstates.charAt(i) != realData.charAt(i)){ - openid = i; - break; - } - } - return channelID + "_" + openid; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - System.out.println(cause.getMessage()); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - Const.add(String.valueOf(UUID.randomUUID()),ctx.channel()); - channels.add(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - System.out.println("Disconnected client " + ctx.channel().remoteAddress()); - Const.remove(ctx.channel()); - } -} - diff --git a/src/main/java/com/myself/nettychat/config/TextWebSocketFrameHandler.java b/src/main/java/com/myself/nettychat/config/TextWebSocketFrameHandler.java deleted file mode 100644 index a47c5aa..0000000 --- a/src/main/java/com/myself/nettychat/config/TextWebSocketFrameHandler.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.myself.nettychat.config; - -import com.myself.nettychat.task.MsgAsyncTesk; -import com.myself.nettychat.constont.LikeRedisTemplate; -import com.myself.nettychat.constont.LikeSomeCacheTemplate; -import com.myself.nettychat.common.utils.StringUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.util.concurrent.GlobalEventExecutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import java.io.FileOutputStream; - - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 11:01 2018\8\14 0014 - */ -@Component -@Qualifier("textWebSocketFrameHandler") -@ChannelHandler.Sharable -public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler{ - - public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - @Autowired - private LikeRedisTemplate redisTemplate; - @Autowired - private LikeSomeCacheTemplate cacheTemplate; - @Autowired - private MsgAsyncTesk msgAsyncTesk; - - @Override - protected void channelRead0(ChannelHandlerContext ctx, - Object msg) throws Exception { - if(msg instanceof TextWebSocketFrame){ - textWebSocketFrame(ctx, (TextWebSocketFrame) msg); - }else if(msg instanceof WebSocketFrame){ //websocket帧类型 已连接 - handleWebSocketFrame(ctx, (WebSocketFrame) msg); - } - } - - private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - if(frame instanceof BinaryWebSocketFrame){ - //返回客户端 - BinaryWebSocketFrame imgBack= (BinaryWebSocketFrame) frame.copy(); - for (Channel channel : channels){ - channel.writeAndFlush(imgBack.retain()); - } - //保存服务器 - BinaryWebSocketFrame img= (BinaryWebSocketFrame) frame; - ByteBuf byteBuf=img.content(); - try { - FileOutputStream outputStream=new FileOutputStream("D:\\a.jpg"); - byteBuf.readBytes(outputStream,byteBuf.capacity()); - byteBuf.clear(); - }catch (Exception e){ - e.printStackTrace(); - } - } - } - - private void textWebSocketFrame(ChannelHandlerContext ctx, TextWebSocketFrame msg) { - Channel incoming = ctx.channel(); - String rName = StringUtil.getName(msg.text()); - String rMsg = StringUtil.getMsg(msg.text()); - if (rMsg.equals("")){ - return; - } - //用户登录判断 - if (redisTemplate.check(incoming.id(),rName)){ - //临时存储聊天数据 - cacheTemplate.save(rName,rMsg); - //存储随机链接ID与对应登录用户名 - redisTemplate.save(incoming.id(),rName); - //存储登录用户名与链接实例,方便API调用链接实例 - redisTemplate.saveChannel(rName,incoming); - }else{ - incoming.writeAndFlush(new TextWebSocketFrame("存在二次登陆,系统已为你自动断开本次链接")); - channels.remove(ctx.channel()); - ctx.close(); - return; - } - for (Channel channel : channels) { - //将当前每个聊天内容进行存储 - if (channel != incoming){ - channel.writeAndFlush(new TextWebSocketFrame( "[" + rName + "]" + rMsg)); - } else { - channel.writeAndFlush(new TextWebSocketFrame(rMsg + "[" + rName + "]" )); - } - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - System.out.println(ctx.channel().remoteAddress()); - channels.add(ctx.channel()); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - //删除存储池对应实例 - String name = (String) redisTemplate.getName(ctx.channel().id()); - redisTemplate.deleteChannel(name); - //删除默认存储对应关系 - redisTemplate.delete(ctx.channel().id()); - channels.remove(ctx.channel()); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - //在线 - } - - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - //掉线 - msgAsyncTesk.saveChatMsgTask(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - throws Exception { - //异常 - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/src/main/java/com/myself/nettychat/constont/LikeSomeCacheTemplate.java b/src/main/java/com/myself/nettychat/constont/LikeSomeCacheTemplate.java deleted file mode 100644 index e1ca623..0000000 --- a/src/main/java/com/myself/nettychat/constont/LikeSomeCacheTemplate.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.myself.nettychat.constont; - -import com.myself.nettychat.dataobject.UserMsg; -import org.omg.CORBA.OBJ_ADAPTER; -import org.springframework.stereotype.Component; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 聊天内容临时存储 - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 13:38 2018\8\14 0014 - */ -@Component -public class LikeSomeCacheTemplate { - - private List SomeCache = new LinkedList<>(); - - public void save(Object user,Object msg){ - UserMsg userMsg = new UserMsg(); - userMsg.setName(String.valueOf(user)); - userMsg.setMsg(String.valueOf(msg)); - SomeCache.add(userMsg); - } - - public List cloneCacheMap(){ - return SomeCache; - } - - public void clearCacheMap(){ - SomeCache.clear(); - } -} diff --git a/src/main/java/com/myself/nettychat/controller/NCBackController.java b/src/main/java/com/myself/nettychat/controller/NCBackController.java deleted file mode 100644 index 579cd9e..0000000 --- a/src/main/java/com/myself/nettychat/controller/NCBackController.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.myself.nettychat.controller; - -import com.myself.nettychat.common.utils.ResultVOUtil; -import com.myself.nettychat.common.utils.SendUtil; -import com.myself.nettychat.constont.LikeRedisTemplate; -import com.myself.nettychat.vo.ResultVo; -import io.netty.channel.Channel; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 20:55 2018\10\7 0007 - */ -@RestController -@RequestMapping("/back") -public class NCBackController { - - @Autowired - private LikeRedisTemplate redisTemplate; - - /** - * 获取在线用户数 - * @return {@link ResultVo} - */ - @GetMapping("/size") - public ResultVo getSize(){ - return ResultVOUtil.success(redisTemplate.getSize()); - } - - /** - * 获取在线用户列表 - * @return {@link ResultVo} - */ - @GetMapping("/online") - public ResultVo getOnline(){ - return ResultVOUtil.success(redisTemplate.getOnline()); - } - - /** - * API调用向在线用户发送消息 - * @param name 用户名 - * @param msg 消息 - * @return {@link ResultVo} - */ - @PostMapping("/send") - public ResultVo send(@RequestParam String name,@RequestParam String msg){ - Channel channel = (Channel) redisTemplate.getChannel(name); - if (channel == null){ - return ResultVOUtil.error(555,"当前用户连接已断开"); - } - String result = SendUtil.sendTest(msg,channel); - return ResultVOUtil.success(result); - } - -} 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/NcChatController.java b/src/main/java/com/myself/nettychat/controller/NcChatController.java deleted file mode 100644 index 3a7d79b..0000000 --- a/src/main/java/com/myself/nettychat/controller/NcChatController.java +++ /dev/null @@ -1,76 +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.dataobject.UserMsg; -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 org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -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.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 14:32 2018\8\14 0014 - */ -@Controller -@RequestMapping("/chat") -public class NcChatController { - - @Autowired - private UserMsgRepository userMsgRepository; - - @Autowired - private UserService userService; - - @GetMapping("/netty") - public ModelAndView netty(@RequestParam(value = "page",defaultValue = "1") Integer page, - @RequestParam(value = "size",defaultValue = "10") Integer size, - 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,map); - } - Integer userId = (Integer) TokenStore.get(cookie.getValue()); - if (userId == null){ - map.put("msg","用户信息不存在"); - return new ModelAndView(H5Constant.LOGIN,map); - } - User user = userService.findOne(userId); - 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("userName",user.getUserName()); - map.put("userMsgList",userMsgList); - return new ModelAndView(H5Constant.ALLCHAT,map); - } - -} diff --git a/src/main/java/com/myself/nettychat/controller/NcLoginController.java b/src/main/java/com/myself/nettychat/controller/NcLoginController.java deleted file mode 100644 index 2db26fb..0000000 --- a/src/main/java/com/myself/nettychat/controller/NcLoginController.java +++ /dev/null @@ -1,145 +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.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 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.servlet.ModelAndView; - -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 16:01 2018\8\18 0018 - */ -@Controller -@RequestMapping("/admin") -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); - } - - /** - * 注册页面 - * @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); - } - List userList = userService.findAll(); - for (User item:userList){ - if (item.getUserName().equals(form.getFUserName())){ - map.put("msg","用户名已存在,请重新填写唯一用户名"); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - } - User user = new User(); - BeanUtils.copyProperties(form,user); - userService.save(user); - map.put("userName",user.getUserName()); - map.put("passWord",user.getPassWord()); - return new ModelAndView(H5Constant.LOGIN_SUI,map); - } - - /** - * 登录判断 - * @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); - } - } - -} diff --git a/src/main/java/com/myself/nettychat/dataobject/User.java b/src/main/java/com/myself/nettychat/dataobject/User.java deleted file mode 100644 index d00bbad..0000000 --- a/src/main/java/com/myself/nettychat/dataobject/User.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.myself.nettychat.dataobject; - -import lombok.Data; -import org.hibernate.annotations.DynamicUpdate; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import java.io.Serializable; -import java.util.Date; - -/** - * 主用户信息表 - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 15:44 2018\8\13 0013 - */ -@Data -@Entity -@DynamicUpdate -public class User implements Serializable { - - private static final long serialVersionUID = 8143981246513357880L; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - private String userName; - - private String passWord; - - private Date createTime; - - private Date updateTime; - -} diff --git a/src/main/java/com/myself/nettychat/dataobject/UserMsg.java b/src/main/java/com/myself/nettychat/dataobject/UserMsg.java deleted file mode 100644 index a651839..0000000 --- a/src/main/java/com/myself/nettychat/dataobject/UserMsg.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.myself.nettychat.dataobject; - -import lombok.Data; -import org.hibernate.annotations.DynamicUpdate; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import java.io.Serializable; -import java.util.Date; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 14:03 2018\8\14 0014 - */ -@Data -@Entity -@DynamicUpdate -public class UserMsg implements Serializable { - - private static final long serialVersionUID = 4133316147283239759L; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - private String name; - - private String msg; - - private Date createTime; - - private Date updateTime; - -} diff --git a/src/main/java/com/myself/nettychat/form/LoginForm.java b/src/main/java/com/myself/nettychat/form/LoginForm.java deleted file mode 100644 index 2792a74..0000000 --- a/src/main/java/com/myself/nettychat/form/LoginForm.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.myself.nettychat.form; - -import lombok.Data; - -import javax.validation.constraints.NotEmpty; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 16:02 2018\8\13 0013 - */ -@Data -public class LoginForm { - @NotEmpty(message = "用户名不能为空") - private String fUserName; - @NotEmpty(message = "密码不能为空") - private String fPassWord; - -} diff --git a/src/main/java/com/myself/nettychat/repository/UserMsgRepository.java b/src/main/java/com/myself/nettychat/repository/UserMsgRepository.java deleted file mode 100644 index 6e98ace..0000000 --- a/src/main/java/com/myself/nettychat/repository/UserMsgRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.myself.nettychat.repository; - -import com.myself.nettychat.dataobject.UserMsg; -import org.springframework.data.jpa.repository.JpaRepository; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 14:06 2018\8\14 0014 - */ -public interface UserMsgRepository extends JpaRepository { -} diff --git a/src/main/java/com/myself/nettychat/repository/UserRepository.java b/src/main/java/com/myself/nettychat/repository/UserRepository.java deleted file mode 100644 index be6c18a..0000000 --- a/src/main/java/com/myself/nettychat/repository/UserRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.myself.nettychat.repository; - -import com.myself.nettychat.dataobject.User; -import org.springframework.data.jpa.repository.JpaRepository; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 15:47 2018\8\13 0013 - */ -public interface UserRepository extends JpaRepository { - - User findByUserName(String userName); - -} diff --git a/src/main/java/com/myself/nettychat/service/UserService.java b/src/main/java/com/myself/nettychat/service/UserService.java deleted file mode 100644 index d106993..0000000 --- a/src/main/java/com/myself/nettychat/service/UserService.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.myself.nettychat.service; - -import com.myself.nettychat.dataobject.User; - -import java.util.List; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 15:52 2018\8\18 0018 - */ -public interface UserService { - - User findOne(Integer id); - - User save(User user); - - User findByUserName(String userName); - - List findAll(); -} diff --git a/src/main/java/com/myself/nettychat/service/impl/UserServiceImpl.java b/src/main/java/com/myself/nettychat/service/impl/UserServiceImpl.java deleted file mode 100644 index 951cc04..0000000 --- a/src/main/java/com/myself/nettychat/service/impl/UserServiceImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.myself.nettychat.service.impl; - -import com.myself.nettychat.dataobject.User; -import com.myself.nettychat.repository.UserRepository; -import com.myself.nettychat.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 15:52 2018\8\18 0018 - */ -@Service -public class UserServiceImpl implements UserService { - - @Autowired - private UserRepository repository; - - - @Override - public User findOne(Integer id) { - return repository.getOne(id); - } - - @Override - public User save(User user) { - return repository.save(user); - } - - @Override - public User findByUserName(String userName) { - return repository.findByUserName(userName); - } - - @Override - public List findAll() { - return repository.findAll(); - } -} diff --git a/src/main/java/com/myself/nettychat/task/MsgAsyncTesk.java b/src/main/java/com/myself/nettychat/task/MsgAsyncTesk.java deleted file mode 100644 index 445e50b..0000000 --- a/src/main/java/com/myself/nettychat/task/MsgAsyncTesk.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.myself.nettychat.task; - -import com.myself.nettychat.constont.LikeSomeCacheTemplate; -import com.myself.nettychat.dataobject.User; -import com.myself.nettychat.dataobject.UserMsg; -import com.myself.nettychat.repository.UserMsgRepository; -import com.myself.nettychat.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.AsyncResult; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.concurrent.Future; - -/** - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 13:50 2018\8\14 0014 - */ -@Component -public class MsgAsyncTesk { - - @Autowired - private LikeSomeCacheTemplate cacheTemplate; - - @Autowired - private UserMsgRepository userMsgRepository; - - @Autowired - private UserRepository userRepository; - - @Async - public Future saveChatMsgTask() throws Exception{ - - List userMsgList = cacheTemplate.cloneCacheMap(); - for (UserMsg item:userMsgList){ - //保护措施 - User user = userRepository.findByUserName(item.getName()); - if (user != null){ - userMsgRepository.save(item); - } - } - //清空临时缓存 - cacheTemplate.clearCacheMap(); - return new AsyncResult<>(true); - } - -} 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(); - } - } -} diff --git a/src/main/java/com/myself/nettychat/vo/ResultVo.java b/src/main/java/com/myself/nettychat/vo/ResultVo.java deleted file mode 100644 index a83c84e..0000000 --- a/src/main/java/com/myself/nettychat/vo/ResultVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.myself.nettychat.vo; - -import lombok.Data; - -import java.io.Serializable; - -/** - * API 统一返回对象 - * @Author:UncleCatMySelf - * @Email:zhupeijie_java@126.com - * @QQ:1341933031 - * @Date:Created in 20:58 2018\10\7 0007 - */ -@Data -public class ResultVo implements Serializable { - - private static final long serialVersionUID = -1020280450330091843L; - - /** 错误码. */ - private Integer code; - - /** 提示信息. */ - private String msg; - - /** 具体内容. */ - private T data; - -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 77aaf54..a5b6a15 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,21 +1,4 @@ -spring: - datasource: - driver-class-name: com.mysql.jdbc.Driver - username: root - password: root - 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 netty: - webport: 8090 #web聊天室端口 - tcpport: 8092 #tcp协议端口 mqttport: 8094 #mqtt协议端口 bossThread: 1 workerThread: 2 @@ -30,7 +13,7 @@ netty: ssl: false # 使用ssl加密 mqttHander: com.myself.nettychat.bootstrap.handler.DefaultMqttHandler # 默认处理 initalDelay: 10 # mqtts qos1 qos2 消息 重发延迟 - protocol: MQTT # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) + protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) period: 10 # mqtts qos1 qos2 消息 重发周期 jksFile: /securesocket.jks # ssl 加密 jks文件地址 jksStorePassword: mu$tch8ng3 # 读取jks密码 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 0c62304..0000000 Binary files a/src/main/resources/static/image/logoBig.jpg and /dev/null differ diff --git a/src/main/resources/static/image/logoSmall.png b/src/main/resources/static/image/logoSmall.png deleted file mode 100644 index 08b5d51..0000000 Binary files a/src/main/resources/static/image/logoSmall.png and /dev/null differ diff --git a/src/main/resources/static/image/nuandao.png b/src/main/resources/static/image/nuandao.png deleted file mode 100644 index be015d1..0000000 Binary files a/src/main/resources/static/image/nuandao.png and /dev/null differ 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 diff --git a/src/test/java/com/myself/nettychat/MqttPublishSample.java b/src/test/java/com/myself/nettychat/MqttPublishSample.java new file mode 100644 index 0000000..f75e2fc --- /dev/null +++ b/src/test/java/com/myself/nettychat/MqttPublishSample.java @@ -0,0 +1,51 @@ +package com.myself.nettychat; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + + +/** + * 本测试用例仅执行次后断开 + * Created by MySelf on 2018/11/1. + */ +public class MqttPublishSample { + + public static void main(String[] args) { + + String topic = "TEST";//确保小程序订阅的topic和这个一致 + String content = "Message from MqttPublishSample";//发送的消息 + int qos = 1; + String broker = "ws://192.168.1.121:8094/mqtt";//地址 + String clientId = "JavaSample"; + MemoryPersistence persistence = new MemoryPersistence(); + + try { + MqttClient sampleClient = new MqttClient(broker, clientId, persistence); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setCleanSession(true); + System.out.println("Connecting to broker: "+broker); + sampleClient.connect(connOpts); + System.out.println("Connected"); + System.out.println("Publishing message: "+content); + MqttMessage message = new MqttMessage(content.getBytes()); + message.setQos(qos); + sampleClient.publish(topic, message); + System.out.println("Message published"); + sampleClient.disconnect(); + System.out.println("Disconnected"); + System.exit(0); + } catch(MqttException me) { + System.out.println("reason "+me.getReasonCode()); + System.out.println("msg "+me.getMessage()); + System.out.println("loc "+me.getLocalizedMessage()); + System.out.println("cause "+me.getCause()); + System.out.println("excep "+me); + me.printStackTrace(); + } + } + + +} diff --git a/wechat-client/app.js b/wechat-client/app.js new file mode 100644 index 0000000..4111a84 --- /dev/null +++ b/wechat-client/app.js @@ -0,0 +1,50 @@ +//app.js +App({ + onLaunch: function () { + // 展示本地存储能力 + var logs = wx.getStorageSync('logs') || [] + logs.unshift(Date.now()) + wx.setStorageSync('logs', logs) + + // 登录 + wx.login({ + success: res => { + // 发送 res.code 到后台换取 openId, sessionKey, unionId + } + }) + // 获取用户信息 + wx.getSetting({ + success: res => { + if (res.authSetting['scope.userInfo']) { + // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 + wx.getUserInfo({ + success: res => { + // 可以将 res 发送给后台解码出 unionId + this.globalData.userInfo = res.userInfo + + // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 + // 所以此处加入 callback 以防止这种情况 + if (this.userInfoReadyCallback) { + this.userInfoReadyCallback(res) + } + } + }) + } + } + }); + + }, + onShow:function(){ + /*if (this.globalData.mqtt_client != null) { + wx.reLaunch({ + url: '../subscribe/subscribe', + }); + return; + }*/ + }, + globalData: { + userInfo: null, + mqtt_client:null, + messages:null + } +}) \ No newline at end of file diff --git a/wechat-client/app.json b/wechat-client/app.json new file mode 100644 index 0000000..a1bbb98 --- /dev/null +++ b/wechat-client/app.json @@ -0,0 +1,32 @@ +{ + "pages":[ + "pages/connect/connect", + "pages/message/message", + "pages/publish/publish", + "pages/subscribe/subscribe", + "pages/index/index", + "pages/logs/logs" + ], + "window":{ + "backgroundTextStyle":"light", + "navigationBarBackgroundColor": "#353535", + "navigationBarTitleText": "MQTT 客户端", + "navigationBarTextStyle":"white" + }, + "tabBar":{ + "selectedColor": "#e64340", + "backgroundColor":"#f0f0f0", + "list":[{ + "pagePath":"pages/subscribe/subscribe", + "text":"subscribe" + },{ + "pagePath":"pages/publish/publish", + "text":"publish" + }, + { + "pagePath": "pages/message/message", + "text": "message" + }] + } + +} diff --git a/wechat-client/app.wxss b/wechat-client/app.wxss new file mode 100644 index 0000000..f3f53fe --- /dev/null +++ b/wechat-client/app.wxss @@ -0,0 +1,10 @@ +/**app.wxss**/ +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rpx 0; + box-sizing: border-box; +} diff --git a/wechat-client/pages/connect/connect.js b/wechat-client/pages/connect/connect.js new file mode 100644 index 0000000..abc6eae --- /dev/null +++ b/wechat-client/pages/connect/connect.js @@ -0,0 +1,204 @@ +// pages/connect/connect.js +var app = getApp(); +var MQTT = require("../../utils/paho-mqtt.js"); + +Page({ + + /** + * 页面的初始数据 + */ + data: { + server_addr: '', + user_name: null, + user_psw: null, + error_message: '', + switch_checked:false, + btn_loading:false + }, + + server_addr_input:function(e){ + console.log(e); + this.setData({ + server_addr:e.detail.value + }); + }, + user_name_input: function (e) { + console.log(e); + this.setData({ + user_name: e.detail.value + }); + }, + user_psw_input: function (e) { + console.log(e); + this.setData({ + user_psw: e.detail.value + }); + }, + switch_change:function(e){ + console.log(e); + this.setData({ + switch_checked: e.detail.value + }); + }, + + btn_connect:function(){ + //查看输入是否为空,设置错误信息 + if(this.data.server_addr==''||this.data.server_addr==null){ + this.setData({ + error_message:"server address can not be empty!" + }); + return; + } + if(this.data.switch_checked){ + if (this.data.user_name == null || this.data.user_name == '') { + this.setData({ + error_message: "user name can not be empty!" + }); + return; + } + if (this.data.user_psw == null || this.data.user_psw == '') { + this.setData({ + error_message: "user password can not be empty!" + }); + return; + } + } + + //reset error message + if(this.data.error_message){ + this.setData({ + error_message:'' + }); + } + //在按钮上显示加载标志 + this.setData({ + btn_loading:true + }); + + var client = new MQTT.Client("wss://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); + var that = this; + //connect to MQTT broker + var connectOptions = { + timeout: 10, + useSSL: true, + cleanSession: true, + keepAliveInterval: 30, + reconnect: true, + onSuccess: function () { + console.log('connected'); + + app.globalData.mqtt_client = client; + + client.onMessageArrived = function (msg) { + /* + if (typeof app.globalData.onMessageArrived === 'function') { + return app.globalData.onMessageArrived(msg); + }*/ + if(app.globalData.messages==null){ + app.globalData.messages = [{ topic: msg.topic, message: msg.payloadString }]; + }else{ + app.globalData.messages = + [{ topic: msg.topic, message: msg.payloadString }].concat(app.globalData.messages); + } + + } + + client.onConnectionLost = function (responseObject) { + if (typeof app.globalData.onConnectionLost === 'function') { + return app.globalData.onConnectionLost(responseObject); + } + if (responseObject.errorCode !== 0) { + console.log("onConnectionLost:" + responseObject.errorMessage); + } + } + //去除按钮上的加载标志 + that.setData({ + btn_loading: false + }); + + wx.switchTab({ + url: '../subscribe/subscribe', + }); + }, + onFailure: function (option) { + console.log(option); + //去除按钮上的加载标志 + that.setData({ + btn_loading: false + }); + wx.showModal({ + //title: msg.destinationName, + content: option.errorMessage + }); + } + }; + if(this.data.switch_checked){ + connectOptions.userName = this.data.user_name; + connectOptions.password = this.data.user_psw; + } + + client.connect(connectOptions); + + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad: function (options) { + + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady: function () { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow: function () { + /*if (app.globalData.mqtt_client != null) { + wx.reLaunch({ + url: '../subscribe/subscribe', + }); + return; + }*/ + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide: function () { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload: function () { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh: function () { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom: function () { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage: function () { + + } +}) \ No newline at end of file diff --git a/wechat-client/pages/connect/connect.json b/wechat-client/pages/connect/connect.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/wechat-client/pages/connect/connect.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/wechat-client/pages/connect/connect.wxml b/wechat-client/pages/connect/connect.wxml new file mode 100644 index 0000000..d14d1a6 --- /dev/null +++ b/wechat-client/pages/connect/connect.wxml @@ -0,0 +1,14 @@ + + + {{error_message}} + + + + Username/Password Auth + + + + + + + diff --git a/wechat-client/pages/connect/connect.wxss b/wechat-client/pages/connect/connect.wxss new file mode 100644 index 0000000..842880b --- /dev/null +++ b/wechat-client/pages/connect/connect.wxss @@ -0,0 +1,20 @@ +/* pages/connect/connect.wxss */ +input{ + border: 1px solid red; + width: 80%; + margin: 4%; + padding: 6px; +} +.error_view{ + background: #de352d; + color: #fff; + height: 58rpx; + line-height: 58rpx; + font-size: 24rpx; + text-align: center; + position: absolute; + left: 0; + top: 0; + width: 100%; + z-index:3; +} \ No newline at end of file diff --git a/wechat-client/pages/index/index.js b/wechat-client/pages/index/index.js new file mode 100644 index 0000000..608a282 --- /dev/null +++ b/wechat-client/pages/index/index.js @@ -0,0 +1,54 @@ +//index.js +//获取应用实例 +const app = getApp() + +Page({ + data: { + motto: 'Hello World', + userInfo: {}, + hasUserInfo: false, + canIUse: wx.canIUse('button.open-type.getUserInfo') + }, + //事件处理函数 + bindViewTap: function() { + wx.navigateTo({ + url: '../logs/logs' + }) + }, + onLoad: function () { + if (app.globalData.userInfo) { + this.setData({ + userInfo: app.globalData.userInfo, + hasUserInfo: true + }) + } else if (this.data.canIUse){ + // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 + // 所以此处加入 callback 以防止这种情况 + app.userInfoReadyCallback = res => { + this.setData({ + userInfo: res.userInfo, + hasUserInfo: true + }) + } + } else { + // 在没有 open-type=getUserInfo 版本的兼容处理 + wx.getUserInfo({ + success: res => { + app.globalData.userInfo = res.userInfo + this.setData({ + userInfo: res.userInfo, + hasUserInfo: true + }) + } + }) + } + }, + getUserInfo: function(e) { + console.log(e) + app.globalData.userInfo = e.detail.userInfo + this.setData({ + userInfo: e.detail.userInfo, + hasUserInfo: true + }) + } +}) diff --git a/wechat-client/pages/index/index.json b/wechat-client/pages/index/index.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/wechat-client/pages/index/index.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/wechat-client/pages/index/index.wxml b/wechat-client/pages/index/index.wxml new file mode 100644 index 0000000..c5d0919 --- /dev/null +++ b/wechat-client/pages/index/index.wxml @@ -0,0 +1,13 @@ + + + + + + + {{userInfo.nickName}} + + + + {{motto}} + + diff --git a/wechat-client/pages/index/index.wxss b/wechat-client/pages/index/index.wxss new file mode 100644 index 0000000..ce30de0 --- /dev/null +++ b/wechat-client/pages/index/index.wxss @@ -0,0 +1,21 @@ +/**index.wxss**/ +.userinfo { + display: flex; + flex-direction: column; + align-items: center; +} + +.userinfo-avatar { + width: 128rpx; + height: 128rpx; + margin: 20rpx; + border-radius: 50%; +} + +.userinfo-nickname { + color: #aaa; +} + +.usermotto { + margin-top: 200px; +} \ No newline at end of file diff --git a/wechat-client/pages/logs/logs.js b/wechat-client/pages/logs/logs.js new file mode 100644 index 0000000..b2b967d --- /dev/null +++ b/wechat-client/pages/logs/logs.js @@ -0,0 +1,15 @@ +//logs.js +const util = require('../../utils/util.js') + +Page({ + data: { + logs: [] + }, + onLoad: function () { + this.setData({ + logs: (wx.getStorageSync('logs') || []).map(log => { + return util.formatTime(new Date(log)) + }) + }) + } +}) diff --git a/wechat-client/pages/logs/logs.json b/wechat-client/pages/logs/logs.json new file mode 100644 index 0000000..28379bc --- /dev/null +++ b/wechat-client/pages/logs/logs.json @@ -0,0 +1,3 @@ +{ + "navigationBarTitleText": "查看启动日志" +} \ No newline at end of file diff --git a/wechat-client/pages/logs/logs.wxml b/wechat-client/pages/logs/logs.wxml new file mode 100644 index 0000000..b5a85ac --- /dev/null +++ b/wechat-client/pages/logs/logs.wxml @@ -0,0 +1,6 @@ + + + + {{index + 1}}. {{log}} + + diff --git a/wechat-client/pages/logs/logs.wxss b/wechat-client/pages/logs/logs.wxss new file mode 100644 index 0000000..94d4b88 --- /dev/null +++ b/wechat-client/pages/logs/logs.wxss @@ -0,0 +1,8 @@ +.log-list { + display: flex; + flex-direction: column; + padding: 40rpx; +} +.log-item { + margin: 10rpx; +} diff --git a/wechat-client/pages/message/message.js b/wechat-client/pages/message/message.js new file mode 100644 index 0000000..594ed51 --- /dev/null +++ b/wechat-client/pages/message/message.js @@ -0,0 +1,79 @@ +// pages/message/message.js +var app = getApp(); +Page({ + + /** + * 页面的初始数据 + */ + data: { + message_objects:[{ + topic:'sumas', + message:'hello world!' + }, + { + topic: 'sumas', + message: 'hello world!' + }] + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad: function (options) { + + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady: function () { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow: function () { + //app.globalData.messages=[{topic:'summer',message:"wind"}].concat(this.data.message_objects); + if(app.globalData.messages!=null){ + this.setData({ + message_objects: app.globalData.messages + }); + } + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide: function () { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload: function () { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh: function () { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom: function () { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage: function () { + + } +}) \ No newline at end of file diff --git a/wechat-client/pages/message/message.json b/wechat-client/pages/message/message.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/wechat-client/pages/message/message.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/wechat-client/pages/message/message.wxml b/wechat-client/pages/message/message.wxml new file mode 100644 index 0000000..2479668 --- /dev/null +++ b/wechat-client/pages/message/message.wxml @@ -0,0 +1,13 @@ + + + Topic + + Message + + + + + {{item.topic}} + {{item.message}} + + \ No newline at end of file diff --git a/wechat-client/pages/message/message.wxss b/wechat-client/pages/message/message.wxss new file mode 100644 index 0000000..26cd08e --- /dev/null +++ b/wechat-client/pages/message/message.wxss @@ -0,0 +1,43 @@ +/* pages/message/message.wxss */ +.header{ + display: flex; + flex-direction: row; + justify-content: center; + line-height: 40px; + border-bottom: 1px solid red; + background-color: #f0f0f0; +} +.head_item{ + text-align: center; +} +.border_item{ + width: 1px; + background-color: red; +} +.content_list{ + display: flex; + flex-direction: column; +} +.items{ + display:flex; + flex-direction: row; + align-items: center; + border-bottom: 1px solid red; + padding-top: 5px; + padding-bottom: 5px; +} +.item_topic{ + padding-left: 10px; + word-break: break-all; + width: 30%; + white-space: pre-line; + border-right: 1px solid #f0f0f0; +} +.item_message{ + padding-left: 10px; + word-break: break-all; + width:70%; +} +.test{ + word-break: break-all; +} diff --git a/wechat-client/pages/publish/publish.js b/wechat-client/pages/publish/publish.js new file mode 100644 index 0000000..37d1b54 --- /dev/null +++ b/wechat-client/pages/publish/publish.js @@ -0,0 +1,132 @@ +// pages/publish/publish.js +var app=getApp(); +Page({ + + /** + * 页面的初始数据 + */ + data: { + pub_topic:'', + pub_message:'', + qos_array:[0,1,2], + retained_array:[false,true], + qos_index:0, + retained_index:0 + }, + pub_topic_input:function(e){ + console.log(e); + this.setData({ + pub_topic:e.detail.value + }); + }, + qos_picker_change: function (e) { + console.log(e); + this.setData({ + qos_index: e.detail.value + }); + }, + retained_picker_change:function(e){ + console.log(e); + this.setData({ + retained_index: e.detail.value + }); + }, + message_input:function(e){ + console.log(e); + this.setData({ + pub_message:e.detail.value + }); + }, + btn_publish:function(){ + app.globalData.tst; + if(this.data.pub_topic==''||this.data.pub_topic==null){ + wx.showToast({ + title: 'input topic', + icon:'loading', + duration:2000 + }); + return; + } + if(this.data.pub_message==''||this.data.pub_message==null){ + wx.showToast({ + title: 'input message', + icon: 'loading', + duration: 2000 + }); + return; + } + if (app.globalData.mqtt_client && app.globalData.mqtt_client.isConnected()){ + app.globalData.mqtt_client.publish(this.data.pub_topic, + this.data.pub_message, + this.data.qos_array[this.data.qos_index], + this.data.retained_array[this.data.retained_index] + ); + wx.showToast({ + title: 'publish success', + icon:"success", + duration:2000 + }); + }else{ + wx.showToast({ + title: 'client invalid', + icon: "loading", + duration: 2000 + }); + } + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad: function (options) { + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady: function () { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow: function () { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide: function () { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload: function () { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh: function () { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom: function () { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage: function () { + + } +}) \ No newline at end of file diff --git a/wechat-client/pages/publish/publish.json b/wechat-client/pages/publish/publish.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/wechat-client/pages/publish/publish.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/wechat-client/pages/publish/publish.wxml b/wechat-client/pages/publish/publish.wxml new file mode 100644 index 0000000..60af4e1 --- /dev/null +++ b/wechat-client/pages/publish/publish.wxml @@ -0,0 +1,16 @@ + + + + + + qos:{{qos_array[qos_index]}} + + + + + retained:{{retained_array[retained_index]}} + + + + + diff --git a/wechat-client/pages/publish/publish.wxss b/wechat-client/pages/publish/publish.wxss new file mode 100644 index 0000000..3f59db5 --- /dev/null +++ b/wechat-client/pages/publish/publish.wxss @@ -0,0 +1,14 @@ +/* pages/publish/publish.wxss */ +textarea, input{ + border: 1px solid red; + width: 80%; + margin: 4%; + padding: 6px; +} + +.picker{ + width:80%; + border:1px solid red; + margin:4%; + padding:6px; +} \ No newline at end of file diff --git a/wechat-client/pages/subscribe/subscribe.js b/wechat-client/pages/subscribe/subscribe.js new file mode 100644 index 0000000..dbfca94 --- /dev/null +++ b/wechat-client/pages/subscribe/subscribe.js @@ -0,0 +1,159 @@ +// pages/subscribe/subscribe.js +var app=getApp(); +Page({ + + /** + * 页面的初始数据 + */ + data: { + current_tab:0, + qos_array:[0,1,2], + sub_topic:'', + unsub_topic:'', + index:0 + }, + switch_tab:function(e){ + console.log(e); + this.setData({ + current_tab:e.target.dataset.current + }); + }, + swiper_change:function(e){ + console.log(e); + this.setData({ + current_tab:e.detail.current + }); + }, + picker_change:function(e){ + console.log(e); + this.setData({ + index:e.detail.value + }); + }, + btn_subscribe:function(){ + if(this.data.sub_topic==''||this.data.sub_topic==null){ + wx.showToast({ + title: 'input topic', + icon:'loading', + duration:2000 + }); + return; + } + if (app.globalData.mqtt_client && app.globalData.mqtt_client.isConnected()){ + app.globalData.mqtt_client.subscribe(this.data.sub_topic,{ + qos:this.data.qos_array[this.data.index], + onSuccess:function(){ + wx.showToast({ + title: 'success', + icon: 'success', + duration: 2000 + }); + }, + onFailure:function(){ + wx.showToast({ + title: 'Failure', + icon:'loading', + duration: 2000 + }); + }, + }); + } + + }, + sub_input:function(e){ + console.log(e); + this.setData({ + sub_topic: e.detail.value + }); + }, + unsub_input:function(e){ + console.log(e); + this.setData({ + unsub_topic:e.detail.value + }); + }, + btn_unsubscribe:function(e){ + if (this.data.unsub_topic == '' || this.data.unsub_topic == null) { + wx.showToast({ + title: 'input topic', + icon: 'loading', + duration: 2000 + }); + return; + } + if (app.globalData.mqtt_client && app.globalData.mqtt_client.isConnected()) { + app.globalData.mqtt_client.unsubscribe(this.data.unsub_topic, { + onSuccess: function () { + wx.showToast({ + title: 'success', + icon: 'success', + duration: 2000 + }); + }, + onFailure: function () { + wx.showToast({ + title: 'Failure', + icon: 'loading', + duration: 2000 + }); + }, + }); + } + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad: function (options) { + + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady: function () { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow: function () { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide: function () { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload: function () { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh: function () { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom: function () { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage: function () { + + } +}) \ No newline at end of file diff --git a/wechat-client/pages/subscribe/subscribe.json b/wechat-client/pages/subscribe/subscribe.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/wechat-client/pages/subscribe/subscribe.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/wechat-client/pages/subscribe/subscribe.wxml b/wechat-client/pages/subscribe/subscribe.wxml new file mode 100644 index 0000000..c402b8a --- /dev/null +++ b/wechat-client/pages/subscribe/subscribe.wxml @@ -0,0 +1,26 @@ + + + subscribe + unsubscribe + + + + + + + + + qos:{{qos_array[index]}} + + + + + + + + + + + + + diff --git a/wechat-client/pages/subscribe/subscribe.wxss b/wechat-client/pages/subscribe/subscribe.wxss new file mode 100644 index 0000000..27928f3 --- /dev/null +++ b/wechat-client/pages/subscribe/subscribe.wxss @@ -0,0 +1,36 @@ +/* pages/subscribe/subscribe.wxss */ +.swiper_tab { + display: flex; + flex-direction: row; + line-height: 50px; + border-bottom: 2px solid; + border-bottom-color: #777; +} + +.tab_item{ + width: 50%; + text-align: center; + font-size: 15px; + color: #777; +} + +.on{ + color: red; + border-bottom: 1px solid red; +} + +input{ + border: 1px solid red; + width: 80%; + margin: 4%; + padding: 6px; +} + +.picker{ + width:80%; + border:1px solid red; + margin:4%; + padding:6px; +} + + diff --git a/wechat-client/project.config.json b/wechat-client/project.config.json new file mode 100644 index 0000000..f3c873a --- /dev/null +++ b/wechat-client/project.config.json @@ -0,0 +1,28 @@ +{ + "description": "项目配置文件。", + "setting": { + "urlCheck": false, + "es6": true, + "postcss": true, + "minified": true, + "newFeature": true + }, + "compileType": "miniprogram", + "libVersion": "1.7.0", + "appid": "touristappid", + "projectname": "hehe", + "condition": { + "search": { + "current": -1, + "list": [] + }, + "conversation": { + "current": -1, + "list": [] + }, + "miniprogram": { + "current": -1, + "list": [] + } + } +} \ No newline at end of file diff --git a/wechat-client/utils/paho-mqtt-min.js b/wechat-client/utils/paho-mqtt-min.js new file mode 100644 index 0000000..71caf2a --- /dev/null +++ b/wechat-client/utils/paho-mqtt-min.js @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2013, 2016 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + *******************************************************************************/ +(function(m,r){"object"===typeof exports&&"object"===typeof module?module.exports=r():"function"===typeof define&&define.amd?define(r):"object"===typeof exports?exports=r():("undefined"===typeof m.Paho&&(m.Paho={}),m.Paho.MQTT=r())})(global,function(){return function(m){function r(a,b,d){b[d++]=a>>8;b[d++]=a%256;return d}function u(a,b,d,k){k=r(b,d,k);D(a,d,k);return k+b}function p(a){for(var b=0,d=0;d=k&&(d++,b++),b+=3):127=e){var f=a.charCodeAt(++k);if(isNaN(f))throw Error(h(g.MALFORMED_UNICODE,[e,f]));e=(e-55296<<10)+(f-56320)+65536}127>=e?b[d++]=e:(2047>=e?b[d++]=e>>6&31|192:(65535>=e?b[d++]=e>>12&15|224:(b[d++]=e>>18&7|240,b[d++]=e>>12&63|128),b[d++]=e>>6&63|128),b[d++]=e&63|128)}return b}function E(a,b,d){for(var k="",e,f=b;fe)){var n=a[f++]-128;if(0>n)throw Error(h(g.MALFORMED_UTF,[e.toString(16),n.toString(16), +""]));if(224>e)e=64*(e-192)+n;else{var c=a[f++]-128;if(0>c)throw Error(h(g.MALFORMED_UTF,[e.toString(16),n.toString(16),c.toString(16)]));if(240>e)e=4096*(e-224)+64*n+c;else{var l=a[f++]-128;if(0>l)throw Error(h(g.MALFORMED_UTF,[e.toString(16),n.toString(16),c.toString(16),l.toString(16)]));if(248>e)e=262144*(e-240)+4096*n+64*c+l;else throw Error(h(g.MALFORMED_UTF,[e.toString(16),n.toString(16),c.toString(16),l.toString(16)]));}}}65535>10)),e=56320+(e& +1023));k+=String.fromCharCode(e)}return k}var y=function(a,b){for(var d in a)if(a.hasOwnProperty(d))if(b.hasOwnProperty(d)){if(typeof a[d]!==b[d])throw Error(h(g.INVALID_TYPE,[typeof a[d],d]));}else{d="Unknown property, "+d+". Valid properties are:";for(var k in b)b.hasOwnProperty(k)&&(d=d+" "+k);throw Error(d);}},s=function(a,b){return function(){return a.apply(b,arguments)}},g={OK:{code:0,text:"AMQJSC0000I OK."},CONNECT_TIMEOUT:{code:1,text:"AMQJSC0001E Connect timed out."},SUBSCRIBE_TIMEOUT:{code:2, +text:"AMQJS0002E Subscribe timed out."},UNSUBSCRIBE_TIMEOUT:{code:3,text:"AMQJS0003E Unsubscribe timed out."},PING_TIMEOUT:{code:4,text:"AMQJS0004E Ping timed out."},INTERNAL_ERROR:{code:5,text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"},CONNACK_RETURNCODE:{code:6,text:"AMQJS0006E Bad Connack return code:{0} {1}."},SOCKET_ERROR:{code:7,text:"AMQJS0007E Socket error:{0}."},SOCKET_CLOSE:{code:8,text:"AMQJS0008I Socket closed."},MALFORMED_UTF:{code:9,text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, +UNSUPPORTED:{code:10,text:"AMQJS0010E {0} is not supported by this browser."},INVALID_STATE:{code:11,text:"AMQJS0011E Invalid state {0}."},INVALID_TYPE:{code:12,text:"AMQJS0012E Invalid type {0} for {1}."},INVALID_ARGUMENT:{code:13,text:"AMQJS0013E Invalid argument {0} for {1}."},UNSUPPORTED_OPERATION:{code:14,text:"AMQJS0014E Unsupported operation."},INVALID_STORED_DATA:{code:15,text:"AMQJS0015E Invalid data in local storage key\x3d{0} value\x3d{1}."},INVALID_MQTT_MESSAGE_TYPE:{code:16,text:"AMQJS0016E Invalid MQTT message type {0}."}, +MALFORMED_UNICODE:{code:17,text:"AMQJS0017E Malformed Unicode string:{0} {1}."},BUFFER_FULL:{code:18,text:"AMQJS0018E Message buffer is full, maximum buffer size: {0}."}},H={0:"Connection Accepted",1:"Connection Refused: unacceptable protocol version",2:"Connection Refused: identifier rejected",3:"Connection Refused: server unavailable",4:"Connection Refused: bad user name or password",5:"Connection Refused: not authorized"},h=function(a,b){var d=a.text;if(b)for(var k,e,f=0;f>7;0h);c=f.length+1;b=new ArrayBuffer(b+c);h=new Uint8Array(b);h[0]=a;h.set(f,1);if(3==this.type)c=u(this.payloadMessage.destinationName,k,h,c);else if(1==this.type){switch(this.mqttVersion){case 3:h.set(A,c);c+=A.length; +break;case 4:h.set(B,c),c+=B.length}a=0;this.cleanSession&&(a=2);void 0!==this.willMessage&&(a=a|4|this.willMessage.qos<<3,this.willMessage.retained&&(a|=32));void 0!==this.userName&&(a|=128);void 0!==this.password&&(a|=64);h[c++]=a;c=r(this.keepAliveInterval,h,c)}void 0!==this.messageIdentifier&&(c=r(this.messageIdentifier,h,c));switch(this.type){case 1:c=u(this.clientId,p(this.clientId),h,c);void 0!==this.willMessage&&(c=u(this.willMessage.destinationName,p(this.willMessage.destinationName),h,c), +c=r(e.byteLength,h,c),h.set(e,c),c+=e.byteLength);void 0!==this.userName&&(c=u(this.userName,p(this.userName),h,c));void 0!==this.password&&u(this.password,p(this.password),h,c);break;case 3:h.set(g,c);break;case 8:for(f=0;f +this.disconnectedBufferSize)throw Error(h(g.BUFFER_FULL,[this.disconnectedBufferSize]));0=e[f]?c+"0"+e[f].toString(16):c+e[f].toString(16);d.payloadMessage.payloadHex=c;d.payloadMessage.qos=b.payloadMessage.qos;d.payloadMessage.destinationName=b.payloadMessage.destinationName;b.payloadMessage.duplicate&&(d.payloadMessage.duplicate=!0);b.payloadMessage.retained&&(d.payloadMessage.retained= +!0);0===a.indexOf("Sent:")&&(void 0===b.sequence&&(b.sequence=++this._sequence),d.sequence=b.sequence);break;default:throw Error(h(g.INVALID_STORED_DATA,[key,d]));}try{m.setStorageSync(a+this._localKey+b.messageIdentifier,JSON.stringify(d))}catch(n){}};c.prototype.restore=function(a){var b=m.getStorageSync(a),d=JSON.parse(b),c=new q(d.type,d);switch(d.type){case 3:for(var b=d.payloadMessage.payloadHex,e=new ArrayBuffer(b.length/2),e=new Uint8Array(e),f=0;2<=b.length;){var n=parseInt(b.substring(0, +2),16),b=b.substring(2,b.length);e[f++]=n}b=new v(e);b.qos=d.payloadMessage.qos;b.destinationName=d.payloadMessage.destinationName;d.payloadMessage.duplicate&&(b.duplicate=!0);d.payloadMessage.retained&&(b.retained=!0);c.payloadMessage=b;break;default:throw Error(h(g.INVALID_STORED_DATA,[a,b]));}0===a.indexOf("Sent:"+this._localKey)?(c.payloadMessage.duplicate=!0,this._sentMessages[c.messageIdentifier]=c):0===a.indexOf("Received:"+this._localKey)&&(this._receivedMessages[c.messageIdentifier]=c)}; +c.prototype._process_queue=function(){for(var a=null,b=this._msg_queue.reverse();a=b.pop();)this._socket_send(a),this._notify_msg_sent[a]&&(this._notify_msg_sent[a](),delete this._notify_msg_sent[a])};c.prototype._requires_ack=function(a){var b=Object.keys(this._sentMessages).length;if(b>this.maxMessageIdentifier)throw Error("Too many messages:"+b);for(;void 0!==this._sentMessages[this._message_identifier];)this._message_identifier++;a.messageIdentifier=this._message_identifier;this._sentMessages[a.messageIdentifier]= +a;3===a.type&&this.store("Sent:",a);this._message_identifier===this.maxMessageIdentifier&&(this._message_identifier=1)};c.prototype._on_socket_open=function(a){a=new q(1,this.connectOptions);a.clientId=this.clientId;this._socket_send(a)};c.prototype._on_socket_message=function(a){this._trace("Client._on_socket_message",a.data);a=this._deframeMessages(a.data);for(var b=0;b>4,t=m&15,f=f+1,w=void 0,C=0,p=1;do{if(f==e.length){c=[null,n];break a}w=e[f++];C+=(w&127)*p;p*=128}while(0!==(w&128));w=f+C;if(w>e.length)c=[null,n];else{var x=new q(l);switch(l){case 2:e[f++]&1&&(x.sessionPresent=!0);x.returnCode=e[f++];break;case 3:var n=t>>1&3,r=256*e[f]+e[f+1],f=f+2,u=E(e,f,r), +f=f+r;0this._reconnectInterval&&(this._reconnectInterval*=2),this.connectOptions.uris?(this.hostIndex=0,this._doConnect(this.connectOptions.uris[0])):this._doConnect(this.uri))};c.prototype._disconnected=function(a,b){this._trace("Client._disconnected",a,b);if(void 0!==a&&this._reconnecting)this._reconnectTimeout=new z(this,this._reconnectInterval, +this._reconnect);else if(this.sendPinger.cancel(),this.receivePinger.cancel(),this._connectTimeout&&(this._connectTimeout.cancel(),this._connectTimeout=null),this._msg_queue=[],this._buffered_msg_queue=[],this._notify_msg_sent={},this.connectOptions.uris&&this.hostIndexb)throw Error(h(g.INVALID_TYPE,[typeof b,"port"]));if("string"!==typeof d)throw Error(h(g.INVALID_TYPE,[typeof d,"path"]));e="ws://"+(-1!==a.indexOf(":")&&"["!==a.slice(0,1)&&"]"!==a.slice(-1)?"["+a+"]":a)+":"+b+d}for(var n=f=0;n=m&&n++;f++}if("string"!==typeof k||65535a.mqttVersion)throw Error(h(g.INVALID_ARGUMENT,[a.mqttVersion,"connectOptions.mqttVersion"]));void 0===a.mqttVersion?(a.mqttVersionExplicit=!1,a.mqttVersion=4):a.mqttVersionExplicit=!0;if(void 0!==a.password&&void 0===a.userName)throw Error(h(g.INVALID_ARGUMENT,[a.password,"connectOptions.password"]));if(a.willMessage){if(!(a.willMessage instanceof +v))throw Error(h(g.INVALID_TYPE,[a.willMessage,"connectOptions.willMessage"]));a.willMessage.stringPayload=null;if("undefined"===typeof a.willMessage.destinationName)throw Error(h(g.INVALID_TYPE,[typeof a.willMessage.destinationName,"connectOptions.willMessage.destinationName"]));}"undefined"===typeof a.cleanSession&&(a.cleanSession=!0);if(a.hosts){if(!(a.hosts instanceof Array))throw Error(h(g.INVALID_ARGUMENT,[a.hosts,"connectOptions.hosts"]));if(1>a.hosts.length)throw Error(h(g.INVALID_ARGUMENT, +[a.hosts,"connectOptions.hosts"]));for(var b=!1,c=0;ca.ports[c])throw Error(h(g.INVALID_TYPE,[typeof a.ports[c],"connectOptions.ports["+c+"]"]));var b=a.hosts[c],f=a.ports[c];e="ws://"+(-1!==b.indexOf(":")?"["+b+"]":b)+":"+f+d;a.uris.push(e)}}}l.connect(a)}; +this.subscribe=function(a,b){if("string"!==typeof a)throw Error("Invalid argument:"+a);b=b||{};y(b,{qos:"number",invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"});if(b.timeout&&!b.onFailure)throw Error("subscribeOptions.timeout specified with no onFailure callback.");if("undefined"!==typeof b.qos&&0!==b.qos&&1!==b.qos&&2!==b.qos)throw Error(h(g.INVALID_ARGUMENT,[b.qos,"subscribeOptions.qos"]));l.subscribe(a,b)};this.unsubscribe=function(a,b){if("string"!==typeof a)throw Error("Invalid argument:"+ +a);b=b||{};y(b,{invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"});if(b.timeout&&!b.onFailure)throw Error("unsubscribeOptions.timeout specified with no onFailure callback.");l.unsubscribe(a,b)};this.send=function(a,b,c,d){var e;if(0===arguments.length)throw Error("Invalid argument.length");if(1==arguments.length){if(!(a instanceof v)&&"string"!==typeof a)throw Error("Invalid argument:"+typeof a);e=a;if("undefined"===typeof e.destinationName)throw Error(h(g.INVALID_ARGUMENT, +[e.destinationName,"Message.destinationName"]));}else e=new v(b),e.destinationName=a,3<=arguments.length&&(e.qos=c),4<=arguments.length&&(e.retained=d);l.send(e)};this.publish=function(a,b,c,d){console.log("Publising message to: ",a);var e;if(0===arguments.length)throw Error("Invalid argument.length");if(1==arguments.length){if(!(a instanceof v)&&"string"!==typeof a)throw Error("Invalid argument:"+typeof a);e=a;if("undefined"===typeof e.destinationName)throw Error(h(g.INVALID_ARGUMENT,[e.destinationName, +"Message.destinationName"]));}else e=new v(b),e.destinationName=a,3<=arguments.length&&(e.qos=c),4<=arguments.length&&(e.retained=d);l.send(e)};this.disconnect=function(){l.disconnect()};this.getTraceLog=function(){return l.getTraceLog()};this.startTrace=function(){l.startTrace()};this.stopTrace=function(){l.stopTrace()};this.isConnected=function(){return l.connected}};G.prototype={get host(){return this._getHost()},set host(a){this._setHost(a)},get port(){return this._getPort()},set port(a){this._setPort(a)}, +get path(){return this._getPath()},set path(a){this._setPath(a)},get clientId(){return this._getClientId()},set clientId(a){this._setClientId(a)},get onConnected(){return this._getOnConnected()},set onConnected(a){this._setOnConnected(a)},get disconnectedPublishing(){return this._getDisconnectedPublishing()},set disconnectedPublishing(a){this._setDisconnectedPublishing(a)},get disconnectedBufferSize(){return this._getDisconnectedBufferSize()},set disconnectedBufferSize(a){this._setDisconnectedBufferSize(a)}, +get onConnectionLost(){return this._getOnConnectionLost()},set onConnectionLost(a){this._setOnConnectionLost(a)},get onMessageDelivered(){return this._getOnMessageDelivered()},set onMessageDelivered(a){this._setOnMessageDelivered(a)},get onMessageArrived(){return this._getOnMessageArrived()},set onMessageArrived(a){this._setOnMessageArrived(a)},get trace(){return this._getTrace()},set trace(a){this._setTrace(a)}};var v=function(a){var b;if("string"===typeof a||a instanceof ArrayBuffer||a instanceof +Int8Array||a instanceof Uint8Array||a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||a instanceof Float32Array||a instanceof Float64Array)b=a;else throw h(g.INVALID_ARGUMENT,[a,"newPayload"]);this._getPayloadString=function(){return"string"===typeof b?b:E(b,0,b.length)};this._getPayloadBytes=function(){if("string"===typeof b){var a=new ArrayBuffer(p(b)),a=new Uint8Array(a);D(b,a,0);return a}return b};var c;this._getDestinationName=function(){return c}; +this._setDestinationName=function(a){if("string"===typeof a)c=a;else throw Error(h(g.INVALID_ARGUMENT,[a,"newDestinationName"]));};var k=0;this._getQos=function(){return k};this._setQos=function(a){if(0===a||1===a||2===a)k=a;else throw Error("Invalid argument:"+a);};var e=!1;this._getRetained=function(){return e};this._setRetained=function(a){if("boolean"===typeof a)e=a;else throw Error(h(g.INVALID_ARGUMENT,[a,"newRetained"]));};var f=!1;this._getDuplicate=function(){return f};this._setDuplicate= +function(a){f=a}};v.prototype={get payloadString(){return this._getPayloadString()},get payloadBytes(){return this._getPayloadBytes()},get destinationName(){return this._getDestinationName()},set destinationName(a){this._setDestinationName(a)},get topic(){return this._getDestinationName()},set topic(a){this._setDestinationName(a)},get qos(){return this._getQos()},set qos(a){this._setQos(a)},get retained(){return this._getRetained()},set retained(a){this._setRetained(a)},get duplicate(){return this._getDuplicate()}, +set duplicate(a){this._setDuplicate(a)}};return{Client:G,Message:v}}(wx)}); \ No newline at end of file diff --git a/wechat-client/utils/paho-mqtt.js b/wechat-client/utils/paho-mqtt.js new file mode 100644 index 0000000..e4524b9 --- /dev/null +++ b/wechat-client/utils/paho-mqtt.js @@ -0,0 +1,2406 @@ +/******************************************************************************* + * Copyright (c) 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Andrew Banks - initial API and implementation and initial documentation + *******************************************************************************/ + + +// Only expose a single object name in the global namespace. +// Everything must go through this module. Global Paho.MQTT module +// only has a single public function, client, which returns +// a Paho.MQTT client object given connection details. + +/** + * Send and receive messages using web browsers. + *

+ * This programming interface lets a JavaScript client application use the MQTT V3.1 or + * V3.1.1 protocol to connect to an MQTT-supporting messaging server. + * + * The function supported includes: + *

    + *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. + *
  2. Specifying options that relate to the communications link with the server, + * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. + *
  3. Subscribing to and receiving messages from MQTT Topics. + *
  4. Publishing messages to MQTT Topics. + *
+ *

+ * The API consists of two main objects: + *

+ *
{@link Paho.MQTT.Client}
+ *
This contains methods that provide the functionality of the API, + * including provision of callbacks that notify the application when a message + * arrives from or is delivered to the messaging server, + * or when the status of its connection to the messaging server changes.
+ *
{@link Paho.MQTT.Message}
+ *
This encapsulates the payload of the message along with various attributes + * associated with its delivery, in particular the destination to which it has + * been (or is about to be) sent.
+ *
+ *

+ * The programming interface validates parameters passed to it, and will throw + * an Error containing an error message intended for developer use, if it detects + * an error with any parameter. + *

+ * Example: + * + *

+client = new Paho.MQTT.Client(location.hostname, Number(location.port), "clientId");
+client.onConnectionLost = onConnectionLost;
+client.onMessageArrived = onMessageArrived;
+client.connect({onSuccess:onConnect});
+function onConnect() {
+  // Once a connection has been made, make a subscription and send a message.
+  console.log("onConnect");
+  client.subscribe("/World");
+  message = new Paho.MQTT.Message("Hello");
+  message.destinationName = "/World";
+  client.send(message);
+};
+function onConnectionLost(responseObject) {
+  if (responseObject.errorCode !== 0)
+  console.log("onConnectionLost:"+responseObject.errorMessage);
+};
+function onMessageArrived(message) {
+  console.log("onMessageArrived:"+message.payloadString);
+  client.disconnect();
+};
+ * 
+ * @namespace Paho.MQTT + */ + +/* jshint shadow:true */ +(function ExportLibrary(root, factory) { + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + exports = factory(); + } else { + if (typeof root.Paho === 'undefined') { + root.Paho = {}; + } + root.Paho.MQTT = factory(); + } +})(global, function LibraryFactory() { + + + var PahoMQTT = (function(wx) { + + // Private variables below, these are only visible inside the function closure + // which is used to define the module. + + var version = "@VERSION@"; + var buildLevel = "@BUILDLEVEL@"; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var MESSAGE_TYPE = { + CONNECT: 1, + CONNACK: 2, + PUBLISH: 3, + PUBACK: 4, + PUBREC: 5, + PUBREL: 6, + PUBCOMP: 7, + SUBSCRIBE: 8, + SUBACK: 9, + UNSUBSCRIBE: 10, + UNSUBACK: 11, + PINGREQ: 12, + PINGRESP: 13, + DISCONNECT: 14 + }; + + // Collection of utility methods used to simplify module code + // and promote the DRY pattern. + + /** + * Validate an object's parameter names to ensure they + * match a list of expected variables name for this option + * type. Used to ensure option object passed into the API don't + * contain erroneous parameters. + * @param {Object} obj - User options object + * @param {Object} keys - valid keys and types that may exist in obj. + * @throws {Error} Invalid option parameter found. + * @private + */ + var validate = function(obj, keys) { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if (keys.hasOwnProperty(key)) { + if (typeof obj[key] !== keys[key]) + throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); + } else { + var errorStr = "Unknown property, " + key + ". Valid properties are:"; + for (var validKey in keys) + if (keys.hasOwnProperty(validKey)) + errorStr = errorStr + " " + validKey; + throw new Error(errorStr); + } + } + } + }; + + /** + * Return a new function which runs the user function bound + * to a fixed scope. + * @param {function} User function + * @param {object} Function scope + * @return {function} User function bound to another scope + * @private + */ + var scope = function(f, scope) { + return function() { + return f.apply(scope, arguments); + }; + }; + + /** + * Unique message type identifiers, with associated + * associated integer values. + * @private + */ + var ERROR = { + OK: { code: 0, text: "AMQJSC0000I OK." }, + CONNECT_TIMEOUT: { code: 1, text: "AMQJSC0001E Connect timed out." }, + SUBSCRIBE_TIMEOUT: { code: 2, text: "AMQJS0002E Subscribe timed out." }, + UNSUBSCRIBE_TIMEOUT: { code: 3, text: "AMQJS0003E Unsubscribe timed out." }, + PING_TIMEOUT: { code: 4, text: "AMQJS0004E Ping timed out." }, + INTERNAL_ERROR: { code: 5, text: "AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}" }, + CONNACK_RETURNCODE: { code: 6, text: "AMQJS0006E Bad Connack return code:{0} {1}." }, + SOCKET_ERROR: { code: 7, text: "AMQJS0007E Socket error:{0}." }, + SOCKET_CLOSE: { code: 8, text: "AMQJS0008I Socket closed." }, + MALFORMED_UTF: { code: 9, text: "AMQJS0009E Malformed UTF data:{0} {1} {2}." }, + UNSUPPORTED: { code: 10, text: "AMQJS0010E {0} is not supported by this browser." }, + INVALID_STATE: { code: 11, text: "AMQJS0011E Invalid state {0}." }, + INVALID_TYPE: { code: 12, text: "AMQJS0012E Invalid type {0} for {1}." }, + INVALID_ARGUMENT: { code: 13, text: "AMQJS0013E Invalid argument {0} for {1}." }, + UNSUPPORTED_OPERATION: { code: 14, text: "AMQJS0014E Unsupported operation." }, + INVALID_STORED_DATA: { code: 15, text: "AMQJS0015E Invalid data in local storage key={0} value={1}." }, + INVALID_MQTT_MESSAGE_TYPE: { code: 16, text: "AMQJS0016E Invalid MQTT message type {0}." }, + MALFORMED_UNICODE: { code: 17, text: "AMQJS0017E Malformed Unicode string:{0} {1}." }, + BUFFER_FULL: { code: 18, text: "AMQJS0018E Message buffer is full, maximum buffer size: {0}." }, + }; + + /** CONNACK RC Meaning. */ + var CONNACK_RC = { + 0: "Connection Accepted", + 1: "Connection Refused: unacceptable protocol version", + 2: "Connection Refused: identifier rejected", + 3: "Connection Refused: server unavailable", + 4: "Connection Refused: bad user name or password", + 5: "Connection Refused: not authorized" + }; + + /** + * Format an error message text. + * @private + * @param {error} ERROR.KEY value above. + * @param {substitutions} [array] substituted into the text. + * @return the text with the substitutions made. + */ + var format = function(error, substitutions) { + var text = error.text; + if (substitutions) { + var field, start; + for (var i = 0; i < substitutions.length; i++) { + field = "{" + i + "}"; + start = text.indexOf(field); + if (start > 0) { + var part1 = text.substring(0, start); + var part2 = text.substring(start + field.length); + text = part1 + substitutions[i] + part2; + } + } + } + return text; + }; + + //MQTT protocol and version 6 M Q I s d p 3 + var MqttProtoIdentifierv3 = [0x00, 0x06, 0x4d, 0x51, 0x49, 0x73, 0x64, 0x70, 0x03]; + //MQTT proto/version for 311 4 M Q T T 4 + var MqttProtoIdentifierv4 = [0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04]; + + /** + * Construct an MQTT wire protocol message. + * @param type MQTT packet type. + * @param options optional wire message attributes. + * + * Optional properties + * + * messageIdentifier: message ID in the range [0..65535] + * payloadMessage: Application Message - PUBLISH only + * connectStrings: array of 0 or more Strings to be put into the CONNECT payload + * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) + * requestQoS: array of QoS values [0..2] + * + * "Flag" properties + * cleanSession: true if present / false if absent (CONNECT) + * willMessage: true if present / false if absent (CONNECT) + * isRetained: true if present / false if absent (CONNECT) + * userName: true if present / false if absent (CONNECT) + * password: true if present / false if absent (CONNECT) + * keepAliveInterval: integer [0..65535] (CONNECT) + * + * @private + * @ignore + */ + var WireMessage = function(type, options) { + this.type = type; + for (var name in options) { + if (options.hasOwnProperty(name)) { + this[name] = options[name]; + } + } + }; + + WireMessage.prototype.encode = function() { + // Compute the first byte of the fixed header + var first = ((this.type & 0x0f) << 4); + + /* + * Now calculate the length of the variable header + payload by adding up the lengths + * of all the component parts + */ + + var remLength = 0; + var topicStrLength = []; + var destinationNameLength = 0; + var willMessagePayloadBytes; + + // if the message contains a messageIdentifier then we need two bytes for that + if (this.messageIdentifier !== undefined) + remLength += 2; + + switch (this.type) { + // If this a Connect then we need to include 12 bytes for its header + case MESSAGE_TYPE.CONNECT: + switch (this.mqttVersion) { + case 3: + remLength += MqttProtoIdentifierv3.length + 3; + break; + case 4: + remLength += MqttProtoIdentifierv4.length + 3; + break; + } + + remLength += UTF8Length(this.clientId) + 2; + if (this.willMessage !== undefined) { + remLength += UTF8Length(this.willMessage.destinationName) + 2; + // Will message is always a string, sent as UTF-8 characters with a preceding length. + willMessagePayloadBytes = this.willMessage.payloadBytes; + if (!(willMessagePayloadBytes instanceof Uint8Array)) + willMessagePayloadBytes = new Uint8Array(payloadBytes); + remLength += willMessagePayloadBytes.byteLength + 2; + } + if (this.userName !== undefined) + remLength += UTF8Length(this.userName) + 2; + if (this.password !== undefined) + remLength += UTF8Length(this.password) + 2; + break; + + // Subscribe, Unsubscribe can both contain topic strings + case MESSAGE_TYPE.SUBSCRIBE: + first |= 0x02; // Qos = 1; + for (var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + remLength += this.requestedQos.length; // 1 byte for each topic's Qos + // QoS on Subscribe only + break; + + case MESSAGE_TYPE.UNSUBSCRIBE: + first |= 0x02; // Qos = 1; + for (var i = 0; i < this.topics.length; i++) { + topicStrLength[i] = UTF8Length(this.topics[i]); + remLength += topicStrLength[i] + 2; + } + break; + + case MESSAGE_TYPE.PUBREL: + first |= 0x02; // Qos = 1; + break; + + case MESSAGE_TYPE.PUBLISH: + if (this.payloadMessage.duplicate) first |= 0x08; + first = first |= (this.payloadMessage.qos << 1); + if (this.payloadMessage.retained) first |= 0x01; + destinationNameLength = UTF8Length(this.payloadMessage.destinationName); + remLength += destinationNameLength + 2; + var payloadBytes = this.payloadMessage.payloadBytes; + remLength += payloadBytes.byteLength; + if (payloadBytes instanceof ArrayBuffer) + payloadBytes = new Uint8Array(payloadBytes); + else if (!(payloadBytes instanceof Uint8Array)) + payloadBytes = new Uint8Array(payloadBytes.buffer); + break; + + case MESSAGE_TYPE.DISCONNECT: + break; + + default: + break; + } + + // Now we can allocate a buffer for the message + + var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format + var pos = mbi.length + 1; // Offset of start of variable header + var buffer = new ArrayBuffer(remLength + pos); + var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes + + //Write the fixed header into the buffer + byteStream[0] = first; + byteStream.set(mbi, 1); + + // If this is a PUBLISH then the variable header starts with a topic + if (this.type == MESSAGE_TYPE.PUBLISH) + pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); + // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time + + else if (this.type == MESSAGE_TYPE.CONNECT) { + switch (this.mqttVersion) { + case 3: + byteStream.set(MqttProtoIdentifierv3, pos); + pos += MqttProtoIdentifierv3.length; + break; + case 4: + byteStream.set(MqttProtoIdentifierv4, pos); + pos += MqttProtoIdentifierv4.length; + break; + } + var connectFlags = 0; + if (this.cleanSession) + connectFlags = 0x02; + if (this.willMessage !== undefined) { + connectFlags |= 0x04; + connectFlags |= (this.willMessage.qos << 3); + if (this.willMessage.retained) { + connectFlags |= 0x20; + } + } + if (this.userName !== undefined) + connectFlags |= 0x80; + if (this.password !== undefined) + connectFlags |= 0x40; + byteStream[pos++] = connectFlags; + pos = writeUint16(this.keepAliveInterval, byteStream, pos); + } + + // Output the messageIdentifier - if there is one + if (this.messageIdentifier !== undefined) + pos = writeUint16(this.messageIdentifier, byteStream, pos); + + switch (this.type) { + case MESSAGE_TYPE.CONNECT: + pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); + if (this.willMessage !== undefined) { + pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); + pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); + byteStream.set(willMessagePayloadBytes, pos); + pos += willMessagePayloadBytes.byteLength; + + } + if (this.userName !== undefined) + pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); + if (this.password !== undefined) + pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); + break; + + case MESSAGE_TYPE.PUBLISH: + // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. + byteStream.set(payloadBytes, pos); + + break; + + // case MESSAGE_TYPE.PUBREC: + // case MESSAGE_TYPE.PUBREL: + // case MESSAGE_TYPE.PUBCOMP: + // break; + + case MESSAGE_TYPE.SUBSCRIBE: + // SUBSCRIBE has a list of topic strings and request QoS + for (var i = 0; i < this.topics.length; i++) { + pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos); + byteStream[pos++] = this.requestedQos[i]; + } + break; + + case MESSAGE_TYPE.UNSUBSCRIBE: + // UNSUBSCRIBE has a list of topic strings + for (var i = 0; i < this.topics.length; i++) + pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos); + break; + + default: + // Do nothing. + } + + return buffer; + }; + + function decodeMessage(input, pos) { + var startingPos = pos; + var first = input[pos]; + var type = first >> 4; + var messageInfo = first &= 0x0f; + pos += 1; + + + // Decode the remaining length (MBI format) + + var digit; + var remLength = 0; + var multiplier = 1; + do { + if (pos == input.length) { + return [null, startingPos]; + } + digit = input[pos++]; + remLength += ((digit & 0x7F) * multiplier); + multiplier *= 128; + } while ((digit & 0x80) !== 0); + + var endPos = pos + remLength; + if (endPos > input.length) { + return [null, startingPos]; + } + + var wireMessage = new WireMessage(type); + switch (type) { + case MESSAGE_TYPE.CONNACK: + var connectAcknowledgeFlags = input[pos++]; + if (connectAcknowledgeFlags & 0x01) + wireMessage.sessionPresent = true; + wireMessage.returnCode = input[pos++]; + break; + + case MESSAGE_TYPE.PUBLISH: + var qos = (messageInfo >> 1) & 0x03; + + var len = readUint16(input, pos); + pos += 2; + var topicName = parseUTF8(input, pos, len); + pos += len; + // If QoS 1 or 2 there will be a messageIdentifier + if (qos > 0) { + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + } + + var message = new Message(input.subarray(pos, endPos)); + if ((messageInfo & 0x01) == 0x01) + message.retained = true; + if ((messageInfo & 0x08) == 0x08) + message.duplicate = true; + message.qos = qos; + message.destinationName = topicName; + wireMessage.payloadMessage = message; + break; + + case MESSAGE_TYPE.PUBACK: + case MESSAGE_TYPE.PUBREC: + case MESSAGE_TYPE.PUBREL: + case MESSAGE_TYPE.PUBCOMP: + case MESSAGE_TYPE.UNSUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + break; + + case MESSAGE_TYPE.SUBACK: + wireMessage.messageIdentifier = readUint16(input, pos); + pos += 2; + wireMessage.returnCode = input.subarray(pos, endPos); + break; + + default: + break; + } + + return [wireMessage, endPos]; + } + + function writeUint16(input, buffer, offset) { + buffer[offset++] = input >> 8; //MSB + buffer[offset++] = input % 256; //LSB + return offset; + } + + function writeString(input, utf8Length, buffer, offset) { + offset = writeUint16(utf8Length, buffer, offset); + stringToUTF8(input, buffer, offset); + return offset + utf8Length; + } + + function readUint16(buffer, offset) { + return 256 * buffer[offset] + buffer[offset + 1]; + } + + /** + * Encodes an MQTT Multi-Byte Integer + * @private + */ + function encodeMBI(number) { + var output = new Array(1); + var numBytes = 0; + + do { + var digit = number % 128; + number = number >> 7; + if (number > 0) { + digit |= 0x80; + } + output[numBytes++] = digit; + } while ((number > 0) && (numBytes < 4)); + + return output; + } + + /** + * Takes a String and calculates its length in bytes when encoded in UTF8. + * @private + */ + function UTF8Length(input) { + var output = 0; + for (var i = 0; i < input.length; i++) { + var charCode = input.charCodeAt(i); + if (charCode > 0x7FF) { + // Surrogate pair means its a 4 byte character + if (0xD800 <= charCode && charCode <= 0xDBFF) { + i++; + output++; + } + output += 3; + } else if (charCode > 0x7F) + output += 2; + else + output++; + } + return output; + } + + /** + * Takes a String and writes it into an array as UTF8 encoded bytes. + * @private + */ + function stringToUTF8(input, output, start) { + var pos = start; + for (var i = 0; i < input.length; i++) { + var charCode = input.charCodeAt(i); + + // Check for a surrogate pair. + if (0xD800 <= charCode && charCode <= 0xDBFF) { + var lowCharCode = input.charCodeAt(++i); + if (isNaN(lowCharCode)) { + throw new Error(format(ERROR.MALFORMED_UNICODE, [charCode, lowCharCode])); + } + charCode = ((charCode - 0xD800) << 10) + (lowCharCode - 0xDC00) + 0x10000; + + } + + if (charCode <= 0x7F) { + output[pos++] = charCode; + } else if (charCode <= 0x7FF) { + output[pos++] = charCode >> 6 & 0x1F | 0xC0; + output[pos++] = charCode & 0x3F | 0x80; + } else if (charCode <= 0xFFFF) { + output[pos++] = charCode >> 12 & 0x0F | 0xE0; + output[pos++] = charCode >> 6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + } else { + output[pos++] = charCode >> 18 & 0x07 | 0xF0; + output[pos++] = charCode >> 12 & 0x3F | 0x80; + output[pos++] = charCode >> 6 & 0x3F | 0x80; + output[pos++] = charCode & 0x3F | 0x80; + } + } + return output; + } + + function parseUTF8(input, offset, length) { + var output = ""; + var utf16; + var pos = offset; + + while (pos < offset + length) { + var byte1 = input[pos++]; + if (byte1 < 128) + utf16 = byte1; + else { + var byte2 = input[pos++] - 128; + if (byte2 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), ""])); + if (byte1 < 0xE0) // 2 byte character + utf16 = 64 * (byte1 - 0xC0) + byte2; + else { + var byte3 = input[pos++] - 128; + if (byte3 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); + if (byte1 < 0xF0) // 3 byte character + utf16 = 4096 * (byte1 - 0xE0) + 64 * byte2 + byte3; + else { + var byte4 = input[pos++] - 128; + if (byte4 < 0) + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); + if (byte1 < 0xF8) // 4 byte character + utf16 = 262144 * (byte1 - 0xF0) + 4096 * byte2 + 64 * byte3 + byte4; + else // longer encodings are not supported + throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); + } + } + } + + if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair + { + utf16 -= 0x10000; + output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character + utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character + } + output += String.fromCharCode(utf16); + } + return output; + } + + /** + * Repeat keepalive requests, monitor responses. + * @ignore + */ + var Pinger = function(client, keepAliveInterval) { + this._client = client; + this._keepAliveInterval = keepAliveInterval * 1000; + this.isReset = false; + + var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); + + var doTimeout = function(pinger) { + return function() { + return doPing.apply(pinger); + }; + }; + + /** @ignore */ + var doPing = function() { + if (!this.isReset) { + this._client._trace("Pinger.doPing", "Timed out"); + this._client._disconnected(ERROR.PING_TIMEOUT.code, format(ERROR.PING_TIMEOUT)); + } else { + this.isReset = false; + this._client._trace("Pinger.doPing", "send PINGREQ"); + wx.sendSocketMessage({ + data: pingReq, + success: function() { + // + }, + fail: function() { + // + }, + complete: function() { + // + } + }) + this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); + } + }; + + this.reset = function() { + this.isReset = true; + clearTimeout(this.timeout); + if (this._keepAliveInterval > 0) + this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); + }; + + this.cancel = function() { + clearTimeout(this.timeout); + }; + }; + + /** + * Monitor request completion. + * @ignore + */ + var Timeout = function(client, timeoutSeconds, action, args) { + if (!timeoutSeconds) + timeoutSeconds = 30; + + var doTimeout = function(action, client, args) { + return function() { + return action.apply(client, args); + }; + }; + this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); + + this.cancel = function() { + clearTimeout(this.timeout); + }; + }; + + /* + * Internal implementation of the Websockets MQTT V3.1 client. + * + * @name Paho.MQTT.ClientImpl @constructor + * @param {String} host the DNS nameof the webSocket host. + * @param {Number} port the port number for that host. + * @param {String} clientId the MQ client identifier. + */ + var ClientImpl = function(uri, host, port, path, clientId) { + this._trace("Paho.MQTT.Client", uri, host, port, path, clientId); + + this.host = host; + this.port = port; + this.path = path; + this.uri = uri; + this.clientId = clientId; + this._wsuri = null; + + // Local storagekeys are qualified with the following string. + // The conditional inclusion of path in the key is for backward + // compatibility to when the path was not configurable and assumed to + // be /mqtt + this._localKey = host + ":" + port + (path != "/mqtt" ? ":" + path : "") + ":" + clientId + ":"; + + // Create private instance-only message queue + // Internal queue of messages to be sent, in sending order. + this._msg_queue = []; + this._buffered_msg_queue = []; + + // Messages we have sent and are expecting a response for, indexed by their respective message ids. + this._sentMessages = {}; + + // Messages we have received and acknowleged and are expecting a confirm message for + // indexed by their respective message ids. + this._receivedMessages = {}; + + // Internal list of callbacks to be executed when messages + // have been successfully sent over web socket, e.g. disconnect + // when it doesn't have to wait for ACK, just message is dispatched. + this._notify_msg_sent = {}; + + // Unique identifier for SEND messages, incrementing + // counter as messages are sent. + this._message_identifier = 1; + + // Used to determine the transmission sequence of stored sent messages. + this._sequence = 0; + + + // Load the local state, if any, from the saved version, only restore state relevant to this client. + for (var key in wx.getStorageInfoSync().keys) + if (key.indexOf("Sent:" + this._localKey) === 0 || key.indexOf("Received:" + this._localKey) === 0) + this.restore(key); + }; + + // Messaging Client public instance members. + ClientImpl.prototype.host = null; + ClientImpl.prototype.port = null; + ClientImpl.prototype.path = null; + ClientImpl.prototype.uri = null; + ClientImpl.prototype.clientId = null; + + // Messaging Client private instance members. + ClientImpl.prototype.socket = null; + /* true once we have received an acknowledgement to a CONNECT packet. */ + ClientImpl.prototype.connected = false; + /* The largest message identifier allowed, may not be larger than 2**16 but + * if set smaller reduces the maximum number of outbound messages allowed. + */ + ClientImpl.prototype.maxMessageIdentifier = 65536; + ClientImpl.prototype.connectOptions = null; + ClientImpl.prototype.hostIndex = null; + ClientImpl.prototype.onConnected = null; + ClientImpl.prototype.onConnectionLost = null; + ClientImpl.prototype.onMessageDelivered = null; + ClientImpl.prototype.onMessageArrived = null; + ClientImpl.prototype.traceFunction = null; + ClientImpl.prototype._msg_queue = null; + ClientImpl.prototype._buffered_msg_queue = null; + ClientImpl.prototype._connectTimeout = null; + /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ + ClientImpl.prototype.sendPinger = null; + /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ + ClientImpl.prototype.receivePinger = null; + ClientImpl.prototype._reconnectInterval = 1; // Reconnect Delay, starts at 1 second + ClientImpl.prototype._reconnecting = false; + ClientImpl.prototype._reconnectTimeout = null; + ClientImpl.prototype.disconnectedPublishing = false; + ClientImpl.prototype.disconnectedBufferSize = 5000; + + ClientImpl.prototype.receiveBuffer = null; + + ClientImpl.prototype._traceBuffer = null; + ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; + + ClientImpl.prototype.connect = function(connectOptions) { + var connectOptionsMasked = this._traceMask(connectOptions, "password"); + this._trace("Client.connect", connectOptionsMasked, null, this.connected); + + if (this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); + + if (this._reconnecting) { + // connect() function is called while reconnect is in progress. + // Terminate the auto reconnect process to use new connect options. + this._reconnectTimeout.cancel(); + this._reconnectTimeout = null; + this._reconnecting = false; + } + + this.connectOptions = connectOptions; + this._reconnectInterval = 1; + this._reconnecting = false; + if (connectOptions.uris) { + this.hostIndex = 0; + this._doConnect(connectOptions.uris[0]); + } else { + this._doConnect(this.uri); + } + + }; + + ClientImpl.prototype.subscribe = function(filter, subscribeOptions) { + this._trace("Client.subscribe", filter, subscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); + wireMessage.topics = [filter]; + if (subscribeOptions.qos !== undefined) + wireMessage.requestedQos = [subscribeOptions.qos]; + else + wireMessage.requestedQos = [0]; + + if (subscribeOptions.onSuccess) { + wireMessage.onSuccess = function(grantedQos) { subscribeOptions.onSuccess({ invocationContext: subscribeOptions.invocationContext, grantedQos: grantedQos }); }; + } + + if (subscribeOptions.onFailure) { + wireMessage.onFailure = function(errorCode) { subscribeOptions.onFailure({ invocationContext: subscribeOptions.invocationContext, errorCode: errorCode, errorMessage: format(errorCode) }); }; + } + + if (subscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, subscribeOptions.timeout, subscribeOptions.onFailure, [{ + invocationContext: subscribeOptions.invocationContext, + errorCode: ERROR.SUBSCRIBE_TIMEOUT.code, + errorMessage: format(ERROR.SUBSCRIBE_TIMEOUT) + }]); + } + + // All subscriptions return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + /** @ignore */ + ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { + this._trace("Client.unsubscribe", filter, unsubscribeOptions); + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); + wireMessage.topics = [filter]; + + if (unsubscribeOptions.onSuccess) { + wireMessage.callback = function() { unsubscribeOptions.onSuccess({ invocationContext: unsubscribeOptions.invocationContext }); }; + } + if (unsubscribeOptions.timeout) { + wireMessage.timeOut = new Timeout(this, unsubscribeOptions.timeout, unsubscribeOptions.onFailure, [{ + invocationContext: unsubscribeOptions.invocationContext, + errorCode: ERROR.UNSUBSCRIBE_TIMEOUT.code, + errorMessage: format(ERROR.UNSUBSCRIBE_TIMEOUT) + }]); + } + + // All unsubscribes return a SUBACK. + this._requires_ack(wireMessage); + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.send = function(message) { + this._trace("Client.send", message); + + var wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); + wireMessage.payloadMessage = message; + + if (this.connected) { + // Mark qos 1 & 2 message as "ACK required" + // For qos 0 message, invoke onMessageDelivered callback if there is one. + // Then schedule the message. + if (message.qos > 0) { + this._requires_ack(wireMessage); + } else if (this.onMessageDelivered) { + this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); + } + this._schedule_message(wireMessage); + } else { + // Currently disconnected, will not schedule this message + // Check if reconnecting is in progress and disconnected publish is enabled. + if (this._reconnecting && this.disconnectedPublishing) { + // Check the limit which include the "required ACK" messages + var messageCount = Object.keys(this._sentMessages).length + this._buffered_msg_queue.length; + if (messageCount > this.disconnectedBufferSize) { + throw new Error(format(ERROR.BUFFER_FULL, [this.disconnectedBufferSize])); + } else { + if (message.qos > 0) { + // Mark this message as "ACK required" + this._requires_ack(wireMessage); + } else { + wireMessage.sequence = ++this._sequence; + this._buffered_msg_queue.push(wireMessage); + } + } + } else { + throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); + } + } + }; + + ClientImpl.prototype.disconnect = function() { + this._trace("Client.disconnect"); + + if (this._reconnecting) { + // disconnect() function is called while reconnect is in progress. + // Terminate the auto reconnect process. + this._reconnectTimeout.cancel(); + this._reconnectTimeout = null; + this._reconnecting = false; + } + + if (!this.connected) + throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); + + var wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); + + // Run the disconnected call back as soon as the message has been sent, + // in case of a failure later on in the disconnect processing. + // as a consequence, the _disconected call back may be run several times. + this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); + + this._schedule_message(wireMessage); + }; + + ClientImpl.prototype.getTraceLog = function() { + if (this._traceBuffer !== null) { + this._trace("Client.getTraceLog", new Date()); + this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); + for (var key in this._sentMessages) + this._trace("_sentMessages ", key, this._sentMessages[key]); + for (var key in this._receivedMessages) + this._trace("_receivedMessages ", key, this._receivedMessages[key]); + + return this._traceBuffer; + } + }; + + ClientImpl.prototype.startTrace = function() { + if (this._traceBuffer === null) { + this._traceBuffer = []; + } + this._trace("Client.startTrace", new Date(), version); + }; + + ClientImpl.prototype.stopTrace = function() { + delete this._traceBuffer; + }; + + ClientImpl.prototype._doConnect = function(wsurl) { + // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. + if (this.connectOptions.useSSL) { + var uriParts = wsurl.split(":"); + uriParts[0] = "wss"; + wsurl = uriParts.join(":"); + } + this._wsuri = wsurl; + this.connected = false; + + wx.connectSocket({ + url: wsurl, + protocols: ['mqtt'] + }); + + wx.onSocketOpen(scope(this._on_socket_open, this)) + wx.onSocketMessage(scope(this._on_socket_message, this)) + wx.onSocketError(scope(this._on_socket_error, this)) + wx.onSocketClose(scope(this._on_socket_close, this)) + + this.sendPinger = new Pinger(this, this.connectOptions.keepAliveInterval); + this.receivePinger = new Pinger(this, this.connectOptions.keepAliveInterval); + if (this._connectTimeout) { + this._connectTimeout.cancel(); + this._connectTimeout = null; + } + this._connectTimeout = new Timeout(this, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); + }; + + + // Schedule a new message to be sent over the WebSockets + // connection. CONNECT messages cause WebSocket connection + // to be started. All other messages are queued internally + // until this has happened. When WS connection starts, process + // all outstanding messages. + ClientImpl.prototype._schedule_message = function(message) { + this._msg_queue.push(message); + // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. + if (this.connected) { + this._process_queue(); + } + }; + + ClientImpl.prototype.store = function(prefix, wireMessage) { + var storedMessage = { type: wireMessage.type, messageIdentifier: wireMessage.messageIdentifier, version: 1 }; + + switch (wireMessage.type) { + case MESSAGE_TYPE.PUBLISH: + if (wireMessage.pubRecReceived) + storedMessage.pubRecReceived = true; + + // Convert the payload to a hex string. + storedMessage.payloadMessage = {}; + var hex = ""; + var messageBytes = wireMessage.payloadMessage.payloadBytes; + for (var i = 0; i < messageBytes.length; i++) { + if (messageBytes[i] <= 0xF) + hex = hex + "0" + messageBytes[i].toString(16); + else + hex = hex + messageBytes[i].toString(16); + } + storedMessage.payloadMessage.payloadHex = hex; + + storedMessage.payloadMessage.qos = wireMessage.payloadMessage.qos; + storedMessage.payloadMessage.destinationName = wireMessage.payloadMessage.destinationName; + if (wireMessage.payloadMessage.duplicate) + storedMessage.payloadMessage.duplicate = true; + if (wireMessage.payloadMessage.retained) + storedMessage.payloadMessage.retained = true; + + // Add a sequence number to sent messages. + if (prefix.indexOf("Sent:") === 0) { + if (wireMessage.sequence === undefined) + wireMessage.sequence = ++this._sequence; + storedMessage.sequence = wireMessage.sequence; + } + break; + + default: + throw Error(format(ERROR.INVALID_STORED_DATA, [key, storedMessage])); + } + try { + wx.setStorageSync(prefix + this._localKey + wireMessage.messageIdentifier, JSON.stringify(storedMessage)); + } catch (e) { + + } + }; + + ClientImpl.prototype.restore = function(key) { + var value = wx.getStorageSync(key); + var storedMessage = JSON.parse(value); + + var wireMessage = new WireMessage(storedMessage.type, storedMessage); + + switch (storedMessage.type) { + case MESSAGE_TYPE.PUBLISH: + // Replace the payload message with a Message object. + var hex = storedMessage.payloadMessage.payloadHex; + var buffer = new ArrayBuffer((hex.length) / 2); + var byteStream = new Uint8Array(buffer); + var i = 0; + while (hex.length >= 2) { + var x = parseInt(hex.substring(0, 2), 16); + hex = hex.substring(2, hex.length); + byteStream[i++] = x; + } + var payloadMessage = new Message(byteStream); + + payloadMessage.qos = storedMessage.payloadMessage.qos; + payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; + if (storedMessage.payloadMessage.duplicate) + payloadMessage.duplicate = true; + if (storedMessage.payloadMessage.retained) + payloadMessage.retained = true; + wireMessage.payloadMessage = payloadMessage; + + break; + + default: + throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); + } + + if (key.indexOf("Sent:" + this._localKey) === 0) { + wireMessage.payloadMessage.duplicate = true; + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + } else if (key.indexOf("Received:" + this._localKey) === 0) { + this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; + } + }; + + ClientImpl.prototype._process_queue = function() { + var message = null; + // Process messages in order they were added + var fifo = this._msg_queue.reverse(); + + // Send all queued messages down socket connection + while ((message = fifo.pop())) { + this._socket_send(message); + // Notify listeners that message was successfully sent + if (this._notify_msg_sent[message]) { + this._notify_msg_sent[message](); + delete this._notify_msg_sent[message]; + } + } + }; + + /** + * Expect an ACK response for this message. Add message to the set of in progress + * messages and set an unused identifier in this message. + * @ignore + */ + ClientImpl.prototype._requires_ack = function(wireMessage) { + var messageCount = Object.keys(this._sentMessages).length; + if (messageCount > this.maxMessageIdentifier) + throw Error("Too many messages:" + messageCount); + + while (this._sentMessages[this._message_identifier] !== undefined) { + this._message_identifier++; + } + wireMessage.messageIdentifier = this._message_identifier; + this._sentMessages[wireMessage.messageIdentifier] = wireMessage; + if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { + this.store("Sent:", wireMessage); + } + if (this._message_identifier === this.maxMessageIdentifier) { + this._message_identifier = 1; + } + }; + + /** + * Called when the underlying websocket has been opened. + * @ignore + */ + ClientImpl.prototype._on_socket_open = function(res) { + // Create the CONNECT message object. + var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); + wireMessage.clientId = this.clientId; + this._socket_send(wireMessage); + }; + + /** + * Called when the underlying websocket has received a complete packet. + * @ignore + */ + ClientImpl.prototype._on_socket_message = function(event) { + this._trace("Client._on_socket_message", event.data); + var messages = this._deframeMessages(event.data); + for (var i = 0; i < messages.length; i += 1) { + this._handleMessage(messages[i]); + } + }; + + ClientImpl.prototype._deframeMessages = function(data) { + var byteArray = new Uint8Array(data); + var messages = []; + if (this.receiveBuffer) { + var newData = new Uint8Array(this.receiveBuffer.length + byteArray.length); + newData.set(this.receiveBuffer); + newData.set(byteArray, this.receiveBuffer.length); + byteArray = newData; + delete this.receiveBuffer; + } + try { + var offset = 0; + while (offset < byteArray.length) { + var result = decodeMessage(byteArray, offset); + var wireMessage = result[0]; + offset = result[1]; + if (wireMessage !== null) { + messages.push(wireMessage); + } else { + break; + } + } + if (offset < byteArray.length) { + this.receiveBuffer = byteArray.subarray(offset); + } + } catch (error) { + var errorStack = ((error.hasOwnProperty('stack') == 'undefined') ? error.stack.toString() : "No Error Stack Available"); + this._disconnected(ERROR.INTERNAL_ERROR.code, format(ERROR.INTERNAL_ERROR, [error.message, errorStack])); + return; + } + return messages; + }; + + ClientImpl.prototype._handleMessage = function(wireMessage) { + + this._trace("Client._handleMessage", wireMessage); + + try { + switch (wireMessage.type) { + case MESSAGE_TYPE.CONNACK: + this._connectTimeout.cancel(); + if (this._reconnectTimeout) + this._reconnectTimeout.cancel(); + + // If we have started using clean session then clear up the local state. + if (this.connectOptions.cleanSession) { + for (var key in this._sentMessages) { + var sentMessage = this._sentMessages[key]; + wx.removeStorageSync("Sent:" + this._localKey + sentMessage.messageIdentifier); + } + this._sentMessages = {}; + + for (var key in this._receivedMessages) { + var receivedMessage = this._receivedMessages[key]; + wx.removeStorageSync("Received:" + this._localKey + receivedMessage.messageIdentifier); + } + this._receivedMessages = {}; + } + // Client connected and ready for business. + if (wireMessage.returnCode === 0) { + + this.connected = true; + // Jump to the end of the list of uris and stop looking for a good host. + + if (this.connectOptions.uris) + this.hostIndex = this.connectOptions.uris.length; + + } else { + this._disconnected(ERROR.CONNACK_RETURNCODE.code, format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); + break; + } + + // Resend messages. + var sequencedMessages = []; + for (var msgId in this._sentMessages) { + if (this._sentMessages.hasOwnProperty(msgId)) + sequencedMessages.push(this._sentMessages[msgId]); + } + + // Also schedule qos 0 buffered messages if any + if (this._buffered_msg_queue.length > 0) { + var msg = null; + var fifo = this._buffered_msg_queue.reverse(); + while ((msg = fifo.pop())) { + sequencedMessages.push(msg); + if (this.onMessageDelivered) + this._notify_msg_sent[msg] = this.onMessageDelivered(msg.payloadMessage); + } + } + + // Sort sentMessages into the original sent order. + var sequencedMessages = sequencedMessages.sort(function(a, b) { return a.sequence - b.sequence; }); + for (var i = 0, len = sequencedMessages.length; i < len; i++) { + var sentMessage = sequencedMessages[i]; + if (sentMessage.type == MESSAGE_TYPE.PUBLISH && sentMessage.pubRecReceived) { + var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, { messageIdentifier: sentMessage.messageIdentifier }); + this._schedule_message(pubRelMessage); + } else { + this._schedule_message(sentMessage); + } + } + + // Execute the connectOptions.onSuccess callback if there is one. + // Will also now return if this connection was the result of an automatic + // reconnect and which URI was successfully connected to. + if (this.connectOptions.onSuccess) { + this.connectOptions.onSuccess({ invocationContext: this.connectOptions.invocationContext }); + } + + var reconnected = false; + if (this._reconnecting) { + reconnected = true; + this._reconnectInterval = 1; + this._reconnecting = false; + } + + // Execute the onConnected callback if there is one. + this._connected(reconnected, this._wsuri); + + // Process all queued messages now that the connection is established. + this._process_queue(); + break; + + case MESSAGE_TYPE.PUBLISH: + this._receivePublish(wireMessage); + break; + + case MESSAGE_TYPE.PUBACK: + var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; + // If this is a re flow of a PUBACK after we have restarted receivedMessage will not exist. + if (sentMessage) { + delete this._sentMessages[wireMessage.messageIdentifier]; + wx.removeStorageSync("Sent:" + this._localKey + wireMessage.messageIdentifier); + if (this.onMessageDelivered) + this.onMessageDelivered(sentMessage.payloadMessage); + } + break; + + case MESSAGE_TYPE.PUBREC: + var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; + // If this is a re flow of a PUBREC after we have restarted receivedMessage will not exist. + if (sentMessage) { + sentMessage.pubRecReceived = true; + var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, { messageIdentifier: wireMessage.messageIdentifier }); + this.store("Sent:", sentMessage); + this._schedule_message(pubRelMessage); + } + break; + + case MESSAGE_TYPE.PUBREL: + var receivedMessage = this._receivedMessages[wireMessage.messageIdentifier]; + wx.removeStorageSync("Received:" + this._localKey + wireMessage.messageIdentifier); + // If this is a re flow of a PUBREL after we have restarted receivedMessage will not exist. + if (receivedMessage) { + this._receiveMessage(receivedMessage); + delete this._receivedMessages[wireMessage.messageIdentifier]; + } + // Always flow PubComp, we may have previously flowed PubComp but the server lost it and restarted. + var pubCompMessage = new WireMessage(MESSAGE_TYPE.PUBCOMP, { messageIdentifier: wireMessage.messageIdentifier }); + this._schedule_message(pubCompMessage); + + + break; + + case MESSAGE_TYPE.PUBCOMP: + var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; + delete this._sentMessages[wireMessage.messageIdentifier]; + wx.removeStorageSync("Sent:" + this._localKey + wireMessage.messageIdentifier); + if (this.onMessageDelivered) + this.onMessageDelivered(sentMessage.payloadMessage); + break; + + case MESSAGE_TYPE.SUBACK: + var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; + if (sentMessage) { + if (sentMessage.timeOut) + sentMessage.timeOut.cancel(); + // This will need to be fixed when we add multiple topic support + if (wireMessage.returnCode[0] === 0x80) { + if (sentMessage.onFailure) { + sentMessage.onFailure(wireMessage.returnCode); + } + } else if (sentMessage.onSuccess) { + sentMessage.onSuccess(wireMessage.returnCode); + } + delete this._sentMessages[wireMessage.messageIdentifier]; + } + break; + + case MESSAGE_TYPE.UNSUBACK: + var sentMessage = this._sentMessages[wireMessage.messageIdentifier]; + if (sentMessage) { + if (sentMessage.timeOut) + sentMessage.timeOut.cancel(); + if (sentMessage.callback) { + sentMessage.callback(); + } + delete this._sentMessages[wireMessage.messageIdentifier]; + } + + break; + + case MESSAGE_TYPE.PINGRESP: + /* The sendPinger or receivePinger may have sent a ping, the receivePinger has already been reset. */ + this.sendPinger.reset(); + break; + + case MESSAGE_TYPE.DISCONNECT: + // Clients do not expect to receive disconnect packets. + this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code, format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type])); + break; + + default: + this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code, format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type])); + } + } catch (error) { + var errorStack = ((error.hasOwnProperty('stack') == 'undefined') ? error.stack.toString() : "No Error Stack Available"); + this._disconnected(ERROR.INTERNAL_ERROR.code, format(ERROR.INTERNAL_ERROR, [error.message, errorStack])); + return; + } + }; + + /** @ignore */ + ClientImpl.prototype._on_socket_error = function(error) { + if (!this._reconnecting) { + this._disconnected(ERROR.SOCKET_ERROR.code, format(ERROR.SOCKET_ERROR, [error.data])); + } + }; + + /** @ignore */ + ClientImpl.prototype._on_socket_close = function() { + if (!this._reconnecting) { + this._disconnected(ERROR.SOCKET_CLOSE.code, format(ERROR.SOCKET_CLOSE)); + } + }; + + /** @ignore */ + ClientImpl.prototype._socket_send = function(wireMessage) { + + if (wireMessage.type == 1) { + var wireMessageMasked = this._traceMask(wireMessage, "password"); + this._trace("Client._socket_send", wireMessageMasked); + } else this._trace("Client._socket_send", wireMessage); + + wx.sendSocketMessage({ + data: wireMessage.encode(), + success: function() { + // + }, + fail: function() { + // + }, + complete: function() { + // + } + }) + /* We have proved to the server we are alive. */ + this.sendPinger.reset(); + }; + + /** @ignore */ + ClientImpl.prototype._receivePublish = function(wireMessage) { + switch (wireMessage.payloadMessage.qos) { + case "undefined": + case 0: + this._receiveMessage(wireMessage); + break; + + case 1: + var pubAckMessage = new WireMessage(MESSAGE_TYPE.PUBACK, { messageIdentifier: wireMessage.messageIdentifier }); + this._schedule_message(pubAckMessage); + this._receiveMessage(wireMessage); + break; + + case 2: + this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; + this.store("Received:", wireMessage); + var pubRecMessage = new WireMessage(MESSAGE_TYPE.PUBREC, { messageIdentifier: wireMessage.messageIdentifier }); + this._schedule_message(pubRecMessage); + + break; + + default: + throw Error("Invaild qos=" + wireMmessage.payloadMessage.qos); + } + }; + + /** @ignore */ + ClientImpl.prototype._receiveMessage = function(wireMessage) { + if (this.onMessageArrived) { + this.onMessageArrived(wireMessage.payloadMessage); + } + }; + + /** + * Client has connected. + * @param {reconnect} [boolean] indicate if this was a result of reconnect operation. + * @param {uri} [string] fully qualified WebSocket URI of the server. + */ + ClientImpl.prototype._connected = function(reconnect, uri) { + // Execute the onConnected callback if there is one. + if (this.onConnected) + this.onConnected(reconnect, uri); + }; + + /** + * Attempts to reconnect the client to the server. + * For each reconnect attempt, will double the reconnect interval + * up to 128 seconds. + */ + ClientImpl.prototype._reconnect = function() { + this._trace("Client._reconnect"); + if (!this.connected) { + this._reconnecting = true; + this.sendPinger.cancel(); + this.receivePinger.cancel(); + if (this._reconnectInterval < 128) + this._reconnectInterval = this._reconnectInterval * 2; + if (this.connectOptions.uris) { + this.hostIndex = 0; + this._doConnect(this.connectOptions.uris[0]); + } else { + this._doConnect(this.uri); + } + } + }; + + /** + * Client has disconnected either at its own request or because the server + * or network disconnected it. Remove all non-durable state. + * @param {errorCode} [number] the error number. + * @param {errorText} [string] the error text. + * @ignore + */ + ClientImpl.prototype._disconnected = function(errorCode, errorText) { + this._trace("Client._disconnected", errorCode, errorText); + + if (errorCode !== undefined && this._reconnecting) { + //Continue automatic reconnect process + this._reconnectTimeout = new Timeout(this, this._reconnectInterval, this._reconnect); + return; + } + + this.sendPinger.cancel(); + this.receivePinger.cancel(); + if (this._connectTimeout) { + this._connectTimeout.cancel(); + this._connectTimeout = null; + } + + // Clear message buffers. + this._msg_queue = []; + this._buffered_msg_queue = []; + this._notify_msg_sent = {}; + + if (this.connectOptions.uris && this.hostIndex < this.connectOptions.uris.length - 1) { + // Try the next host. + this.hostIndex++; + this._doConnect(this.connectOptions.uris[this.hostIndex]); + } else { + + if (errorCode === undefined) { + errorCode = ERROR.OK.code; + errorText = format(ERROR.OK); + } + + // Run any application callbacks last as they may attempt to reconnect and hence create a new socket. + if (this.connected) { + this.connected = false; + // Execute the connectionLostCallback if there is one, and we were connected. + if (this.onConnectionLost) { + this.onConnectionLost({ errorCode: errorCode, errorMessage: errorText, reconnect: this.connectOptions.reconnect, uri: this._wsuri }); + } + if (errorCode !== ERROR.OK.code && this.connectOptions.reconnect) { + // Start automatic reconnect process for the very first time since last successful connect. + this._reconnectInterval = 1; + this._reconnect(); + return; + } + } else { + // Otherwise we never had a connection, so indicate that the connect has failed. + if (this.connectOptions.mqttVersion === 4 && this.connectOptions.mqttVersionExplicit === false) { + this._trace("Failed to connect V4, dropping back to V3"); + this.connectOptions.mqttVersion = 3; + if (this.connectOptions.uris) { + this.hostIndex = 0; + this._doConnect(this.connectOptions.uris[0]); + } else { + this._doConnect(this.uri); + } + } else if (this.connectOptions.onFailure) { + this.connectOptions.onFailure({ invocationContext: this.connectOptions.invocationContext, errorCode: errorCode, errorMessage: errorText }); + } + } + } + }; + + /** @ignore */ + ClientImpl.prototype._trace = function() { + // Pass trace message back to client's callback function + if (this.traceFunction) { + for (var i in arguments) { + if (typeof arguments[i] !== "undefined") + arguments.splice(i, 1, JSON.stringify(arguments[i])); + } + var record = Array.prototype.slice.call(arguments).join(""); + this.traceFunction({ severity: "Debug", message: record }); + } + + //buffer style trace + if (this._traceBuffer !== null) { + for (var i = 0, max = arguments.length; i < max; i++) { + if (this._traceBuffer.length == this._MAX_TRACE_ENTRIES) { + this._traceBuffer.shift(); + } + if (i === 0) this._traceBuffer.push(arguments[i]); + else if (typeof arguments[i] === "undefined") this._traceBuffer.push(arguments[i]); + else this._traceBuffer.push(" " + JSON.stringify(arguments[i])); + } + } + }; + + /** @ignore */ + ClientImpl.prototype._traceMask = function(traceObject, masked) { + var traceObjectMasked = {}; + for (var attr in traceObject) { + if (traceObject.hasOwnProperty(attr)) { + if (attr == masked) + traceObjectMasked[attr] = "******"; + else + traceObjectMasked[attr] = traceObject[attr]; + } + } + return traceObjectMasked; + }; + + // ------------------------------------------------------------------------ + // Public Programming interface. + // ------------------------------------------------------------------------ + + /** + * The JavaScript application communicates to the server using a {@link Paho.MQTT.Client} object. + *

+ * Most applications will create just one Client object and then call its connect() method, + * however applications can create more than one Client object if they wish. + * In this case the combination of host, port and clientId attributes must be different for each Client object. + *

+ * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods + * (even though the underlying protocol exchange might be synchronous in nature). + * This means they signal their completion by calling back to the application, + * via Success or Failure callback functions provided by the application on the method in question. + * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime + * of the script that made the invocation. + *

+ * In contrast there are some callback functions, most notably onMessageArrived, + * that are defined on the {@link Paho.MQTT.Client} object. + * These may get called multiple times, and aren't directly related to specific method invocations made by the client. + * + * @name Paho.MQTT.Client + * + * @constructor + * + * @param {string} host - the address of the messaging server, as a fully qualified WebSocket URI, as a DNS name or dotted decimal IP address. + * @param {number} port - the port number to connect to - only required if host is not a URI + * @param {string} path - the path on the host to connect to - only used if host is not a URI. Default: '/mqtt'. + * @param {string} clientId - the Messaging client identifier, between 1 and 23 characters in length. + * + * @property {string} host - read only the server's DNS hostname or dotted decimal IP address. + * @property {number} port - read only the server's port. + * @property {string} path - read only the server's path. + * @property {string} clientId - read only used when connecting to the server. + * @property {function} onConnectionLost - called when a connection has been lost. + * after a connect() method has succeeded. + * Establish the call back used when a connection has been lost. The connection may be + * lost because the client initiates a disconnect or because the server or network + * cause the client to be disconnected. The disconnect call back may be called without + * the connectionComplete call back being invoked if, for example the client fails to + * connect. + * A single response object parameter is passed to the onConnectionLost callback containing the following fields: + *

    + *
  1. errorCode + *
  2. errorMessage + *
+ * @property {function} onMessageDelivered - called when a message has been delivered. + * All processing that this Client will ever do has been completed. So, for example, + * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server + * and the message has been removed from persistent storage before this callback is invoked. + * Parameters passed to the onMessageDelivered callback are: + *
    + *
  1. {@link Paho.MQTT.Message} that was delivered. + *
+ * @property {function} onMessageArrived - called when a message has arrived in this Paho.MQTT.client. + * Parameters passed to the onMessageArrived callback are: + *
    + *
  1. {@link Paho.MQTT.Message} that has arrived. + *
+ * @property {function} onConnected - called when a connection is successfully made to the server. + * after a connect() method. + * Parameters passed to the onConnected callback are: + *
    + *
  1. reconnect (boolean) - If true, the connection was the result of a reconnect.
  2. + *
  3. URI (string) - The URI used to connect to the server.
  4. + *
+ * @property {boolean} disconnectedPublishing - if set, will enable disconnected publishing in + * in the event that the connection to the server is lost. + * @property {number} disconnectedBufferSize - Used to set the maximum number of messages that the disconnected + * buffer will hold before rejecting new messages. Default size: 5000 messages + * @property {function} trace - called whenever trace is called. TODO + */ + var Client = function(host, port, path, clientId) { + + var uri; + + if (typeof host !== "string") + throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); + + if (arguments.length == 2) { + // host: must be full ws:// uri + // port: clientId + clientId = port; + uri = host; + var match = uri.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/); + if (match) { + host = match[4] || match[2]; + port = parseInt(match[7]); + path = match[8]; + } else { + throw new Error(format(ERROR.INVALID_ARGUMENT, [host, "host"])); + } + } else { + if (arguments.length == 3) { + clientId = path; + path = "/mqtt"; + } + if (typeof port !== "number" || port < 0) + throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); + if (typeof path !== "string") + throw new Error(format(ERROR.INVALID_TYPE, [typeof path, "path"])); + + var ipv6AddSBracket = (host.indexOf(":") !== -1 && host.slice(0, 1) !== "[" && host.slice(-1) !== "]"); + uri = "ws://" + (ipv6AddSBracket ? "[" + host + "]" : host) + ":" + port + path; + } + + var clientIdLength = 0; + for (var i = 0; i < clientId.length; i++) { + var charCode = clientId.charCodeAt(i); + if (0xD800 <= charCode && charCode <= 0xDBFF) { + i++; // Surrogate pair. + } + clientIdLength++; + } + if (typeof clientId !== "string" || clientIdLength > 65535) + throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); + + var client = new ClientImpl(uri, host, port, path, clientId); + this._getHost = function() { return host; }; + this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getPort = function() { return port; }; + this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getPath = function() { return path; }; + this._setPath = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getURI = function() { return uri; }; + this._setURI = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getClientId = function() { return client.clientId; }; + this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); }; + + this._getOnConnected = function() { return client.onConnected; }; + this._setOnConnected = function(newOnConnected) { + if (typeof newOnConnected === "function") + client.onConnected = newOnConnected; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnected, "onConnected"])); + }; + + this._getDisconnectedPublishing = function() { return client.disconnectedPublishing; }; + this._setDisconnectedPublishing = function(newDisconnectedPublishing) { + client.disconnectedPublishing = newDisconnectedPublishing; + }; + + this._getDisconnectedBufferSize = function() { return client.disconnectedBufferSize; }; + this._setDisconnectedBufferSize = function(newDisconnectedBufferSize) { + client.disconnectedBufferSize = newDisconnectedBufferSize; + }; + + this._getOnConnectionLost = function() { return client.onConnectionLost; }; + this._setOnConnectionLost = function(newOnConnectionLost) { + if (typeof newOnConnectionLost === "function") + client.onConnectionLost = newOnConnectionLost; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); + }; + + this._getOnMessageDelivered = function() { return client.onMessageDelivered; }; + this._setOnMessageDelivered = function(newOnMessageDelivered) { + if (typeof newOnMessageDelivered === "function") + client.onMessageDelivered = newOnMessageDelivered; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); + }; + + this._getOnMessageArrived = function() { return client.onMessageArrived; }; + this._setOnMessageArrived = function(newOnMessageArrived) { + if (typeof newOnMessageArrived === "function") + client.onMessageArrived = newOnMessageArrived; + else + throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); + }; + + this._getTrace = function() { return client.traceFunction; }; + this._setTrace = function(trace) { + if (typeof trace === "function") { + client.traceFunction = trace; + } else { + throw new Error(format(ERROR.INVALID_TYPE, [typeof trace, "onTrace"])); + } + }; + + /** + * Connect this Messaging client to its server. + * + * @name Paho.MQTT.Client#connect + * @function + * @param {object} connectOptions - Attributes used with the connection. + * @param {number} connectOptions.timeout - If the connect has not succeeded within this + * number of seconds, it is deemed to have failed. + * The default is 30 seconds. + * @param {string} connectOptions.userName - Authentication username for this connection. + * @param {string} connectOptions.password - Authentication password for this connection. + * @param {Paho.MQTT.Message} connectOptions.willMessage - sent by the server when the client + * disconnects abnormally. + * @param {number} connectOptions.keepAliveInterval - the server disconnects this client if + * there is no activity for this number of seconds. + * The default value of 60 seconds is assumed if not set. + * @param {boolean} connectOptions.cleanSession - if true(default) the client and server + * persistent state is deleted on successful connect. + * @param {boolean} connectOptions.useSSL - if present and true, use an SSL Websocket connection. + * @param {object} connectOptions.invocationContext - passed to the onSuccess callback or onFailure callback. + * @param {function} connectOptions.onSuccess - called when the connect acknowledgement + * has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *
    + *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. + *
+ * @param {function} connectOptions.onFailure - called when the connect request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext as passed in to the onFailure method in the connectOptions. + *
  2. errorCode a number indicating the nature of the error. + *
  3. errorMessage text describing the error. + *
+ * @param {array} connectOptions.hosts - If present this contains either a set of hostnames or fully qualified + * WebSocket URIs (ws://iot.eclipse.org:80/ws), that are tried in order in place + * of the host and port paramater on the construtor. The hosts are tried one at at time in order until + * one of then succeeds. + * @param {array} connectOptions.ports - If present the set of ports matching the hosts. If hosts contains URIs, this property + * is not used. + * @param {boolean} connectOptions.reconnect - Sets whether the client will automatically attempt to reconnect + * to the server if the connection is lost. + *
    + *
  • If set to false, the client will not attempt to automatically reconnect to the server in the event that the + * connection is lost.
  • + *
  • If set to true, in the event that the connection is lost, the client will attempt to reconnect to the server. + * It will initially wait 1 second before it attempts to reconnect, for every failed reconnect attempt, the delay + * will double until it is at 2 minutes at which point the delay will stay at 2 minutes.
  • + *
+ * @param {number} connectOptions.mqttVersion - The version of MQTT to use to connect to the MQTT Broker. + *
    + *
  • 3 - MQTT V3.1
  • + *
  • 4 - MQTT V3.1.1
  • + *
+ * @param {boolean} connectOptions.mqttVersionExplicit - If set to true, will force the connection to use the + * selected MQTT Version or will fail to connect. + * @param {array} connectOptions.uris - If present, should contain a list of fully qualified WebSocket uris + * (e.g. ws://iot.eclipse.org:80/ws), that are tried in order in place of the host and port parameter of the construtor. + * The uris are tried one at a time in order until one of them succeeds. Do not use this in conjunction with hosts as + * the hosts array will be converted to uris and will overwrite this property. + * @throws {InvalidState} If the client is not in disconnected state. The client must have received connectionLost + * or disconnected before calling connect for a second or subsequent time. + */ + this.connect = function(connectOptions) { + connectOptions = connectOptions || {}; + validate(connectOptions, { + timeout: "number", + userName: "string", + password: "string", + willMessage: "object", + keepAliveInterval: "number", + cleanSession: "boolean", + useSSL: "boolean", + invocationContext: "object", + onSuccess: "function", + onFailure: "function", + hosts: "object", + ports: "object", + reconnect: "boolean", + mqttVersion: "number", + mqttVersionExplicit: "boolean", + uris: "object" + }); + + // If no keep alive interval is set, assume 60 seconds. + if (connectOptions.keepAliveInterval === undefined) + connectOptions.keepAliveInterval = 60; + + if (connectOptions.mqttVersion > 4 || connectOptions.mqttVersion < 3) { + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.mqttVersion, "connectOptions.mqttVersion"])); + } + + if (connectOptions.mqttVersion === undefined) { + connectOptions.mqttVersionExplicit = false; + connectOptions.mqttVersion = 4; + } else { + connectOptions.mqttVersionExplicit = true; + } + + //Check that if password is set, so is username + if (connectOptions.password !== undefined && connectOptions.userName === undefined) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.password, "connectOptions.password"])); + + if (connectOptions.willMessage) { + if (!(connectOptions.willMessage instanceof Message)) + throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); + // The will message must have a payload that can be represented as a string. + // Cause the willMessage to throw an exception if this is not the case. + connectOptions.willMessage.stringPayload = null; + + if (typeof connectOptions.willMessage.destinationName === "undefined") + throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); + } + if (typeof connectOptions.cleanSession === "undefined") + connectOptions.cleanSession = true; + if (connectOptions.hosts) { + + if (!(connectOptions.hosts instanceof Array)) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + if (connectOptions.hosts.length < 1) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); + + var usingURIs = false; + for (var i = 0; i < connectOptions.hosts.length; i++) { + if (typeof connectOptions.hosts[i] !== "string") + throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.hosts[i], "connectOptions.hosts[" + i + "]"])); + if (/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(connectOptions.hosts[i])) { + if (i === 0) { + usingURIs = true; + } else if (!usingURIs) { + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts[" + i + "]"])); + } + } else if (usingURIs) { + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts[" + i + "]"])); + } + } + + if (!usingURIs) { + if (!connectOptions.ports) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (!(connectOptions.ports instanceof Array)) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + if (connectOptions.hosts.length !== connectOptions.ports.length) + throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"])); + + connectOptions.uris = []; + + for (var i = 0; i < connectOptions.hosts.length; i++) { + if (typeof connectOptions.ports[i] !== "number" || connectOptions.ports[i] < 0) + throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.ports[i], "connectOptions.ports[" + i + "]"])); + var host = connectOptions.hosts[i]; + var port = connectOptions.ports[i]; + + var ipv6 = (host.indexOf(":") !== -1); + uri = "ws://" + (ipv6 ? "[" + host + "]" : host) + ":" + port + path; + connectOptions.uris.push(uri); + } + } else { + connectOptions.uris = connectOptions.hosts; + } + } + + client.connect(connectOptions); + }; + + /** + * Subscribe for messages, request receipt of a copy of messages sent to the destinations described by the filter. + * + * @name Paho.MQTT.Client#subscribe + * @function + * @param {string} filter describing the destinations to receive messages from. + *
+ * @param {object} subscribeOptions - used to control the subscription + * + * @param {number} subscribeOptions.qos - the maiximum qos of any publications sent + * as a result of making this subscription. + * @param {object} subscribeOptions.invocationContext - passed to the onSuccess callback + * or onFailure callback. + * @param {function} subscribeOptions.onSuccess - called when the subscribe acknowledgement + * has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *
    + *
  1. invocationContext if set in the subscribeOptions. + *
+ * @param {function} subscribeOptions.onFailure - called when the subscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext - if set in the subscribeOptions. + *
  2. errorCode - a number indicating the nature of the error. + *
  3. errorMessage - text describing the error. + *
+ * @param {number} subscribeOptions.timeout - which, if present, determines the number of + * seconds after which the onFailure calback is called. + * The presence of a timeout does not prevent the onSuccess + * callback from being called when the subscribe completes. + * @throws {InvalidState} if the client is not in connected state. + */ + this.subscribe = function(filter, subscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:" + filter); + subscribeOptions = subscribeOptions || {}; + validate(subscribeOptions, { + qos: "number", + invocationContext: "object", + onSuccess: "function", + onFailure: "function", + timeout: "number" + }); + if (subscribeOptions.timeout && !subscribeOptions.onFailure) + throw new Error("subscribeOptions.timeout specified with no onFailure callback."); + if (typeof subscribeOptions.qos !== "undefined" && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2)) + throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); + client.subscribe(filter, subscribeOptions); + }; + + /** + * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. + * + * @name Paho.MQTT.Client#unsubscribe + * @function + * @param {string} filter - describing the destinations to receive messages from. + * @param {object} unsubscribeOptions - used to control the subscription + * @param {object} unsubscribeOptions.invocationContext - passed to the onSuccess callback + or onFailure callback. + * @param {function} unsubscribeOptions.onSuccess - called when the unsubscribe acknowledgement has been received from the server. + * A single response object parameter is passed to the + * onSuccess callback containing the following fields: + *
    + *
  1. invocationContext - if set in the unsubscribeOptions. + *
+ * @param {function} unsubscribeOptions.onFailure called when the unsubscribe request has failed or timed out. + * A single response object parameter is passed to the onFailure callback containing the following fields: + *
    + *
  1. invocationContext - if set in the unsubscribeOptions. + *
  2. errorCode - a number indicating the nature of the error. + *
  3. errorMessage - text describing the error. + *
+ * @param {number} unsubscribeOptions.timeout - which, if present, determines the number of seconds + * after which the onFailure callback is called. The presence of + * a timeout does not prevent the onSuccess callback from being + * called when the unsubscribe completes + * @throws {InvalidState} if the client is not in connected state. + */ + this.unsubscribe = function(filter, unsubscribeOptions) { + if (typeof filter !== "string") + throw new Error("Invalid argument:" + filter); + unsubscribeOptions = unsubscribeOptions || {}; + validate(unsubscribeOptions, { + invocationContext: "object", + onSuccess: "function", + onFailure: "function", + timeout: "number" + }); + if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) + throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); + client.unsubscribe(filter, unsubscribeOptions); + }; + + /** + * Send a message to the consumers of the destination in the Message. + * + * @name Paho.MQTT.Client#send + * @function + * @param {string|Paho.MQTT.Message} topic - mandatory The name of the destination to which the message is to be sent. + * - If it is the only parameter, used as Paho.MQTT.Message object. + * @param {String|ArrayBuffer} payload - The message data to be sent. + * @param {number} qos The Quality of Service used to deliver the message. + *
+ *
0 Best effort (default). + *
1 At least once. + *
2 Exactly once. + *
+ * @param {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + * @throws {InvalidState} if the client is not connected. + */ + this.send = function(topic, payload, qos, retained) { + var message; + + if (arguments.length === 0) { + throw new Error("Invalid argument." + "length"); + + } else if (arguments.length == 1) { + + if (!(topic instanceof Message) && (typeof topic !== "string")) + throw new Error("Invalid argument:" + typeof topic); + + message = topic; + if (typeof message.destinationName === "undefined") + throw new Error(format(ERROR.INVALID_ARGUMENT, [message.destinationName, "Message.destinationName"])); + client.send(message); + + } else { + //parameter checking in Message object + message = new Message(payload); + message.destinationName = topic; + if (arguments.length >= 3) + message.qos = qos; + if (arguments.length >= 4) + message.retained = retained; + client.send(message); + } + }; + + /** + * Publish a message to the consumers of the destination in the Message. + * Synonym for Paho.Mqtt.Client#send + * + * @name Paho.MQTT.Client#publish + * @function + * @param {string|Paho.MQTT.Message} topic - mandatory The name of the topic to which the message is to be published. + * - If it is the only parameter, used as Paho.MQTT.Message object. + * @param {String|ArrayBuffer} payload - The message data to be published. + * @param {number} qos The Quality of Service used to deliver the message. + *
+ *
0 Best effort (default). + *
1 At least once. + *
2 Exactly once. + *
+ * @param {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + * @throws {InvalidState} if the client is not connected. + */ + this.publish = function(topic, payload, qos, retained) { + console.log("Publising message to: ", topic); + var message; + + if (arguments.length === 0) { + throw new Error("Invalid argument." + "length"); + + } else if (arguments.length == 1) { + + if (!(topic instanceof Message) && (typeof topic !== "string")) + throw new Error("Invalid argument:" + typeof topic); + + message = topic; + if (typeof message.destinationName === "undefined") + throw new Error(format(ERROR.INVALID_ARGUMENT, [message.destinationName, "Message.destinationName"])); + client.send(message); + + } else { + //parameter checking in Message object + message = new Message(payload); + message.destinationName = topic; + if (arguments.length >= 3) + message.qos = qos; + if (arguments.length >= 4) + message.retained = retained; + client.send(message); + } + }; + + /** + * Normal disconnect of this Messaging client from its server. + * + * @name Paho.MQTT.Client#disconnect + * @function + * @throws {InvalidState} if the client is already disconnected. + */ + this.disconnect = function() { + client.disconnect(); + }; + + /** + * Get the contents of the trace log. + * + * @name Paho.MQTT.Client#getTraceLog + * @function + * @return {Object[]} tracebuffer containing the time ordered trace records. + */ + this.getTraceLog = function() { + return client.getTraceLog(); + }; + + /** + * Start tracing. + * + * @name Paho.MQTT.Client#startTrace + * @function + */ + this.startTrace = function() { + client.startTrace(); + }; + + /** + * Stop tracing. + * + * @name Paho.MQTT.Client#stopTrace + * @function + */ + this.stopTrace = function() { + client.stopTrace(); + }; + + this.isConnected = function() { + return client.connected; + }; + }; + + Client.prototype = { + get host() { return this._getHost(); }, + set host(newHost) { this._setHost(newHost); }, + + get port() { return this._getPort(); }, + set port(newPort) { this._setPort(newPort); }, + + get path() { return this._getPath(); }, + set path(newPath) { this._setPath(newPath); }, + + get clientId() { return this._getClientId(); }, + set clientId(newClientId) { this._setClientId(newClientId); }, + + get onConnected() { return this._getOnConnected(); }, + set onConnected(newOnConnected) { this._setOnConnected(newOnConnected); }, + + get disconnectedPublishing() { return this._getDisconnectedPublishing(); }, + set disconnectedPublishing(newDisconnectedPublishing) { this._setDisconnectedPublishing(newDisconnectedPublishing); }, + + get disconnectedBufferSize() { return this._getDisconnectedBufferSize(); }, + set disconnectedBufferSize(newDisconnectedBufferSize) { this._setDisconnectedBufferSize(newDisconnectedBufferSize); }, + + get onConnectionLost() { return this._getOnConnectionLost(); }, + set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); }, + + get onMessageDelivered() { return this._getOnMessageDelivered(); }, + set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); }, + + get onMessageArrived() { return this._getOnMessageArrived(); }, + set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); }, + + get trace() { return this._getTrace(); }, + set trace(newTraceFunction) { this._setTrace(newTraceFunction); } + + }; + + /** + * An application message, sent or received. + *

+ * All attributes may be null, which implies the default values. + * + * @name Paho.MQTT.Message + * @constructor + * @param {String|ArrayBuffer} payload The message data to be sent. + *

+ * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. + * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. + *

+ * @property {string} destinationName mandatory The name of the destination to which the message is to be sent + * (for messages about to be sent) or the name of the destination from which the message has been received. + * (for messages received by the onMessage function). + *

+ * @property {number} qos The Quality of Service used to deliver the message. + *

+ *
0 Best effort (default). + *
1 At least once. + *
2 Exactly once. + *
+ *

+ * @property {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + *

+ * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. + * This is only set on messages received from the server. + * + */ + var Message = function(newPayload) { + var payload; + if (typeof newPayload === "string" || + newPayload instanceof ArrayBuffer || + newPayload instanceof Int8Array || + newPayload instanceof Uint8Array || + newPayload instanceof Int16Array || + newPayload instanceof Uint16Array || + newPayload instanceof Int32Array || + newPayload instanceof Uint32Array || + newPayload instanceof Float32Array || + newPayload instanceof Float64Array + ) { + payload = newPayload; + } else { + throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); + } + + this._getPayloadString = function() { + if (typeof payload === "string") + return payload; + else + return parseUTF8(payload, 0, payload.length); + }; + + this._getPayloadBytes = function() { + if (typeof payload === "string") { + var buffer = new ArrayBuffer(UTF8Length(payload)); + var byteStream = new Uint8Array(buffer); + stringToUTF8(payload, byteStream, 0); + + return byteStream; + } else { + return payload; + } + }; + + var destinationName; + this._getDestinationName = function() { return destinationName; }; + this._setDestinationName = function(newDestinationName) { + if (typeof newDestinationName === "string") + destinationName = newDestinationName; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); + }; + + var qos = 0; + this._getQos = function() { return qos; }; + this._setQos = function(newQos) { + if (newQos === 0 || newQos === 1 || newQos === 2) + qos = newQos; + else + throw new Error("Invalid argument:" + newQos); + }; + + var retained = false; + this._getRetained = function() { return retained; }; + this._setRetained = function(newRetained) { + if (typeof newRetained === "boolean") + retained = newRetained; + else + throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); + }; + + var duplicate = false; + this._getDuplicate = function() { return duplicate; }; + this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; }; + }; + + Message.prototype = { + get payloadString() { return this._getPayloadString(); }, + get payloadBytes() { return this._getPayloadBytes(); }, + + get destinationName() { return this._getDestinationName(); }, + set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); }, + + get topic() { return this._getDestinationName(); }, + set topic(newTopic) { this._setDestinationName(newTopic); }, + + get qos() { return this._getQos(); }, + set qos(newQos) { this._setQos(newQos); }, + + get retained() { return this._getRetained(); }, + set retained(newRetained) { this._setRetained(newRetained); }, + + get duplicate() { return this._getDuplicate(); }, + set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); } + }; + + // Module contents. + return { + Client: Client, + Message: Message + }; + })(wx); + return PahoMQTT; +}); \ No newline at end of file diff --git a/wechat-client/utils/util.js b/wechat-client/utils/util.js new file mode 100644 index 0000000..dbadbb8 --- /dev/null +++ b/wechat-client/utils/util.js @@ -0,0 +1,19 @@ +const formatTime = date => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + + return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') +} + +const formatNumber = n => { + n = n.toString() + return n[1] ? n : '0' + n +} + +module.exports = { + formatTime: formatTime +}