2023-03-11 08:04:43 +00:00
|
|
|
|
#!/usr/bin/env python3
|
2023-03-11 13:25:34 +00:00
|
|
|
|
# coding:utf-8
|
2023-03-10 01:34:44 +00:00
|
|
|
|
import socketserver
|
|
|
|
|
import subprocess
|
2023-03-11 03:51:56 +00:00
|
|
|
|
import pickle
|
2023-03-13 07:27:36 +00:00
|
|
|
|
from collections import defaultdict
|
2023-03-13 09:20:43 +00:00
|
|
|
|
import datetime
|
|
|
|
|
import time
|
2023-03-11 03:51:56 +00:00
|
|
|
|
|
2023-03-13 07:27:36 +00:00
|
|
|
|
cache = defaultdict(list)
|
2023-03-11 13:25:34 +00:00
|
|
|
|
offs = 0 # 偏移量
|
2023-03-13 07:27:36 +00:00
|
|
|
|
|
|
|
|
|
# 保存终端信息,ccid=地址
|
2023-03-11 13:25:34 +00:00
|
|
|
|
p = {}
|
2023-03-13 07:27:36 +00:00
|
|
|
|
# 地址 ==ccid
|
2023-03-11 13:25:34 +00:00
|
|
|
|
pp = {}
|
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
|
2023-03-13 09:20:43 +00:00
|
|
|
|
def add_timestamp():
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print(datetime.datetime.now(), ":")
|
2023-03-11 03:51:56 +00:00
|
|
|
|
|
2023-03-10 01:34:44 +00:00
|
|
|
|
|
2023-03-11 03:51:56 +00:00
|
|
|
|
def save_cache(dic):
|
2023-03-11 13:25:34 +00:00
|
|
|
|
"""缓存未发送成功的消息
|
2023-03-13 07:14:36 +00:00
|
|
|
|
仅在目标终端未上线或发送失败时缓存
|
|
|
|
|
发送失败的一般原因是终端掉线但未及时更新在线列表"""
|
2023-03-11 03:51:56 +00:00
|
|
|
|
|
|
|
|
|
# f=open("./cache.txt",mode='w+')
|
|
|
|
|
# f.write(str(dic))
|
|
|
|
|
# f.close
|
2023-03-11 13:25:34 +00:00
|
|
|
|
pickle.dump(dic, open("./cache.txt", "wb"))
|
|
|
|
|
|
2023-03-11 03:51:56 +00:00
|
|
|
|
|
|
|
|
|
def load_cache():
|
2023-03-11 13:25:34 +00:00
|
|
|
|
"""程序启动时加载缓存"""
|
2023-03-11 03:51:56 +00:00
|
|
|
|
try:
|
|
|
|
|
# f=open("./cache.txt")
|
|
|
|
|
# c = eval(f.read())
|
|
|
|
|
# f.close()
|
2023-03-11 13:25:34 +00:00
|
|
|
|
c = pickle.load(open("./cache.txt", "rb"))
|
2023-03-11 03:51:56 +00:00
|
|
|
|
return c
|
|
|
|
|
except:
|
|
|
|
|
return None
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
|
|
|
|
def update_pairs(addr, ccid, clear=0):
|
|
|
|
|
"""更新终端库
|
2023-03-10 09:58:19 +00:00
|
|
|
|
addr:绑定tcp地址
|
|
|
|
|
ccid:终端号
|
2023-03-11 13:25:34 +00:00
|
|
|
|
clear:清空掉线终端"""
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
|
|
|
|
if ccid and not clear:
|
2023-03-11 13:25:34 +00:00
|
|
|
|
# 添加或更新客户端
|
2023-03-10 09:58:19 +00:00
|
|
|
|
p[ccid] = addr
|
|
|
|
|
pp[addr] = ccid
|
|
|
|
|
else:
|
|
|
|
|
del p[ccid]
|
|
|
|
|
del pp[addr]
|
|
|
|
|
|
2023-03-13 09:20:43 +00:00
|
|
|
|
print("ccid已更新注册。")
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# 打印在线终端
|
|
|
|
|
for i in p:
|
2023-03-11 13:25:34 +00:00
|
|
|
|
print(i, "<--->", p[i])
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# for i in pp:
|
|
|
|
|
# print(i,"-",pp[i])
|
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
2023-03-10 09:58:19 +00:00
|
|
|
|
def get_addr(ccid):
|
2023-03-11 13:25:34 +00:00
|
|
|
|
"""获取ccid对应的地址"""
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-10 01:34:44 +00:00
|
|
|
|
try:
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# 如果有直接返回
|
|
|
|
|
return p[ccid]
|
2023-03-10 01:34:44 +00:00
|
|
|
|
except Exception:
|
2023-03-11 13:25:34 +00:00
|
|
|
|
# 没有就返回None
|
2023-03-10 09:58:19 +00:00
|
|
|
|
return None
|
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
2023-03-10 09:58:19 +00:00
|
|
|
|
def get_ccid(addr):
|
|
|
|
|
try:
|
|
|
|
|
# 如果有直接返回
|
|
|
|
|
return pp[addr]
|
|
|
|
|
except Exception:
|
2023-03-11 13:25:34 +00:00
|
|
|
|
# 没有就返回None
|
2023-03-13 07:14:36 +00:00
|
|
|
|
print("终端未上报心跳注册ccid。")
|
2023-03-10 09:58:19 +00:00
|
|
|
|
return None
|
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
|
|
|
|
"""
|
2023-03-10 09:58:19 +00:00
|
|
|
|
+--------+--------------+----------+----------+--------------+------------------------+--------+
|
|
|
|
|
| ff | 长度 | 地址信息 | 信令 | 内容 | 校验位 |
|
|
|
|
|
+--------+--------------+----------+----------+--------------+------------------------+--------+
|
2023-03-13 09:20:43 +00:00
|
|
|
|
| | 2Bytes | 4Bytes | 心跳 | 0x01 | 时间戳32bit(单位ms) | 1Bytes |
|
2023-03-10 09:58:19 +00:00
|
|
|
|
+ $TTMS +--------------+----------+----------+--------------+------------------------+--------+
|
2023-03-13 09:20:43 +00:00
|
|
|
|
| | 2Bytes | 4Bytes | 传输数据 | 0xaa | 载荷 | 1Bytes |
|
2023-03-10 09:58:19 +00:00
|
|
|
|
+--------+--------------+----------+----------+--------------+------------------------+--------+
|
2023-03-11 13:25:34 +00:00
|
|
|
|
"""
|
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
def tt_hh(addr, data):
|
|
|
|
|
"""处理心跳"""
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
|
|
|
|
# 更新pairs
|
2023-03-11 13:25:34 +00:00
|
|
|
|
ccid = data[7 + offs : 11 + offs]
|
|
|
|
|
update_pairs(addr, ccid)
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# 组帧再返回
|
2023-03-14 13:21:21 +00:00
|
|
|
|
msg = bytearray(data[:])
|
2023-03-14 14:04:51 +00:00
|
|
|
|
val = "%04x" % int(time.time())
|
|
|
|
|
tmp=[]
|
|
|
|
|
for i in range(0,len(val),2):
|
|
|
|
|
tmp.append(int(val[i:i+2],16))
|
|
|
|
|
msg[12:-1] = tmp
|
2023-03-13 09:20:43 +00:00
|
|
|
|
# 计算校验和
|
|
|
|
|
crc = 0
|
|
|
|
|
for i in msg[:-1]:
|
|
|
|
|
crc = crc ^ i
|
|
|
|
|
msg[-1] = crc
|
|
|
|
|
add_timestamp()
|
|
|
|
|
addr.send(msg)
|
|
|
|
|
print("回复心跳。")
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-13 09:20:43 +00:00
|
|
|
|
# 从缓存中匹配到数据才重发
|
2023-03-11 08:04:43 +00:00
|
|
|
|
# 仅有新终端上线时才重发
|
2023-03-11 03:51:56 +00:00
|
|
|
|
if ccid in cache:
|
2023-03-13 11:27:54 +00:00
|
|
|
|
msgs = cache[ccid][:] # 浅拷贝
|
|
|
|
|
sccid = msgs[0][7 + offs : 11 + offs]
|
|
|
|
|
print("有", len(msgs), "包缓存数据待发。", sccid, "-->", ccid)
|
2023-03-11 08:04:43 +00:00
|
|
|
|
for i in range(len(msgs)):
|
|
|
|
|
try:
|
2023-03-13 09:20:43 +00:00
|
|
|
|
add_timestamp()
|
2023-03-11 09:06:55 +00:00
|
|
|
|
# 按缓存先后顺序发送
|
2023-03-11 08:04:43 +00:00
|
|
|
|
addr.send(msgs[i])
|
2023-03-13 07:27:36 +00:00
|
|
|
|
# 成功则清空已发送成功的缓存数据
|
2023-03-13 02:11:29 +00:00
|
|
|
|
cache[ccid].pop(0)
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("第", i + 1, "包缓存数据发送完成。")
|
2023-03-11 08:04:43 +00:00
|
|
|
|
except:
|
2023-03-11 09:06:55 +00:00
|
|
|
|
# 异常时退出循环,不用再尝试发送后续msgs
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("第", i + 1, "包发送失败,停止尝试")
|
2023-03-11 08:04:43 +00:00
|
|
|
|
break
|
2023-03-13 02:11:29 +00:00
|
|
|
|
if cache[ccid] == []:
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("缓存数据全部发送完成。", sccid, "-->", ccid)
|
2023-03-13 02:11:29 +00:00
|
|
|
|
del cache[ccid]
|
|
|
|
|
save_cache(cache)
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
|
2023-03-13 09:20:43 +00:00
|
|
|
|
def check_valid(data):
|
|
|
|
|
# 检查数据有效性
|
|
|
|
|
|
|
|
|
|
return bytearray(data[:])
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
def tt_trans(addr, data):
|
|
|
|
|
"""发送数据到指定ccid"""
|
|
|
|
|
|
2023-03-13 07:14:36 +00:00
|
|
|
|
sccid = get_ccid(addr)
|
|
|
|
|
if not sccid:
|
|
|
|
|
return -1
|
2023-03-11 13:25:34 +00:00
|
|
|
|
tccid = data[7 + offs : 11 + offs]
|
2023-03-10 09:58:19 +00:00
|
|
|
|
taddr = get_addr(tccid)
|
|
|
|
|
# 组帧
|
2023-03-13 09:20:43 +00:00
|
|
|
|
msg = check_valid(data)
|
2023-03-11 13:25:34 +00:00
|
|
|
|
msg[:5] = b"$TTMX"
|
2023-03-13 07:14:36 +00:00
|
|
|
|
msg[7 + offs : 11 + offs] = sccid
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
|
|
|
|
# 计算校验和
|
|
|
|
|
crc = 0
|
2023-03-11 03:51:56 +00:00
|
|
|
|
for i in msg[:-1]:
|
2023-03-10 09:58:19 +00:00
|
|
|
|
crc = crc ^ i
|
2023-03-11 13:25:34 +00:00
|
|
|
|
msg[-1] = crc
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-11 03:51:56 +00:00
|
|
|
|
if not taddr:
|
2023-03-11 09:06:55 +00:00
|
|
|
|
# 未找到匹配的在线终端
|
2023-03-11 08:04:43 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
# 更新缓存
|
|
|
|
|
# {tccid1=[msg1,msg2,...],...}
|
2023-03-11 03:51:56 +00:00
|
|
|
|
cache[tccid].append(msg)
|
|
|
|
|
save_cache(cache)
|
2023-03-11 09:06:55 +00:00
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("终端未在线或ccid", tccid, "错误。数据已缓存,待目标终端上线后重发。")
|
2023-03-11 03:51:56 +00:00
|
|
|
|
return 0
|
2023-03-13 07:14:36 +00:00
|
|
|
|
print("匹配在线终端为:", tccid, "<--->", taddr)
|
2023-03-11 03:51:56 +00:00
|
|
|
|
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# 发送
|
|
|
|
|
try:
|
2023-03-13 09:20:43 +00:00
|
|
|
|
add_timestamp()
|
2023-03-10 09:58:19 +00:00
|
|
|
|
taddr.send(msg)
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("数据发送成功。", sccid, "-->", tccid)
|
2023-03-10 09:58:19 +00:00
|
|
|
|
except Exception:
|
2023-03-11 08:04:43 +00:00
|
|
|
|
# 发送失败
|
2023-03-11 09:06:55 +00:00
|
|
|
|
taddr.close()
|
2023-03-11 03:51:56 +00:00
|
|
|
|
# 更新pairs,清空对应终端
|
2023-03-11 13:25:34 +00:00
|
|
|
|
update_pairs(taddr, tccid, 1)
|
|
|
|
|
# 更新缓存
|
|
|
|
|
# {tccid1=[msg1,msg2,...],...}
|
2023-03-11 03:51:56 +00:00
|
|
|
|
cache[tccid].append(msg)
|
|
|
|
|
save_cache(cache)
|
2023-03-11 09:06:55 +00:00
|
|
|
|
|
2023-03-13 07:14:36 +00:00
|
|
|
|
print("发送失败,终端可能已掉线。数据已缓存,待目标终端上线后重发。", taddr)
|
2023-03-10 09:58:19 +00:00
|
|
|
|
return 0
|
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
2023-03-13 07:14:36 +00:00
|
|
|
|
def data_split(data):
|
|
|
|
|
# 按帧头分割长数据
|
|
|
|
|
|
2023-03-13 11:27:54 +00:00
|
|
|
|
h = b"$TTMS" # b'\x24\x54\x54\x4d\x53'
|
|
|
|
|
dlist = data.split(h)[1:]
|
2023-03-13 07:14:36 +00:00
|
|
|
|
for i in range(len(dlist)):
|
|
|
|
|
dlist[i] = h + dlist[i]
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("解析为", len(dlist), "包数据。")
|
2023-03-13 07:14:36 +00:00
|
|
|
|
return dlist
|
|
|
|
|
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
def tt_decode(addr, data):
|
|
|
|
|
"""处理收到的数据
|
2023-03-13 07:14:36 +00:00
|
|
|
|
|
2023-03-11 09:06:55 +00:00
|
|
|
|
数据正常返回0
|
2023-03-11 13:25:34 +00:00
|
|
|
|
数据异常返回-1"""
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-13 07:14:36 +00:00
|
|
|
|
flag = -1
|
|
|
|
|
|
|
|
|
|
# 依据帧头和最小长度初判数据有效性
|
2023-03-13 09:20:43 +00:00
|
|
|
|
# 只要接收数据给的缓存足够大,即使是多条数据组成的长数据也默认帧格式是完整的,前几个字节必须是帧头
|
2023-03-13 11:27:54 +00:00
|
|
|
|
if data[:5] != b"$TTMS" or len(data) < 12:
|
2023-03-10 09:58:19 +00:00
|
|
|
|
return -1
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
2023-03-13 09:20:43 +00:00
|
|
|
|
dlist = data_split(data)
|
2023-03-13 07:14:36 +00:00
|
|
|
|
for data in dlist:
|
|
|
|
|
cmd = data[11 + offs]
|
|
|
|
|
# 回退到if-elif而不用match,因服务器默认版本3.8
|
|
|
|
|
if cmd == 0x01:
|
|
|
|
|
# 处理心跳
|
|
|
|
|
flag = tt_hh(addr, data)
|
|
|
|
|
elif cmd == 0xAA:
|
|
|
|
|
# 处理数据
|
|
|
|
|
if tt_trans(addr, data):
|
|
|
|
|
flag = -1
|
|
|
|
|
break
|
|
|
|
|
else:
|
2023-03-13 11:27:54 +00:00
|
|
|
|
flag = 0
|
2023-03-13 07:14:36 +00:00
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
return flag
|
|
|
|
|
|
2023-03-10 09:58:19 +00:00
|
|
|
|
|
2023-03-10 01:34:44 +00:00
|
|
|
|
class MyServer(socketserver.BaseRequestHandler):
|
2023-03-11 13:25:34 +00:00
|
|
|
|
def handle(self): # 回调
|
2023-03-13 09:20:43 +00:00
|
|
|
|
add_timestamp()
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("终端", self.client_address, "已上线,等待上报心跳注册ccid。")
|
2023-03-10 01:34:44 +00:00
|
|
|
|
conn = self.request
|
2023-03-10 09:58:19 +00:00
|
|
|
|
# print(type(conn),conn.fd)
|
2023-03-11 13:25:34 +00:00
|
|
|
|
|
2023-03-10 01:34:44 +00:00
|
|
|
|
while True:
|
2023-03-13 07:27:36 +00:00
|
|
|
|
data = conn.recv(1024000)
|
2023-03-10 01:34:44 +00:00
|
|
|
|
if not data:
|
|
|
|
|
break
|
2023-03-13 09:20:43 +00:00
|
|
|
|
add_timestamp()
|
2023-03-11 13:25:34 +00:00
|
|
|
|
print(
|
|
|
|
|
"接收到新数据", self.client_address, ",长度", len(data), ":\r\n", data.hex(" ")
|
|
|
|
|
)
|
|
|
|
|
if tt_decode(conn, data):
|
2023-03-10 09:58:19 +00:00
|
|
|
|
conn.close()
|
2023-03-11 13:25:34 +00:00
|
|
|
|
print(self.client_address, "疑是非法连接,已切断。")
|
2023-03-10 09:58:19 +00:00
|
|
|
|
break
|
|
|
|
|
|
2023-03-10 01:34:44 +00:00
|
|
|
|
|
2023-03-11 13:25:34 +00:00
|
|
|
|
if __name__ == "__main__":
|
2023-03-11 03:51:56 +00:00
|
|
|
|
# load_cache
|
|
|
|
|
cache = load_cache()
|
|
|
|
|
|
2023-03-12 05:32:32 +00:00
|
|
|
|
server = socketserver.ThreadingTCPServer(("", 7222), MyServer)
|
2023-03-13 09:20:43 +00:00
|
|
|
|
add_timestamp()
|
2023-03-13 11:27:54 +00:00
|
|
|
|
print("服务端初始化成功。", server.server_address)
|
2023-03-10 01:34:44 +00:00
|
|
|
|
server.serve_forever()
|
2023-03-13 07:14:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
以下几种情况服务器会主动断开终端:
|
|
|
|
|
单次数据不以帧头开始
|
|
|
|
|
单次数据长度不足11
|
|
|
|
|
未上报心跳(注册ccid)而直接发数据
|
2023-03-13 11:27:54 +00:00
|
|
|
|
"""
|