微信蓝牙BLE接入调试指引
第三方服务器篇
3 构建第三方服务器
服务器的功能主要是接收微信发过来的绑定、解绑、菜单等事件,以及微信发过来的文本、设备发过来的数据等。
3.1 编译服务程序
QQ提供了服务程序的DEMO,下载地址如下:
Nordic nRF51822接入服务器端源代码
http://iot.weixin.qq.com/wiki/doc/blue/BlueDemoServer.zip
下载编译工具:
Download Java 1.7 64位
http://www.cr173.com/soft/55503.html
或者
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
Install and set JAVA_HOME=C:\Program Files\Java\jdk1.7.0_80
Download Eclipse 64位 java EE版
http://www.eclipse.org/downloads/
测试的电脑系统是WIN10 64位,其他系统或情况,自决。
导入代码:
打开eclipse,点菜单File-》Import,弹出如下界面:
选择General->Existing Projects into Workspace,点“Next”,选“Select root directory”,浏览BlueDemoServer目录,完成导入。
修改src/config.properties,
appID=微信公众号的appID
appsecret=微信公众号的appsecret
token=自定义,访问令牌,是微信访问第三方服务器时需要附带的参数
BlueDemoServer代码包不是通过eclipse中的run来编译的,而是通过Ant插件来编译,编译脚本是build.xml,操作步骤如下:
点击菜单栏“Window”->“Show View”->“Ant”,打开Ant界面:
然后,将Package Explorer里的build.xml拖拉到Ant界面里。
展开bluelight,然后双击make-war:
如果没有错误,就会在dist目录下生成mpdevice.war文件,mpdevice的名字可以在build.xml中修改:
<property name="war.name" value="mpdevice.war" />
代码编译好后,我们接着就要申请个第三方服务器帐号。
3.2 申请第三方服务器帐号
一般公司申请的帐号,钱多选择多,这里只讲个人开发,想要免费服务好方便调试的,选择就很少,适合的有百度BAE和新浪SAE,这两个要免费使用都比较麻烦,但花个10块钱,就简单多了。
我申请的是SAE,讲一下过程。
打开http://www.sinacloud.com/sae.html 进入控制台,登陆,点击“创建应用”,进入后,填写二级域名,命字自己取,只要通过就行,然后是应用名,一般会自动生成与二级域名一样,然后,选择Java1.7,这时会提示要有云豆才可以创建,那就用淘宝充个10块钱,也别舍不得,10块有1000个云豆,可以用好久呢,用到调试结束都有剩的。然后在JVM等级里选择最便宜的A型,如下。点创建就可以了。
创建好后,可以看到应用信息下的应用名称和网址,这个网址就是微信要访问的:
点击应用名称,进入管理界面,到了管理界面,点击“代码管理”,在这里,点击“上传war包”,就可以将3.1编译出来的war包上传,注意,war包的名称必须应用名称一样,比如,你创建应用填的名称是test111,那么war包的文件名就得改为test111.war。
正常情况下,服务器在没有流量时会自动关闭,在有请求时会自动打开。另外,也可以手动操作,点击,“JVM管理”,可以在这里开关服务器。
上传好war包后,我们就可以配置微信第三方服务器了。
3.3 配置微信第三方服务器
通过网址http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login登入微信公众平台接口测试帐号,修改“接口配置信息”,在URL填入 http://xxxx.applinzi.com/callback,因为第三方服务器把servlet name配置为callback,所以需加callback后缀,以便微信通过get发送的接入设备凭证能够被后台程序接收到。Token的值就config.properties里token的值相同。配置好后点“提交”就可以了。
用微信扫描一下设备二维码进入公众号,如果有提示“欢迎关注蓝牙灯泡demo测试公众号!”说明服务器的配置已经成功了。
3.4 调试
服务器程序的功能主要处理微信绑定事件、菜单按键事件、微信文本,以及与BLE设备数据互发。打开BlueDemoServer代码里的BlueDemoServer\bluelight\src\com\bluelight\demo\service\CallbackService.java文件,可以看到,主要的事件处理都在这里。
SUBSCRIBE 用户关注公众号后发的订阅事件
CLICK 用户点击菜单时发的点击事件
TEXT 用户发来的文本
BIND 绑定设备时发来的事件
DEVICE_TEXT 设备发来的文本
首先调试一下SUBSCRIBE和BIND功能,用微信扫描设备二维码,然后点击“绑定设备”,微信会向服务器发送BIND事件,查看服务程序的log,看看是否正常。
服务程序的log的查看方法:
在SAE控制台里,点击“运维”下的“日志中心”,日志类型选择“notice”,再点查询,就要以看到代码里用System.out.println打印出来的信息了。
在CallbackService.java中可以看到有两个点击事件V1001_LIGHT_ON和V1002_LIGHT_OFF,所以我们需要在测试公众号创建对应的菜单按钮。
打开http://mp.weixin.qq.com/debug,选择自定义菜单:
填上已获得的access_token值,然后填body,内容如下:
{
"button": [
{
"type": "click",
"name": "open",
"key": "V1001_LIGHT_ON",
"sub_button": [ ]
},
{
"type": "click",
"name": "close",
"key": "V1002_LIGHT_OFF",
"sub_button": [ ]
}
]
点击“检查问题”按钮后,在进入手机微信公众号,就会发现多了两个按键:
点击按钮,微信会向服务器发送对应的CLICK事件,查看服务程序的log,看通信是否正常。
另外,点击按钮出,可能会出现没反应,而正常情况下,微信应该显示“已发送点灯消息:”,这时去看查服务器LOG,会发现有这样的错误提示:
SAE java.lang.RuntimeException: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
这表示,服务器程序在调用API时,如https://api.weixin.qq.com/device/transmsg?access_token=ACCESS_TOKEN,因认证问题而失败了。
解决的方法是打上这个PATCH:
--- a/src/com/bluelight/demo/api/util/HttpUtil.java
+++ b/src/com/bluelight/demo/api/util/HttpUtil.java
@@ -15,6 +15,14 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
/**
* 服务器端http请求工具类
@@ -44,6 +52,33 @@ public class HttpUtil {
return rs;
}
+ public static HttpClient wrapClient(HttpClient base) {
+ try {
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ X509TrustManager tm = new X509TrustManager() {
+ public void checkClientTrusted(X509Certificate[] xcs,
+ String string) {
+ }
+ public void checkServerTrusted(X509Certificate[] xcs,
+ String string) {
+ }
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+ ctx.init(null, new TrustManager[] { tm }, null);
+ SSLSocketFactory ssf = new SSLSocketFactory(ctx);
+ ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ ClientConnectionManager ccm = base.getConnectionManager();
+ SchemeRegistry sr = ccm.getSchemeRegistry();
+ sr.register(new Scheme("https", ssf, 443));
+ return new DefaultHttpClient(ccm, base.getParams());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
/**
* access_token 接口直接调用,其它调用doGet
*/
@@ -51,6 +86,7 @@ public class HttpUtil {
try {
HttpGet httpGet = new HttpGet(url);
HttpClient httpclient = new DefaultHttpClient();
+ httpclient = wrapClient(httpclient);
HttpResponse response = httpclient.execute(httpGet);
String resultContent = new Utf8ResponseHandler()
.handleResponse(response);
@@ -90,6 +126,7 @@ public class HttpUtil {
StringEntity entity = new StringEntity(body, "UTF-8");
httpPost.setEntity(entity);
HttpClient httpclient = new DefaultHttpClient();
+ httpclient = wrapClient(httpclient);
HttpResponse response = httpclient.execute(httpPost);
String resultContent = new Utf8ResponseHandler()
.handleResponse(response);