暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

物联网TCP设备交互解决方案-golang

Python效率工程 2021-12-14
1407

    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 // 消息序列自增id
msgChan 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#cmd
handleJob(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端口和imei
v := 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
}
// 直接给客户端发送cmd
conn.SetWriteDeadline(time.Now().Add(3 * time.Second))
conn.Write([]byte(cmd))
logger.Sugar.Infof("指令已下发成功, %v", cmd)
}

接下来只需要写后台接口,将任务下发至redis就可以了。

此方案可以在众多设备链接TCP Server时,能够一对一的下发指令,实现设备的单点控制。

文章转载自Python效率工程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论