前一段时间研究了一下阿里云IOT官网上的连接demo,有java和c版本的,但是暂时没有python版本,于是参考了java版本的设计思路,编写了一个python版本的客户端,代码如下
import socket
import uuid
import time
from util import SignUtil
from hashlib import sha1
import paho.mqtt.publish as publish
import paho.mqtt.client as mqtt
import os,logging
#定义一个列表
data = [1,3,2,5,7,6]
#定义一个元组作为字典索引
seq = ('productKey', 'deviceName', 'clientId', 'timestamp')
'''开启TLS时的认证文件目录'''
trust = 'F:/workspace/aliiot_python/root.cer' #修改为自己文件实际路径即可
'''日志格式配置'''
logging.basicConfig(level=logging.DEBUG,
format='[%(filename)s]-(line:%(lineno)d)-[%(funcName)s]-%(message)s----%(asctime)s',
datefmt='%y-%b %d %a %H:%M:%S')
class SimpleClient4IOT :
def __init__(self, clientid=None):
#这里是客户端需要的参数,改成自己生成的即可
self.deviceName = "test"
self.productKey = "3y7iXH9wQDt"
self.secret = "IoW7r1E8WcEN2ROXEvPrH7e5V9B4tFJp"
self.clientId = self.get_localhost_ip()
#用于测试的topic
self.subTopic = "/" + self.productKey + "/" + self.deviceName + "/get"
self.pubTopic = "/" + self.productKey + "/" + self.deviceName + "/update"
#MQTT服务器地址,TLS连接使用ssl开头,这里选择直连不加ssl
self.targetServer = "ssl://" + self.productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com:1883"
self.targetServer1 = self.productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com"
self.targetServer2 = "3y7iXH9wQDt.iot-as-mqtt.cn-shanghai.aliyuncs.com"
dit = dict.fromkeys(seq)
dit['productKey'] = self.productKey #这个是对应用户在控制台注册的 设备productkey
dit['deviceName'] = self.deviceName #这个是对应用户在控制台注册的 设备name
dit['clientId'] = self.clientId
t = int(round(time.time()*1000)) #获取系统当前时间
dit['timestamp'] = str(t)
#客户端ID格式,两个||之间的内容为设备端自定义的标记,字符范围[0-9][a-z][A-Z]
self.params = dit
self.mqttclientId = self.clientId + "|securemode=2,signmethod=hmacsha1,timestamp=" + str(t) + "|"
self.mqttUsername = self.deviceName + "&" + self.productKey #mqtt用户名格式
self.mqttPassword = SignUtil.SignUtil().sign(sha1, self.secret, **self.params)#签名,固定使用hamc加密
self.connec()
time.sleep(1)#这里要加延时,否则订阅不成功,原因未知
self.client.subscribe(subTopic)
time.sleep(1)#这里要加延时,否则发送不成功,原因未知
self.client.publish(pubTopic,message)
self.client.loop_forever()
# while True:
# self.client.loop()
def connec(self):
logging.info("进行连接-服务器地址: " + self.targetServer1);
self.client = mqtt.Client(client_id=self.mqttclientId, clean_session=True, userdata=None, protocol=mqtt.MQTTv31)
self.client.on_publish = self.on_publish
self.client.on_connect = self.on_connect
self.client.on_disconnect = self.on_disconnect
self.client.on_message = self.on_message
self.client.on_subscribe = self.on_subscribe
self.client.tls_insecure_set(True) #检查hostname的cert认证
self.client.tls_set(trust) #设置认证文件
self.setAutho() #设置用户名和密码
self.client.connect( self.targetServer1 ,port=1883, keepalive=65) #向服务器发起连接
def setAutho(self):
self.client.username_pw_set(self.mqttUsername, self.mqttPassword)
#获得主机IP地址
def get_localhost_ip(self):
self.local_ip = socket.gethostbyname(socket.gethostname())
return self.local_ip
#获得主机MAC地址
def get_localhost_mac(self):
mac=uuid.UUID(int = uuid.getnode()).hex[-12:]
return ":".join([mac[e:e+2] for e in range(0,11,2)])
#创建客户端标识
def SetClientId(self):
return self.get_localhost_ip()
'''回调函数'''
def on_connect(self, mqttc, obj, flags, rc):
# print("Connection returned " + str(rc))
if str(rc)=='0':
logging.info("连接已被服务端接受")
elif str(rc)=='1':
logging.info("连接已拒绝,不支持的协议版本")
elif str(rc)=='2':
logging.info("连接已拒绝, 不合格的客户端标识符")
elif str(rc)=='3':
logging.info("连接已拒绝, 服务端不可用")
elif str(rc)=='4':
logging.info("连接已拒绝,无效的用户名或密码")
def on_message(self, mqttc, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
# self.client.disconnect()
def on_publish(self, mqttc, obj, mid):
print("mid: "+str(mid))
def on_subscribe(self, mqttc, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def on_log(self, mqttc, obj, level, string):
print(string)
def on_disconnect(self, client, userdata, rc):
if rc != 0:
print("连接断开,准备重新发起连接.")
self.client.reconnect()
s = SimpleClient4IOT()
def sign(self, Method, key, **params):
""" 数字签名程序"""
#字典排序生成规范化请求字符串
sortedKey = sorted(params.iteritems(), key=lambda d:d[0] )
temp = sortedKey[0]+sortedKey[1]+sortedKey[2]+sortedKey[3]
list_str = ''.join(temp)
return self.encryptHMAC(list_str, key, Method)
"""HMAC加密"""
def encryptHMAC(self, content, key, Method):
a = hmac.new(key, content, Method)
return a.digest().encode('hex').rstrip('')
跟java版本相比,python在选择服务器地址时用tcp直连加证书认证
server地址不加ssl,带上ssl地址解析失败,这点跟java客户端不一样。
运行结果:其中连接过程中有个重连动作,订阅了subtopic,云端发送一个消息,可以正常显示
完整项目地址:http://git.oschina.net/DSSDD/aliiot_python/tree/master