MQTT协议是目前物联网底层协议的主流解决方案,例如移动物联网平台,联通物联网平台以及阿里云物联网平台等等,更有设备厂家将MQTT协议嵌入设备中,直接绕过这些物联网平台与业务平台合作,但是大多数的业务平台也是通过MQTT消息转发,有着属于自己的一套数据解析与分发的流程,所以大多数业务平台还是会选择TCP和HTTP协议来对接厂家设备,那么我就单独把TCP对接设备以及对设备控制的解决方案拎出来讲讲。
目前物联网设备都是自带网络功能,能主动上报设备心跳数据,意思就是设备有TCP Client功能,但是没有TCP Server功能,所以只能在设备链接上我们的Server端才能给设备发送信息来控制设备启停开关等功能。但是如何在设备链接上我们的Server端上报消息时,通过前台页面给设备发送消息呢?解决方案如下:
1.首先设备链接上我们的Server端时,我们需要在代码中存下设备ip和port以及设备的链接io,即 ip:port--->conn
2.将设备ip、端口和设备唯一标识存入数据库,并在设备上报心跳数据时更新
3.前台页面通过设备唯一标识请求后台接口
4.后台通过数据库将设备的ip和端口查出来,接下来是重点
5.将ip和端口及要下发的命令数据通过一定的结构放入redis集合或者消息队列
6.我们的TCP Server端监听redis结合或者消息队列,拿到数据之后根据
ip:port找到第一步的conn,通过conn发送消息
整个步骤一气呵成,屡试不爽!
接下来上核心代码:
1、tcp server端
// 客户端对象type Client struct {sequence int // 消息序列自增idmsgChan chan []byte // 处理消息队列writeChan chan []byte // 写消息队列Product *Product // 产品对象Conn net.Conn // 连接对象Buf []byte // 数据缓冲区DeviceId string // 设备id(只有当接收到消息时, 才能获得)}// 客户端映射表var ClientMap = make(map[string]net.Conn)// 新对象func NewClient(conn net.Conn, product *Product) *Client {c := &Client{msgChan: make(chan []byte, 16),writeChan: make(chan []byte, 16),Product: product,Conn: conn,Buf: make([]byte, 0),}ClientMap[conn.RemoteAddr().String()] = conn // 将客户端连接放入数组中return c}
2、redis任务监听(消息队列采用的是redis集合)
const (QueueName string = "NETWORK_CONTROL_JOB")func Consumer() {conn := redis.GetAccessCacheConn()defer conn.Close()for {valueList := getRedisJob(conn)if len(valueList) > 1 {taskValue := valueList[1] // 127.0.0.1:32854#cmdhandleJob(taskValue)logger.Sugar.Infof("消费的KEY:", valueList[0], "消费数据:", taskValue, "当前时间:", time.Now().Unix())time.Sleep(time.Second)}//logger.Sugar.Infof("获取的值:%v", valueList)}}// 获取redis中的任务func getRedisJob(conn *goRedis.Client) []string {// brpop支持监听多个list,因此它有两个返回值,第一个返回值是list的名称,即key的名称,第二个返回值是pop出来的元素value, err := conn.BRPop(5 *time.Second, QueueName).Result()if err == redis.Nil{// 查询不到数据time.Sleep(1 * time.Second)}if err != nil {// 查询出错time.Sleep(1 * time.Second)}return value}// 处理任务func handleJob(taskValue string) {// 获取ip端口和imeiv := strings.Split(taskValue, "#")addr, cmd := v[0], v[1]// 根据addr获取tcp客户端conn, ok := tcp.ClientMap[addr]if ok == false {logger.Sugar.Errorf("客户端已断开连接, 指令下发失败:%v", addr)return}// 直接给客户端发送cmdconn.SetWriteDeadline(time.Now().Add(3 * time.Second))conn.Write([]byte(cmd))logger.Sugar.Infof("指令已下发成功, %v", cmd)}
接下来只需要写后台接口,将任务下发至redis就可以了。
此方案可以在众多设备链接TCP Server时,能够一对一的下发指令,实现设备的单点控制。




