From 448efbab6c186601ba43fb36a334f4309677b410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=AE=8F=E9=9F=AC?= <3183764662@qq.com> Date: Thu, 1 Nov 2018 15:23:01 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 124 +- h5/chat.html | 105 - h5/home.html | 47 - h5/index.html | 67 - h5/logoSmall.png | Bin 8661 -> 0 bytes mqttclient/mqttclient/.gitignore | 25 - .../mqttclient/.mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 - mqttclient/mqttclient/mvnw | 225 -- mqttclient/mqttclient/mvnw.cmd | 143 - mqttclient/mqttclient/pom.xml | 50 - .../mqttclient/MqttclientApplication.java | 12 - .../src/main/resources/application.properties | 0 .../MqttclientApplicationTests.java | 16 - pom.xml | 20 +- sql/nettychat.sql | 71 - .../nettychat/NettychatApplication.java | 33 +- .../nettychat/common/utils/ResultVOUtil.java | 30 - .../myself/nettychat/config/NettyConfig.java | 75 - .../config/NettyTcpChannelInitializer.java | 34 - .../nettychat/config/NettyTcpConfig.java | 76 - .../NettyWebSocketChannelInitializer.java | 40 - .../myself/nettychat/config/TCPServer.java | 58 - .../nettychat/config/TCPServerHandler.java | 260 -- .../config/TextWebSocketFrameHandler.java | 141 - .../constont/LikeSomeCacheTemplate.java | 36 - .../controller/NCBackController.java | 58 - .../controller/NcChangeController.java | 88 - .../controller/NcChatController.java | 76 - .../controller/NcLoginController.java | 145 - .../com/myself/nettychat/dataobject/User.java | 39 - .../myself/nettychat/dataobject/UserMsg.java | 38 - .../com/myself/nettychat/form/LoginForm.java | 20 - .../repository/UserMsgRepository.java | 13 - .../nettychat/repository/UserRepository.java | 16 - .../myself/nettychat/service/UserService.java | 22 - .../service/impl/UserServiceImpl.java | 43 - .../myself/nettychat/task/MsgAsyncTesk.java | 50 - .../myself/nettychat/tcptest/CRC16myself.java | 101 - .../nettychat/tcptest/TCPTestClient.java | 82 - .../com/myself/nettychat/vo/ResultVo.java | 28 - src/main/resources/application.yml | 19 +- src/main/resources/static/css/allchat.css | 59 - src/main/resources/static/css/chat.css | 43 - src/main/resources/static/css/newChat.css | 172 -- src/main/resources/static/css/registered.css | 160 -- src/main/resources/static/image/logoBig.jpg | Bin 137374 -> 0 bytes src/main/resources/static/image/logoSmall.png | Bin 8661 -> 0 bytes src/main/resources/static/image/nuandao.png | Bin 62980 -> 0 bytes src/main/resources/static/js/chat.js | 51 - src/main/resources/static/js/newChat.js | 165 -- src/main/resources/static/js/registered.js | 72 - src/main/resources/templates/chat/allchat.ftl | 49 - src/main/resources/templates/chat/chat.ftl | 33 - src/main/resources/templates/common/floor.ftl | 3 - .../resources/templates/common/header.ftl | 12 - src/main/resources/templates/find/find.ftl | 41 - src/main/resources/templates/h5.ftl | 58 - src/main/resources/templates/home/home.ftl | 62 - src/main/resources/templates/login/login.ftl | 57 - .../resources/templates/login/loginSui.ftl | 124 - src/main/resources/templates/me/me.ftl | 78 - .../myself/nettychat/MqttPublishSample.java | 51 + wechat-client/app.js | 50 + wechat-client/app.json | 32 + wechat-client/app.wxss | 10 + wechat-client/pages/connect/connect.js | 204 ++ wechat-client/pages/connect/connect.json | 1 + wechat-client/pages/connect/connect.wxml | 14 + wechat-client/pages/connect/connect.wxss | 20 + wechat-client/pages/index/index.js | 54 + wechat-client/pages/index/index.json | 1 + wechat-client/pages/index/index.wxml | 13 + wechat-client/pages/index/index.wxss | 21 + wechat-client/pages/logs/logs.js | 15 + wechat-client/pages/logs/logs.json | 3 + wechat-client/pages/logs/logs.wxml | 6 + wechat-client/pages/logs/logs.wxss | 8 + wechat-client/pages/message/message.js | 79 + wechat-client/pages/message/message.json | 1 + wechat-client/pages/message/message.wxml | 13 + wechat-client/pages/message/message.wxss | 43 + wechat-client/pages/publish/publish.js | 132 + wechat-client/pages/publish/publish.json | 1 + wechat-client/pages/publish/publish.wxml | 16 + wechat-client/pages/publish/publish.wxss | 14 + wechat-client/pages/subscribe/subscribe.js | 159 ++ wechat-client/pages/subscribe/subscribe.json | 1 + wechat-client/pages/subscribe/subscribe.wxml | 26 + wechat-client/pages/subscribe/subscribe.wxss | 36 + wechat-client/project.config.json | 28 + wechat-client/utils/paho-mqtt-min.js | 77 + wechat-client/utils/paho-mqtt.js | 2406 +++++++++++++++++ wechat-client/utils/util.js | 19 + 94 files changed, 3606 insertions(+), 3714 deletions(-) delete mode 100644 h5/chat.html delete mode 100644 h5/home.html delete mode 100644 h5/index.html delete mode 100644 h5/logoSmall.png delete mode 100644 mqttclient/mqttclient/.gitignore delete mode 100644 mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar delete mode 100644 mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties delete mode 100644 mqttclient/mqttclient/mvnw delete mode 100644 mqttclient/mqttclient/mvnw.cmd delete mode 100644 mqttclient/mqttclient/pom.xml delete mode 100644 mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java delete mode 100644 mqttclient/mqttclient/src/main/resources/application.properties delete mode 100644 mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java delete mode 100644 sql/nettychat.sql delete mode 100644 src/main/java/com/myself/nettychat/common/utils/ResultVOUtil.java delete mode 100644 src/main/java/com/myself/nettychat/config/NettyConfig.java delete mode 100644 src/main/java/com/myself/nettychat/config/NettyTcpChannelInitializer.java delete mode 100644 src/main/java/com/myself/nettychat/config/NettyTcpConfig.java delete mode 100644 src/main/java/com/myself/nettychat/config/NettyWebSocketChannelInitializer.java delete mode 100644 src/main/java/com/myself/nettychat/config/TCPServer.java delete mode 100644 src/main/java/com/myself/nettychat/config/TCPServerHandler.java delete mode 100644 src/main/java/com/myself/nettychat/config/TextWebSocketFrameHandler.java delete mode 100644 src/main/java/com/myself/nettychat/constont/LikeSomeCacheTemplate.java delete mode 100644 src/main/java/com/myself/nettychat/controller/NCBackController.java delete mode 100644 src/main/java/com/myself/nettychat/controller/NcChangeController.java delete mode 100644 src/main/java/com/myself/nettychat/controller/NcChatController.java delete mode 100644 src/main/java/com/myself/nettychat/controller/NcLoginController.java delete mode 100644 src/main/java/com/myself/nettychat/dataobject/User.java delete mode 100644 src/main/java/com/myself/nettychat/dataobject/UserMsg.java delete mode 100644 src/main/java/com/myself/nettychat/form/LoginForm.java delete mode 100644 src/main/java/com/myself/nettychat/repository/UserMsgRepository.java delete mode 100644 src/main/java/com/myself/nettychat/repository/UserRepository.java delete mode 100644 src/main/java/com/myself/nettychat/service/UserService.java delete mode 100644 src/main/java/com/myself/nettychat/service/impl/UserServiceImpl.java delete mode 100644 src/main/java/com/myself/nettychat/task/MsgAsyncTesk.java delete mode 100644 src/main/java/com/myself/nettychat/tcptest/CRC16myself.java delete mode 100644 src/main/java/com/myself/nettychat/tcptest/TCPTestClient.java delete mode 100644 src/main/java/com/myself/nettychat/vo/ResultVo.java delete mode 100644 src/main/resources/static/css/allchat.css delete mode 100644 src/main/resources/static/css/chat.css delete mode 100644 src/main/resources/static/css/newChat.css delete mode 100644 src/main/resources/static/css/registered.css delete mode 100644 src/main/resources/static/image/logoBig.jpg delete mode 100644 src/main/resources/static/image/logoSmall.png delete mode 100644 src/main/resources/static/image/nuandao.png delete mode 100644 src/main/resources/static/js/chat.js delete mode 100644 src/main/resources/static/js/newChat.js delete mode 100644 src/main/resources/static/js/registered.js delete mode 100644 src/main/resources/templates/chat/allchat.ftl delete mode 100644 src/main/resources/templates/chat/chat.ftl delete mode 100644 src/main/resources/templates/common/floor.ftl delete mode 100644 src/main/resources/templates/common/header.ftl delete mode 100644 src/main/resources/templates/find/find.ftl delete mode 100644 src/main/resources/templates/h5.ftl delete mode 100644 src/main/resources/templates/home/home.ftl delete mode 100644 src/main/resources/templates/login/login.ftl delete mode 100644 src/main/resources/templates/login/loginSui.ftl delete mode 100644 src/main/resources/templates/me/me.ftl create mode 100644 src/test/java/com/myself/nettychat/MqttPublishSample.java create mode 100644 wechat-client/app.js create mode 100644 wechat-client/app.json create mode 100644 wechat-client/app.wxss create mode 100644 wechat-client/pages/connect/connect.js create mode 100644 wechat-client/pages/connect/connect.json create mode 100644 wechat-client/pages/connect/connect.wxml create mode 100644 wechat-client/pages/connect/connect.wxss create mode 100644 wechat-client/pages/index/index.js create mode 100644 wechat-client/pages/index/index.json create mode 100644 wechat-client/pages/index/index.wxml create mode 100644 wechat-client/pages/index/index.wxss create mode 100644 wechat-client/pages/logs/logs.js create mode 100644 wechat-client/pages/logs/logs.json create mode 100644 wechat-client/pages/logs/logs.wxml create mode 100644 wechat-client/pages/logs/logs.wxss create mode 100644 wechat-client/pages/message/message.js create mode 100644 wechat-client/pages/message/message.json create mode 100644 wechat-client/pages/message/message.wxml create mode 100644 wechat-client/pages/message/message.wxss create mode 100644 wechat-client/pages/publish/publish.js create mode 100644 wechat-client/pages/publish/publish.json create mode 100644 wechat-client/pages/publish/publish.wxml create mode 100644 wechat-client/pages/publish/publish.wxss create mode 100644 wechat-client/pages/subscribe/subscribe.js create mode 100644 wechat-client/pages/subscribe/subscribe.json create mode 100644 wechat-client/pages/subscribe/subscribe.wxml create mode 100644 wechat-client/pages/subscribe/subscribe.wxss create mode 100644 wechat-client/project.config.json create mode 100644 wechat-client/utils/paho-mqtt-min.js create mode 100644 wechat-client/utils/paho-mqtt.js create mode 100644 wechat-client/utils/util.js diff --git a/README.md b/README.md index c98ce64..02645a9 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.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 08b5d51eb40008001afd2293b59a107f0bfc474d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8661 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 diff --git a/mqttclient/mqttclient/.gitignore b/mqttclient/mqttclient/.gitignore deleted file mode 100644 index 82eca33..0000000 --- a/mqttclient/mqttclient/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/build/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar b/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47610 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 diff --git a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties b/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 6c8c0e0..0000000 --- a/mqttclient/mqttclient/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip diff --git a/mqttclient/mqttclient/mvnw b/mqttclient/mqttclient/mvnw deleted file mode 100644 index 5bf251c..0000000 --- a/mqttclient/mqttclient/mvnw +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mqttclient/mqttclient/mvnw.cmd b/mqttclient/mqttclient/mvnw.cmd deleted file mode 100644 index 019bd74..0000000 --- a/mqttclient/mqttclient/mvnw.cmd +++ /dev/null @@ -1,143 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/mqttclient/mqttclient/pom.xml b/mqttclient/mqttclient/pom.xml deleted file mode 100644 index 2d1b502..0000000 --- a/mqttclient/mqttclient/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - com.myself - mqttclient - 0.0.1-SNAPSHOT - jar - - mqttclient - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 2.0.5.RELEASE - - - - - UTF-8 - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - diff --git a/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java b/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java deleted file mode 100644 index 569cf76..0000000 --- a/mqttclient/mqttclient/src/main/java/com/myself/mqttclient/MqttclientApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.myself.mqttclient; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class MqttclientApplication { - - public static void main(String[] args) { - SpringApplication.run(MqttclientApplication.class, args); - } -} diff --git a/mqttclient/mqttclient/src/main/resources/application.properties b/mqttclient/mqttclient/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java b/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java deleted file mode 100644 index 5c938a0..0000000 --- a/mqttclient/mqttclient/src/test/java/com/myself/mqttclient/MqttclientApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.myself.mqttclient; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class MqttclientApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/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 0c62304cc054799f416de6d121214a05ecd90830..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137374 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

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

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

全体用户

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

酥酥

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

酥酥

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

“酥酥”聊天室

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

登录

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

注册

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

酥酥

-
- -
-
-
    -
  • -
    -
    ${userName!''}
    -
    -
  • -
-
-
-
-
    -
  • -
    -
    收藏
    -
    -
  • -
-
-
-
    -
  • -
    -
    相册
    -
    -
  • -
-
-
-
    -
  • -
    -
    卡包
    -
    -
  • -
-
-
-
    -
  • -
    -
    表情
    -
    -
  • -
-
-
-
-<#include "../common/floor.ftl"> - - \ No newline at end of file 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 +} From 42d7c4284824f57eaa13ac46f39199ee01a9c6a5 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Sat, 17 Nov 2018 14:30:17 +0800 Subject: [PATCH 2/3] updateMD --- README.md | 43 +++++++++++++++++++++++-------------------- doc/doc.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 doc/doc.md diff --git a/README.md b/README.md index 02645a9..4f5716b 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,57 @@ -## paho-mqtt 分支介绍 +## paho-mqtt -本Demo是小程序端的Iot案例简单实现。 +This demo is a simple implementation of a small program-side IoT case. -### 一 +## 中文文档说明 -首先是配置修改,你可以在本分支的yml配置文件进行mqtt的配置,核心的参数是: +* [中文文档说明](doc/doc.md) + +### Server-side configuration + +The first is configuration modifications, which you can configure for MQTT in the YML configuration file in this branch, and the core parameters are: > ssl: false # 使用ssl加密 > > protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) -本项目使用的paho.js的mqtt连接形式,所以protocol要选择MQTT_WS_PAHO。项目目前是未加密的,启动ssl本案例暂时不能通讯。 -默认直接启动项目就好。 +This project uses the Paho.js mqtt connection form, so protocol to select Mqtt_ws_paho. +The project is currently unencrypted, starting SSL this case is temporarily unable to communicate. It is good to start the project directly by default. -> 项目启动后的地址 :ws://192.168.1.121:8094/mqtt +> Address after the project starts :ws://192.168.1.121:8094/mqtt -ws、与后缀mqtt是com.myself.nettychat.bootstrap.AbstractBootstrapServer.java中的配置 +WS, and Suffix MQTT is the configuration in Com.myself.nettychat.bootstrap.AbstractBootstrapServer.java -### 二 +### Small program Configuration -你需要小程序开发者工具,并默认认定你是具备基本的小程序开发经验的开发者,这里省略部分的基本配置,你只需要将本分支中**wechat-client**文件夹中的文件完全复制到你新建的小程序项目即可,调试情况下无需AppID -你需要注意的是pages/connect/connect.js中的第78行 +You need a small program developer tool, and by default that you are a developer with basic small program development experience, omit the basic configuration of the section here, you just need to completely copy the files in the **wechat-client** folder in this branch to your new small program project, debugging conditions do not need to AppID What you need to be aware of is the 78th line in Pages/connect/connect.js. ```javascript var client = new MQTT.Client("ws://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); ``` -这里就是小程序的连接地址配置,默认和项目启动的一致,你需要在小程序的连接页面填写你的 -**IP:端口** +Here is the small program connection address configuration, the default and project startup consistent, you need to fill in the small program on the connection page of your +**IP:Port** -然后就连接成功了,接着你可以在subscribe页面订阅一个主题,本Demo是订阅TEST。 +Then the connection succeeds, and then you can subscribe to a topic on the Subscribe page, and this demo is subscribed to test. ![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151707.png) -### 三 +### Java Analog MQTT Client -运行test中的com.myself.nettychat.MqttPublishSample,你需要修改成本机的配置,类似连接地址等 +To run the com.myself.nettychat.MqttPublishSample in test, you need to modify the configuration of the cost machine, similar to the connection address, etc. > String broker = "ws://192.168.1.121:8094/mqtt";//地址 -需要注意的是,你的topic也要与小程序订阅的主题一致哦! +It is important to note that your topic should also be consistent with the theme of the small program subscription Oh! -运行测试用例,模拟硬件发送信息 +Run test cases to simulate hardware sending information ![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) -### 四 +### Test -回到小程序的message页面,你可以看到接收到了消息 +Back to the Message page of the small program, you can see that the message was received ![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151723.png) diff --git a/doc/doc.md b/doc/doc.md new file mode 100644 index 0000000..f230b7c --- /dev/null +++ b/doc/doc.md @@ -0,0 +1,54 @@ +## paho-mqtt + +本Demo是小程序端的Iot案例简单实现。 + +### 服务端配置 + +首先是配置修改,你可以在本分支的yml配置文件进行mqtt的配置,核心的参数是: + +> ssl: false # 使用ssl加密 +> +> protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) + +本项目使用的paho.js的mqtt连接形式,所以protocol要选择MQTT_WS_PAHO。项目目前是未加密的,启动ssl本案例暂时不能通讯。 +默认直接启动项目就好。 + +> 项目启动后的地址 :ws://192.168.1.121:8094/mqtt + +ws、与后缀mqtt是com.myself.nettychat.bootstrap.AbstractBootstrapServer.java中的配置 + +### 小程序配置 + +你需要小程序开发者工具,并默认认定你是具备基本的小程序开发经验的开发者,这里省略部分的基本配置,你只需要将本分支中**wechat-client**文件夹中的文件完全复制到你新建的小程序项目即可,调试情况下无需AppID +你需要注意的是pages/connect/connect.js中的第78行 + +```javascript +var client = new MQTT.Client("ws://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); +``` + +这里就是小程序的连接地址配置,默认和项目启动的一致,你需要在小程序的连接页面填写你的 +**IP:端口** + +然后就连接成功了,接着你可以在subscribe页面订阅一个主题,本Demo是订阅TEST。 + +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151707.png) + +### Java模拟MQtt客户端 + +运行test中的com.myself.nettychat.MqttPublishSample,你需要修改成本机的配置,类似连接地址等 + +> String broker = "ws://192.168.1.121:8094/mqtt";//地址 + +需要注意的是,你的topic也要与小程序订阅的主题一致哦! + +运行测试用例,模拟硬件发送信息 + +![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) + + +### 测试 + +回到小程序的message页面,你可以看到接收到了消息 + +![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151723.png) From 0a0493af516ed80cdb1cb60b13134dd7459d6f48 Mon Sep 17 00:00:00 2001 From: hacker <3183764662@qq.com> Date: Thu, 6 Dec 2018 23:15:40 +0800 Subject: [PATCH 3/3] update --- README.md | 41 +++++++++++++++++++---------------------- doc/doc.md | 54 ------------------------------------------------------ 2 files changed, 19 insertions(+), 76 deletions(-) delete mode 100644 doc/doc.md diff --git a/README.md b/README.md index 4f5716b..f230b7c 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,54 @@ ## paho-mqtt -This demo is a simple implementation of a small program-side IoT case. +本Demo是小程序端的Iot案例简单实现。 -## 中文文档说明 +### 服务端配置 -* [中文文档说明](doc/doc.md) - -### Server-side configuration - -The first is configuration modifications, which you can configure for MQTT in the YML configuration file in this branch, and the core parameters are: +首先是配置修改,你可以在本分支的yml配置文件进行mqtt的配置,核心的参数是: > ssl: false # 使用ssl加密 > > protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) -This project uses the Paho.js mqtt connection form, so protocol to select Mqtt_ws_paho. -The project is currently unencrypted, starting SSL this case is temporarily unable to communicate. It is good to start the project directly by default. +本项目使用的paho.js的mqtt连接形式,所以protocol要选择MQTT_WS_PAHO。项目目前是未加密的,启动ssl本案例暂时不能通讯。 +默认直接启动项目就好。 -> Address after the project starts :ws://192.168.1.121:8094/mqtt +> 项目启动后的地址 :ws://192.168.1.121:8094/mqtt -WS, and Suffix MQTT is the configuration in Com.myself.nettychat.bootstrap.AbstractBootstrapServer.java +ws、与后缀mqtt是com.myself.nettychat.bootstrap.AbstractBootstrapServer.java中的配置 -### Small program Configuration +### 小程序配置 -You need a small program developer tool, and by default that you are a developer with basic small program development experience, omit the basic configuration of the section here, you just need to completely copy the files in the **wechat-client** folder in this branch to your new small program project, debugging conditions do not need to AppID What you need to be aware of is the 78th line in Pages/connect/connect.js. +你需要小程序开发者工具,并默认认定你是具备基本的小程序开发经验的开发者,这里省略部分的基本配置,你只需要将本分支中**wechat-client**文件夹中的文件完全复制到你新建的小程序项目即可,调试情况下无需AppID +你需要注意的是pages/connect/connect.js中的第78行 ```javascript var client = new MQTT.Client("ws://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); ``` -Here is the small program connection address configuration, the default and project startup consistent, you need to fill in the small program on the connection page of your -**IP:Port** +这里就是小程序的连接地址配置,默认和项目启动的一致,你需要在小程序的连接页面填写你的 +**IP:端口** -Then the connection succeeds, and then you can subscribe to a topic on the Subscribe page, and this demo is subscribed to test. +然后就连接成功了,接着你可以在subscribe页面订阅一个主题,本Demo是订阅TEST。 ![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151707.png) -### Java Analog MQTT Client +### Java模拟MQtt客户端 -To run the com.myself.nettychat.MqttPublishSample in test, you need to modify the configuration of the cost machine, similar to the connection address, etc. +运行test中的com.myself.nettychat.MqttPublishSample,你需要修改成本机的配置,类似连接地址等 > String broker = "ws://192.168.1.121:8094/mqtt";//地址 -It is important to note that your topic should also be consistent with the theme of the small program subscription Oh! +需要注意的是,你的topic也要与小程序订阅的主题一致哦! -Run test cases to simulate hardware sending information +运行测试用例,模拟硬件发送信息 ![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) -### Test +### 测试 -Back to the Message page of the small program, you can see that the message was received +回到小程序的message页面,你可以看到接收到了消息 ![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151723.png) diff --git a/doc/doc.md b/doc/doc.md deleted file mode 100644 index f230b7c..0000000 --- a/doc/doc.md +++ /dev/null @@ -1,54 +0,0 @@ -## paho-mqtt - -本Demo是小程序端的Iot案例简单实现。 - -### 服务端配置 - -首先是配置修改,你可以在本分支的yml配置文件进行mqtt的配置,核心的参数是: - -> ssl: false # 使用ssl加密 -> -> protocol: MQTT_WS_PAHO # MQTT MQTT_WS_MQTT(mqtts.js) MQTT_WS_PAHO(paho.js) - -本项目使用的paho.js的mqtt连接形式,所以protocol要选择MQTT_WS_PAHO。项目目前是未加密的,启动ssl本案例暂时不能通讯。 -默认直接启动项目就好。 - -> 项目启动后的地址 :ws://192.168.1.121:8094/mqtt - -ws、与后缀mqtt是com.myself.nettychat.bootstrap.AbstractBootstrapServer.java中的配置 - -### 小程序配置 - -你需要小程序开发者工具,并默认认定你是具备基本的小程序开发经验的开发者,这里省略部分的基本配置,你只需要将本分支中**wechat-client**文件夹中的文件完全复制到你新建的小程序项目即可,调试情况下无需AppID -你需要注意的是pages/connect/connect.js中的第78行 - -```javascript -var client = new MQTT.Client("ws://" + this.data.server_addr+"/mqtt", "clientId_" + Math.random().toString(36).substr(2)); -``` - -这里就是小程序的连接地址配置,默认和项目启动的一致,你需要在小程序的连接页面填写你的 -**IP:端口** - -然后就连接成功了,接着你可以在subscribe页面订阅一个主题,本Demo是订阅TEST。 - -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151707.png) - -### Java模拟MQtt客户端 - -运行test中的com.myself.nettychat.MqttPublishSample,你需要修改成本机的配置,类似连接地址等 - -> String broker = "ws://192.168.1.121:8094/mqtt";//地址 - -需要注意的是,你的topic也要与小程序订阅的主题一致哦! - -运行测试用例,模拟硬件发送信息 - -![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) - - -### 测试 - -回到小程序的message页面,你可以看到接收到了消息 - -![Image text](https://raw.githubusercontent.com/UncleCatMySelf/img-myself/master/img/inchat-mqtt/TIM%E5%9B%BE%E7%89%8720181101151723.png)