C 语言实现 Modbus 协议并获取云端服务器参数
C 语言实现 Modbus 协议并获取云端服务器参数
实验目标
在消化学习 server.c 和 client.c 套接字代码、python-modbus-over-tcp.py 代码基础上,试着用 C 编程完成 modbus 协议,从云端服务器读取温湿度数据
实验原理
详细原理看我的另外两篇博客:
示例代码
首先看给出的示例代码,这个代码是用 Python 编写的,代码如下:
点击查看TCP完整代码
import socket
import sys
import struct
import time
import tcp
import threading
import _thread
# import pymysql
#本程序是应变传感器采集,可以通过发送ALL指令进行全部传感器的采集
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
#连接数据
def MySQLConnect():
connection = pymysql.connect(
host='localhost', # IP,MySQL数据库服务器IP地址
port=3306, # 端口,默认3306,可以不输入
user='root', # 数据库用户名
password='123456', # 数据库登录密码
database='sensor', # 要连接的数据库
charset='utf8' # 字符集,注意不是'utf-8'
)
return connection
#插入数据到数据库
def AddData(num,yb,wd,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO strain_sensor(id ,mic, strain_temp, time) VALUES (%s,%s,%s,%s); "
cursor.execute(sql, [num,yb, wd, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
#获取一次数据
def getStain(cmd,num,time):
#print(cmd)
#print(num)
cmd = bytes.fromhex(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
cmd = cmd + crc
#print(cmd)
#发送对应的指令
tcp.send(cmd)
try:
data = tcp.recv(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if len(crc1) == 3:
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
yb, wd = struct.unpack('>ii', data[4:12])
yb = yb / 100.0
wd = wd / 100.0
print("应变:", yb, "温度:", wd)
# print(time)
yb = str(yb)
wd = str(wd)
# AddData(num, yb, wd, time)
print(num,"\n",yb,"\n",wd,"\n")
def setCircleData(cmd,num):
count=0
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
last1 = time.time()
while True:
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
now1 = time.time()
# print(now)
if (flag == 0):
count=count+1
flag = 1
getStain(cmd, num, last)
last = now
last1 = now1
if now1 - last1 > 5:
if count>=5:
str = input("请选择是否继续采集(y表示继续,n表示退出):")
if str == 'y':
count = 0
continue
else:
break
count = count + 1
getStain(cmd, num, now)
last = now
last1 = now1
#同时采集全部应变传感器
def setCircleAll(cmd):
flag=0
count=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
last1 = time.time()
while True:
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
now1 = time.time()
#count=0
# print(now)
if (flag == 0):
flag = 1
count=count+1
for cmd1 in cmd:
id='00'+cmd1[0:2]
#print(id)
#print(cmd1)
getStain(cmd1,id,last)
last = now
last1 = now1
if now1 - last1 > 5:
if count>=5:
str=input("请选择是否继续采集(y表示继续,n表示退出):")
if str=='y':
count=0
continue
else:
break
count = count + 1
for cmd1 in cmd:
id = '00' + cmd1[0:2]
getStain(cmd1, id, now)
last = now
last1 = now1
if __name__ == '__main__':
tcp = socket.socket(socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP)
tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp.settimeout(5)
try:
tcp.connect(('demo-monitor.igong.com', 8002))
print("TCP连接成功")
except:
print("连接TCP失败")
sys.exit(1)
flag=0
while True:
print("具体指令给格式为000+传感器编号(1,2,3,4,5)")
num = input("请输入采集传感器的编号(All表示采集全部传感器,0表示退出采集):")
if num == '0001':
cmd = '010300010002'
setCircleData(cmd,num)
elif num == '0002':
cmd = '020300010002'
setCircleData(cmd, num)
elif num == '0003':
cmd = '030300010002'
setCircleData(cmd, num)
elif num == '0004':
cmd = '040300010002'
setCircleData(cmd, num)
elif num == '0005':
cmd = '050300010002'
setCircleData(cmd, num)
elif num == 'All':
cmd={'010300010002','020300010002','030300010002','040300010002','050300010002'}
setCircleAll(cmd)
elif num == '0':
break
else:
print("输入信息不合法,请重新输入")
点击查看UDP完整代码
import socket
import sys
import struct
import time
# import pymysql
import datetime
#实现采集温度传感器和静力水准仪
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
#连接数据库
def MySQLConnect():
connection = pymysql.connect(
host='localhost', # IP,MySQL数据库服务器IP地址
port=3306, # 端口,默认3306,可以不输入
user='root', # 数据库用户名
password='123456', # 数据库登录密码
database='sensor', # 要连接的数据库
charset='utf8' # 字符集,注意不是'utf-8'
)
return connection
#插入温湿度采集到数据库
def AddData1(wd,sd,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO temp_hum_sensor(temp, hum, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [wd, sd, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
#插入静力水准仪采集到数据库
def AddData2(id,water_level,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO static_level(id, water_level, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [id, water_level, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
#采集温度传感器的数据
def getDataTemp(cmd):
#flag标志采集的次数
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(last)
last1 = time.time()
cmd = bytes.fromhex(cmd)
#print(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
#得到发送的指令(modbus协议定义内容+校验)
cmd = cmd + crc
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if (len(crc1) == 3):
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
# print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
# 解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.
print("温度:", wd, "湿度:", sd)
# AddData1(wd, sd, last)
flag=flag+1
while True:
now= time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
#print(s)
now1=time.time()
#每隔5s获取一次数据
if(now1-last1>5):
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc2=bytes.hex(crc)
#print(crc2)
crc1 = crc16(data[:-2])
crc1=crc1[2:]
if(len(crc1)==3):
crc1='0'+crc1
#print(crc1)
crc1=bytes.fromhex(crc1)
#print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
#解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.0
#当前时间
print(now)
#获取得到的数据
print("温度:", wd, "湿度:", sd)
last=now
last1=now1
wd=str(wd)
sd=str(sd)
# AddData1(wd,sd,now)
flag = flag + 1
if flag >= 5:
str1 = input("请选择是否继续采集(y表示继续,n表示退出):")
if str1 == 'y':
flag = 0
continue
else:
break
def getDataStaticLevel(cmd):
id=cmd[0:2]
#print(id)
if id=='02':
#print("2号")
id='00'+id
getData(id,cmd)
elif id=='03':
#print("3号")
id = '00' + id
getData(id,cmd)
elif id=='04':
#print("4号")
id = '00' + id
getData(id,cmd)
elif id=='05':
#print("5号")
id = '00' + id
getData(id,cmd)
def getData(id,cmd):
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(last)
last1 = time.time()
cmd = bytes.fromhex(cmd)
crc = crc16(cmd)
crc = crc[2:]
if (len(crc) == 3):
crc = '0' + crc
crc = bytes.fromhex(crc)
cmd = cmd + crc
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
print("发送数据成功")
try:
data, address = udp.recvfrom(8192)
#print(data)
except socket.timeout:
print("超时")
sys.exit(1)
#print(len(data))
crc = data[-2:]
#print(data[:-2])
crc1 = crc16(data[:-2])
#print(crc1)
crc1=crc1[2:]
if len(crc1) == 3:
crc1 = '0' + crc1
#print(crc1)
crc1 = bytes.fromhex(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
#print(data[4:8])
nd = struct.unpack('>i', data[4:8])
#print(nd)
nd1 = nd[0]*10.0
nd1=str(nd1)
#print(last)
print("挠度:"+nd1)
AddData2(id,nd1,last)
flag=flag+1
while True:
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# print(s)
now1 = time.time()
# 每隔5s获取一次数据
if (now1 - last1 > 5):
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc2 = bytes.hex(crc)
# print(crc2)
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if (len(crc1) == 3):
crc1 = '0' + crc1
# print(crc1)
crc1 = bytes.fromhex(crc1)
# print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
# 解析数据
nd = struct.unpack('>i', data[4:8])
nd = nd[0] * 10.0
nd1=str(nd)
print(now)
print("挠度:" + nd1)
nd=str(nd)
AddData2(id, nd1, now)
last=now
last1=now1
flag = flag + 1
if flag >= 5:
str1 = input("请选择是否继续采集(y表示继续,n表示退出):")
if str1 == 'y':
flag = 0
continue
else:
break
if __name__ == '__main__':
print("开始程序")
print("程序相关说明:")
print("本程序采用UDP协议,其中当输入指令为0就退出整个程序。")
print("命令格式类似于地址(01,02,03,04,05)+03+传感器地址(0001)+传感器个数(0001,0002)")
print("例如:010300010002(温度传感器),020300010001(静力水准仪1)")
print("如果出现不合法指令就输出提示信息,并重新输入指令。")
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
udp.settimeout(5)
while True:
cmd = input("请输入相关命令:")
#print(len(cmd))
num = cmd[8:]
#print(num)
if num == '0001' and cmd[0:2] == '01':
print("此处不实现只对温度采集!!!")
elif num == '0001':
print("一个传感器")
getDataStaticLevel(cmd)
elif num == '0002':
print("两个传感器")
getDataTemp(cmd)
elif cmd == '0':
break
else:
print("指令不合法!!!")
TCP 连接代码分析
这里我们需要用到 CRC 验证,生成 2 字节的校验位数据,代码如下:
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
我们需要知道如何获取相关是数据,完整代码流程如下:
#获取一次数据
def getStain(cmd,num,time):
#print(cmd)
#print(num)
cmd = bytes.fromhex(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
cmd = cmd + crc
#print(cmd)
#发送对应的指令
tcp.send(cmd)
try:
data = tcp.recv(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if len(crc1) == 3:
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
yb, wd = struct.unpack('>ii', data[4:12])
yb = yb / 100.0
wd = wd / 100.0
print("应变:", yb, "温度:", wd)
# print(time)
yb = str(yb)
wd = str(wd)
# AddData(num, yb, wd, time)
print(num,"\n",yb,"\n",wd,"\n")
参照上述的数据接收格式我们知道 data[1] 为地址位,data[2] 为指令,data[3] 是数据区长度,data 的后面两位为 crc16 校验位
我首先对接收的数据进行 crc16 校验,校验通过后,表明数据有效,在通过 struct 中封装的函数 unpack 进行数据位解析拿到我们的应变以及温度,数据包的解析格式为 ‘>ii’ 即大端方式 8 位解析
UDP 连接代码分析
采集温度数据函数如下:
#采集温度传感器的数据
def getDataTemp(cmd):
#flag标志采集的次数
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(last)
last1 = time.time()
cmd = bytes.fromhex(cmd)
#print(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
#得到发送的指令(modbus协议定义内容+校验)
cmd = cmd + crc
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if (len(crc1) == 3):
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
# print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
# 解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.
print("温度:", wd, "湿度:", sd)
# AddData1(wd, sd, last)
flag=flag+1
while True:
now= time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
#print(s)
now1=time.time()
#每隔5s获取一次数据
if(now1-last1>5):
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc2=bytes.hex(crc)
#print(crc2)
crc1 = crc16(data[:-2])
crc1=crc1[2:]
if(len(crc1)==3):
crc1='0'+crc1
#print(crc1)
crc1=bytes.fromhex(crc1)
#print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
#解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.0
#当前时间
print(now)
#获取得到的数据
print("温度:", wd, "湿度:", sd)
last=now
last1=now1
wd=str(wd)
sd=str(sd)
# AddData1(wd,sd,now)
flag = flag + 1
if flag >= 5:
str1 = input("请选择是否继续采集(y表示继续,n表示退出):")
if str1 == 'y':
flag = 0
continue
else:
break
这里我们只需要根据示例代码了解数据获取的流程即可,然后我们按照 Modbus 协议的指令格式,能够模拟出我们设计的合理数据即可输入到 cmd 获取目标传感器数据
可视化代码
点击查看可视化代码:main.py
import wx
import socket
import threading
import subprocess
import struct
import time
import wxpy
class CalcFrame(wxpy.MyFrame1):
def __init__(self):
wxpy.MyFrame1.__init__(self, None)
# self.Maximize() #直接运行窗口最大化
self.sensors = {1: {'cgq': [{'cgqmc': '环境温湿度', #udp
'cmd': b'\x01\x03\x00\x01\x00\x02\x95\xcb',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]"},
{'cgqmc': '武隆方向挠度基准点',
'cmd': b'\x02\x03\x00\x01\x00\x01\xd5\xf9',
'inst': "struct.unpack('>i',data[4:8])[0]/10.0",
'glz': None},
{'cgqmc': '武隆方向1#引拱挠度',
'cmd': b'\x03\x03\x00\x01\x00\x01\xd4\x28',
'inst': "struct.unpack('>i',data[4:8])[0]/10.0",
'glz': None},
{'cgqmc': '武隆方向挠度',
'cmd': b'\x04\x03\x00\x01\x00\x01\xd5\x9f',
'inst': "struct.unpack('>i',data[4:8])[0]/10.0",
'glz': None},
{'cgqmc': '跨中挠度',
'cmd': b'\x05\x03\x00\x01\x00\x01\xd4\x4e',
'inst': "struct.unpack('>i',data[4:8])[0]/10.0",
'glz': None},
]},
2: {'cgq': {1:{'cgqmc': '主拱跨中1#应变', #tcp
'cmd': b'\x01\x03\x00\x01\x00\x02\x95\xcb',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]/100.0"},
2:{'cgqmc': '主拱跨中2#应变',
'cmd': b'\x02\x03\x00\x01\x00\x02\x95\xf8',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]/100.0"},
3:{'cgqmc': '主拱跨中3#应变',
'cmd': b'\x03\x03\x00\x01\x00\x02\x94\x29',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]/100.0"},
4:{'cgqmc': '主拱跨中4#应变',
'cmd': b'\x04\x03\x00\x01\x00\x02\x95\x9e',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]/100.0"},
5:{'cgqmc': '主拱跨中5#应变',
'cmd': b'\x05\x03\x00\x01\x00\x02\x94\x4f',
'inst': "struct.unpack('>i',data[4:8])[0]/100.0",
'glz': "struct.unpack('>i',data[8:12])[0]/100.0"},
}},
}
self.start = False
self.t_udp = None
self.t_tcp = None
def u_client(self,address,port):
udp = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
udp.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1)
udp.settimeout(2)
print("udp")
try:
self.input_all.write(" udp连接!\n"+" 地址: "+str(address)+"\n"+" 端口号:"+str(port)+"\n")
self.u_search(udp,address,port) #实现查询函数
print("连接成功!")
except socket.timeout:
print("超时,连接失败!")
return False
except:
print("端口号或地址错误,连接失败!")
return False
try:
print("进行查询操作")
finally:
print('u_close')
udp.close()
def t_client(self,address,port):
tcp = socket.socket(socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP)
tcp.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1)
tcp.settimeout(30)
print("tcp")
try: # 已经连接到远程端口
print(address, type(address))
print(port, type(port))
cmd = 'netstat -ano | findstr %s' % port
print(cmd)
tcp.connect((address, port)) # 连接目标地址和端口的套接字
print("成功")
self.input_all.write(" tcp连接!\n"+" 地址: "+str(address)+"\n"+" 端口号:"+str(port)+"\n")
result1 = subprocess.getoutput(cmd)
print(result1)
# self.input_all.AppendText(result1+"\n ")
self.t_search(tcp) #实现查询函数
print("链接成功!")
except socket.timeout:
print("超时,连接失败!")
return
except BaseException:
print("端口号或地址错误,连接失败!")
return
finally:
print('t_close')
tcp.close()
def t_search(self,tcp):
choose_s = self.kind_choice.Selection
num = self.num_chose.Selection
try:
cgq=self.sensors[2]['cgq']
if choose_s==0:
print('温度传感器')
self.input_all.write(" tcp通道无温度传感器!\n")
self.start=False
elif choose_s==1:
print('静力水准仪')
self.input_all.write(" tcp通道无静力水准仪!\n")
self.start=False
elif choose_s==2:
print('应变计')
if self.start:
# print('cgq0:',cgq[0])
cgq=cgq[num+1]
else:
self.input_all.write(" 连接失败!\n")
if self.start:
print(cgq)
cgqmc = cgq['cgqmc']
cmd = cgq['cmd']
inst_gs = cgq['inst']
glz_gs = cgq['glz']
tcp.sendall(cmd)
try:
data = tcp.recv(8192)
except socket.timeout:
self.input_all.AppendText('采集数据超时')
wx.Yield()
crc = data[-2:]
if crc != crc16(data[:-2]):
self.input_all.AppendText(' 采集 %s 数据时,CRC16校验失败!\n' % cgqmc)
wx.Yield()
try:
inst = eval(inst_gs)
if glz_gs is not None:
glz = eval(glz_gs)
else:
glz = None
except BaseException:
self.input_all.AppendText(' 采集 %s 数据时,解析数据失败!\n' % cgqmc)
wx.Yield()
self.input_all.AppendText(' %s: inst=%s glz=%s\n' % (cgqmc, inst, glz))
wx.Yield()
t = time.time()
while self.start and time.time() - t <= 2:
time.sleep(0.1)
finally:
print('T__CLO')
self.input_all.write(" 查询结束!\n")
tcp.close()
def u_search(self,udp,address,port):
choose_s = self.kind_choice.Selection
num = self.num_chose.Selection
try:
cgq = self.sensors[1]['cgq']
if choose_s==0:
print('温度传感器')
if num==0 and self.start:
cgq=cgq[1]
else:
self.input_all.write(" udp通道无此编号温度传感器!\n")
self.start=False
elif choose_s==1:
print('静力水准仪')
if num!=0 and self.start:
cgq = cgq[num+1]
else:
self.input_all.write(" udp通道无此编号静力水准仪!\n")
self.start=False
elif choose_s==2:
print('应变计')
self.input_all.write(" tcp通道无应变计!\n")
self.start=False
if self.start:
print(cgq)
server=(address,port)
print(server)
cgqmc = cgq['cgqmc']
cmd = cgq['cmd']
inst_gs = cgq['inst']
glz_gs = cgq['glz']
udp.sendto(cmd, server)
try:
data, _ = udp.recvfrom(8192)
except socket.timeout:
self.input_all.AppendText(' 采集 %s 数据超时!\n' % cgqmc)
wx.Yield()
crc = data[-2:]
if crc != crc16(data[:-2]):
self.input_all.AppendText(' 采集 %s 数据时,CRC16校验失败!\n' % cgqmc)
wx.Yield()
try:
inst = eval(inst_gs)
if glz_gs is not None:
glz = eval(glz_gs)
else:
glz = None
except BaseException:
self.input_all.AppendText(' 采集 %s 数据时,解析数据失败!\n' % cgqmc)
wx.Yield()
self.input_all.AppendText(' %s: inst=%s glz=%s\n' %
(cgqmc, inst, glz))
wx.Yield()
t = time.time()
while self.start and time.time() - t < 2:
time.sleep(0.1)
finally:
self.input_all.write(" 查询结束!\n")
udp.close()
def do_client_sys(self, event):
choose = self.t_d_chose.Selection
self.start = True
address = self.address_input.GetValue() # 实现输入判断端口与地址
port = self.port_input.GetValue()
if (port=="" or address=="") or (port=="" and address==""):
self.input_all.write(" 地址与端口号均不能为空!\n")
else:
port = int(port)
# address = 'demo-monitor.igong.com'
print("点击连接按钮")
if choose == 0:
# port = 8002
self.t_tcp = threading.Thread(target=self.t_client,args=(address,port))
self.t_tcp.setDaemon(True)
self.t_tcp.start()
elif choose == 1:
# port=8001
self.t_udp = threading.Thread(target=self.u_client,args=(address,port))
self.t_udp.setDaemon(True)
self.t_udp.start()
event.Skip()
def clear_input( self, event ):
self.input_all.Clear()
event.Skip()
def exit_sys(self, event):
choose=self.t_d_chose.Selection
if choose==1:
while self.t_udp.is_alive():
time.sleep(0.1)
# self.client_button.Enable(True)
# self.exit_button.Enable(False)
print('退出成功!')
self.start = False
self.Close()
event.Skip()
def hex_bytes(x):
if not isinstance(x, str):
x = str(x, 'ascii')
return bytes.fromhex(x)
def crc16(x):
u'''
@summary: 计算CRC16值
@param x: bytes
@return: 返回2字节值,类似:b'\x7B\x2A'。
'''
if not isinstance(x, bytes):
raise ValueError('Parameter must be a bytes type')
b = 0xA001
a = 0xFFFF
for byte in x:
a = a ^ byte
for _ in range(8):
last = a % 2
a = a >> 1
if last == 1:
a = a ^ b
aa = '0' * (6 - len(hex(a))) + hex(a)[2:]
ll, hh = int(aa[:2], 16), int(aa[2:], 16)
rtn = '%x' % (hh * 256 + ll & 0xffff)
while len(rtn) < 4:
rtn = '0' + rtn
rtn = hex_bytes(rtn)
return rtn
class MyApp(wx.App):
def OnInit(self):
main = CalcFrame()
main.Show()
return True
def OnExit(self):
return True
if __name__ == '__main__':
# app = wx.App(False)
# frame = CalcFrame(None)
# frame.Show(True)
# app.MainLoop()
app = MyApp()
app.MainLoop()
点击查看可视化代码:wxpy.py
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################
import wx
import wx.xrc
###########################################################################
## Class MyFrame1
###########################################################################
class MyFrame1 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"采集数据", pos = wx.DefaultPosition, size = wx.Size( 790,562 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_APPWORKSPACE ) )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
self.address = wx.StaticText( self, wx.ID_ANY, u"地址:", wx.Point( -1,-1 ), wx.DefaultSize, 0 )
self.address.Wrap( -1 )
self.address.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
self.address.SetMinSize( wx.Size( 70,20 ) )
bSizer2.Add( self.address, 0, wx.ALL, 5 )
self.address_input = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.address_input.SetMinSize( wx.Size( 150,28 ) )
bSizer2.Add( self.address_input, 0, wx.ALL, 5 )
self.port = wx.StaticText( self, wx.ID_ANY, u"端口:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.port.Wrap( -1 )
self.port.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
self.port.SetMinSize( wx.Size( 70,20 ) )
bSizer2.Add( self.port, 0, wx.ALL, 5 )
self.port_input = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.port_input.SetMinSize( wx.Size( 150,28 ) )
bSizer2.Add( self.port_input, 0, wx.ALL, 5 )
self.tcp = wx.StaticText( self, wx.ID_ANY, u"tcp", wx.DefaultPosition, wx.DefaultSize, 0 )
self.tcp.Wrap( -1 )
self.tcp.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
self.tcp.SetMinSize( wx.Size( 70,20 ) )
bSizer2.Add( self.tcp, 0, wx.ALL, 5 )
t_d_choseChoices = [ u"TCP", u"UDP" ]
self.t_d_chose = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, t_d_choseChoices, 0 )
self.t_d_chose.SetSelection( 0 )
self.t_d_chose.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
self.t_d_chose.SetMinSize( wx.Size( 100,30 ) )
bSizer2.Add( self.t_d_chose, 0, wx.ALL, 5 )
self.client_button = wx.Button( self, wx.ID_ANY, u"连接", wx.DefaultPosition, wx.DefaultSize, 0 )
self.client_button.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
bSizer2.Add( self.client_button, 0, wx.ALL, 5 )
bSizer1.Add( bSizer2, 0, wx.ALIGN_CENTER_HORIZONTAL, 5 )
bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
kind_choiceChoices = [ u"温度传感器", u"静力水准仪", u"应变计" ]
self.kind_choice = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, kind_choiceChoices, 0 )
self.kind_choice.SetSelection( 0 )
self.kind_choice.SetFont( wx.Font( 15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
self.kind_choice.SetMinSize( wx.Size( 140,30 ) )
bSizer4.Add( self.kind_choice, 0, wx.ALL, 5 )
num_choseChoices = [ u"1", u"2", u"3", u"4", u"5" ]
self.num_chose = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, num_choseChoices, 0 )
self.num_chose.SetSelection( 0 )
bSizer4.Add( self.num_chose, 0, wx.ALL, 5 )
bSizer1.Add( bSizer4, 0, 0, 5 )
bSizer12 = wx.BoxSizer( wx.HORIZONTAL )
bSizer1.Add( bSizer12, 0, 0, 5 )
self.input_all = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
bSizer1.Add( self.input_all, 1, wx.ALL|wx.EXPAND, 5 )
bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
self.clear_button = wx.Button( self, wx.ID_ANY, u"清除", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer5.Add( self.clear_button, 0, wx.ALL, 5 )
self.exit_button = wx.Button( self, wx.ID_ANY, u"退出", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer5.Add( self.exit_button, 0, wx.ALL, 5 )
bSizer1.Add( bSizer5, 0, wx.ALIGN_RIGHT, 5 )
self.SetSizer( bSizer1 )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.client_button.Bind( wx.EVT_BUTTON, self.do_client_sys )
self.clear_button.Bind( wx.EVT_BUTTON, self.clear_input )
self.exit_button.Bind( wx.EVT_BUTTON, self.exit_sys )
def __del__( self ):
pass
# Virtual event handlers, override them in your derived class
def do_client_sys( self, event ):
event.Skip()
def clear_input( self, event ):
event.Skip()
def exit_sys( self, event ):
event.Skip()
###########################################################################
## Class MyPanel1
###########################################################################
class MyPanel1 ( wx.Panel ):
def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
def __del__( self ):
pass
代码设计
我们需要获取数据的 IP 为:demo-monitor.igong.com
由于通过 c 语言使用 Socket 进行网络连接并不能解析域名封装过的 IP,所以我们可以直接访问当前连接,然后通过 F12 查看网络协议参数,能够看到我们的所需要访问的真正 IP
首先我们需要设置一些默认的数据和头文件:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <winsock2.h>
#include <math.h>
#include "stdint.h"
#define length_8 8 //定义一个宏,为传入8位16进制数的个数
#define PORT 8002 // 设置默认端口,这里 tcp 需要使用的端口是 8002
#define SERVER_IP "123.56.90.74" //设置默认IP
#define BUFFER_SIZE 4196
这里我们同样需要通过 CRC 验证得到两位校验位,C 语言实现 CRC 校验代码如下:
uint16_t CRC_16(uint8_t *temp)
{
uint8_t i,j;
uint16_t CRC_1 = 0xFFFF; //声明CRC寄存区,也就是步骤1
for(i = 0;i < 6;i++) //这里的for循环说的是步骤6中的重复步骤 2 到步骤 5
{
CRC_1 ^= temp[i]; //这里就是步骤2,进行异或运算
for(j = 0;j < 8;j++) //用来将异或后的低八位全部移出的for循环
{
if(CRC_1 & 0x01) //判断低八位的最后一位是否为1,为1时执行下列语句,也就是步骤3说的移位判断与步骤5说的右移8次
{
/*一定要先移位,再异或*/
CRC_1 >>=1; //移位后再异或,就是步骤4
CRC_1 ^= 0xA001; //0xA001为0x8005的逆序
}
else //若不为1,则直接移位。
{
CRC_1 >>=1;
}
}
}
// CRC_1 = (((CRC_1 & 0xFF)<<8) + (CRC_1>>8));
// printf("%04x\r\n",CRC_1); //用于打印检测CRC校验码
return(CRC_1);
}
根据示例代码进行分析,我们还需要将十六进制从数组转为字符串,代码如下:
unsigned char *fromhex(char *str)
{
static unsigned char buf[512];
size_t len = strlen(str) / 2;
if (len > 512) len = 512;
for (size_t i = 0; i < len; i++) {
unsigned char c = 0;
if (str[i * 2] >= '0' && str[i*2] <= '9')
c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i*2] & ~0x20) <= 'F')
c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9')
c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F')
c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}
这里还需要如下两个函数:获取ch字符在sign数组中的序号,十六进制数转换为十进制数,代码如下:
/* 返回ch字符在sign数组中的序号 */
int getIndexOfSigns(char ch)
{
if(ch >= '0' && ch <= '9')
{
return ch - '0';
}
if(ch >= 'A' && ch <='F')
{
return ch - 'A' + 10;
}
if(ch >= 'a' && ch <= 'f')
{
return ch - 'a' + 10;
}
return -1;
}
/* 十六进制数转换为十进制数 */
int hexToDec(char *source)
{
int sum = 0;
char low,high;
for(int i=0,j=7;i<4;i++){
//TODO
high = (source[i] & 0xf0)>>4;
low = source[i] & 0x0f;
sum += high*pow(16,j--)+low*pow(16,j--);
}
return sum;
}
做完以上准备,我们就可以进行 TCP 或 UDP 连接了,下面我们通过代码进行介绍
TCP 连接
初始化及进行连接的代码如下:
// 初始化socket dll。
WORD winsock_version = MAKEWORD(2,2);
//WSADATA结构包含有关Windows Sockets实现的信息。
WSADATA wsa_data;
//Winsock进行初始化
//调用 WSAStartup 函数以启动使用 WS2 _32.dll
//WSAStartup的 MAKEWORD (2,2) 参数发出对系统上 Winsock 版本2.2 的请求,并将传递的版本设置为调用方可以使用的最新版本的 Windows 套接字支持
int iResult = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (iResult != 0) {
printf("WSAStartup 失败!\n");
WSACleanup();
return 1;
}
// socket 函数创建绑定到特定
//为服务器创建一个SOCKET来监听客户端连接
//socket函数创建绑定到特定传输服务提供者的套接字。
//参数1:地址族规范
//参数2:新套接字的类型规范
//参数3:使用的协议
SOCKET client_socket = INVALID_SOCKET;
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
printf("套接字错误\n");
WSACleanup();
return 2;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
//尝试连接到一个地址,直到一个成功
if (connect(client_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to connect server: %ld !\n", GetLastError());
closesocket(client_socket);//关闭一个已存在的套接字。
client_socket = INVALID_SOCKET;
return 3;
}
下面我们需要获取数据
char *data = new char[length_8]; // 要进行输入的指令数据
printf("具体指令给格式为0+传感器编号(1,2,3,4,5)0300010002");
printf("请输入采集传感器的指令) 输入exit退出:\r\n");
scanf("%s",data);
if (strcmp(data,kExitFlag)==0) { // 输入退出
printf("Exit!\n");
break;
}
uint16_t crc; // CRC 校验数据
unsigned char * cmd; // 要进行输入的控制台shuju
char crc1[8];
cmd = fromhex(data); // 获取数据
crc = CRC_16(cmd); // 进行 CRC 校验
uint8_t a = 0xFF;
for(int i=0;i<6;i++){
//TODO
crc1[i] = cmd[i];
}
crc1[6] = a & crc; // 设置最后两位为 CRC 校验位
crc1[7] = (crc >> 8) & a;
if (send(client_socket, crc1, 8, 0) < 0) {
printf("发送失败!\n");
break;
}
int ret = recv(client_socket, recv_data, BUFFER_SIZE, 0);
if (ret < 0) {
printf("接收失败!\n");
break;
}
recv_data[ret]=0; // 正确结束收到的字符串
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float mic = hexToDec(yb)/100.0; // 由于我们获取到的数据是十六进制,这里需要进行进制转换
float strain_temp = hexToDec(wd)/100.0;
printf("应变:%f\r\n",mic);
printf("温度:%f\r\n",strain_temp);
最后我们需要关闭连接即可:
closesocket(client_socket);
WSACleanup();
完整 TCP 连接代码如下:
点击查看完整 TCP 连接代码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <winsock2.h>
#include <math.h>
#include "stdint.h"
#define length_8 8 //定义一个宏,为传入8位16进制数的个数
#define PORT 8002
#define SERVER_IP "123.56.90.74"
#define BUFFER_SIZE 4196
const char* kExitFlag = "exit";
/* 返回ch字符在sign数组中的序号 */
int getIndexOfSigns(char ch)
{
if(ch >= '0' && ch <= '9')
{
return ch - '0';
}
if(ch >= 'A' && ch <='F')
{
return ch - 'A' + 10;
}
if(ch >= 'a' && ch <= 'f')
{
return ch - 'a' + 10;
}
return -1;
}
/* 十六进制数转换为十进制数 */
int hexToDec(char *source)
{
int sum = 0;
char low,high;
for(int i=0,j=7;i<4;i++){
//TODO
high = (source[i] & 0xf0)>>4;
low = source[i] & 0x0f;
sum += high*pow(16,j--)+low*pow(16,j--);
}
return sum;
}
unsigned char *fromhex(char *str)
{
static unsigned char buf[512];
size_t len = strlen(str) / 2;
if (len > 512) len = 512;
for (size_t i = 0; i < len; i++) {
unsigned char c = 0;
if (str[i * 2] >= '0' && str[i*2] <= '9')
c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i*2] & ~0x20) <= 'F')
c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9')
c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F')
c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}
uint16_t CRC_16(uint8_t *temp)
{
uint8_t i,j;
uint16_t CRC_1 = 0xFFFF; //声明CRC寄存区,也就是步骤1
for(i = 0;i < 6;i++) //这里的for循环说的是步骤6中的重复步骤 2 到步骤 5
{
CRC_1 ^= temp[i]; //这里就是步骤2,进行异或运算
for(j = 0;j < 8;j++) //用来将异或后的低八位全部移出的for循环
{
if(CRC_1 & 0x01) //判断低八位的最后一位是否为1,为1时执行下列语句,也就是步骤3说的移位判断与步骤5说的右移8次
{
/*一定要先移位,再异或*/
CRC_1 >>=1; //移位后再异或,就是步骤4
CRC_1 ^= 0xA001; //0xA001为0x8005的逆序
}
else //若不为1,则直接移位。
{
CRC_1 >>=1;
}
}
}
// CRC_1 = (((CRC_1 & 0xFF)<<8) + (CRC_1>>8));
// printf("%04x\r\n",CRC_1); //用于打印检测CRC校验码
return(CRC_1);
}
int main() {
printf("启动TCP连接!\n");
// 初始化socket dll。
WORD winsock_version = MAKEWORD(2,2);
//WSADATA结构包含有关Windows Sockets实现的信息。
WSADATA wsa_data;
//Winsock进行初始化
//调用 WSAStartup 函数以启动使用 WS2 _32.dll
//WSAStartup的 MAKEWORD (2,2) 参数发出对系统上 Winsock 版本2.2 的请求,并将传递的版本设置为调用方可以使用的最新版本的 Windows 套接字支持
int iResult = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (iResult != 0) {
printf("WSAStartup 失败!\n");
WSACleanup();
return 1;
}
// socket 函数创建绑定到特定
//为服务器创建一个SOCKET来监听客户端连接
//socket函数创建绑定到特定传输服务提供者的套接字。
//参数1:地址族规范
//参数2:新套接字的类型规范
//参数3:使用的协议
SOCKET client_socket = INVALID_SOCKET;
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
printf("套接字错误\n");
WSACleanup();
return 2;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
//尝试连接到一个地址,直到一个成功
if (connect(client_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to connect server: %ld !\n", GetLastError());
closesocket(client_socket);//关闭一个已存在的套接字。
client_socket = INVALID_SOCKET;
return 3;
}
char recv_data[BUFFER_SIZE+1];
while (true) {
char *data = new char[length_8]; // 要进行输入的指令数据
printf("具体指令给格式为0+传感器编号(1,2,3,4,5)+0300010002,请输入采集传感器的编号(0表示退出采集):\n");
scanf("%s",data);
if (strcmp(data,kExitFlag)==0) { // 输入退出
printf("Exit!\n");
break;
}
uint16_t crc; // CRC 校验数据
unsigned char * cmd; // 要进行输入的控制台shuju
char crc1[8];
cmd = fromhex(data); // 获取数据
crc = CRC_16(cmd); // 进行 CRC 校验
uint8_t a = 0xFF;
for(int i=0;i<6;i++){
//TODO
crc1[i] = cmd[i];
}
crc1[6] = a & crc; // 设置最后两位为 CRC 校验位
crc1[7] = (crc >> 8) & a;
if (send(client_socket, crc1, 8, 0) < 0) {
printf("发送失败!\n");
break;
}
int ret = recv(client_socket, recv_data, BUFFER_SIZE, 0);
if (ret < 0) {
printf("接收失败!\n");
break;
}
recv_data[ret]=0; // 正确结束收到的字符串
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float mic = hexToDec(yb)/100.0; // 由于我们获取到的数据是十六进制,这里需要进行进制转换
float strain_temp = hexToDec(wd)/100.0;
printf("应变:%f\r\n",mic);
printf("温度:%f\r\n",strain_temp);
//printf("Receive data from server: \"%x\"\n",recv_data);
}
closesocket(client_socket);
WSACleanup();
return 0;
}
UDP 连接
这里我们的连接原理与 TCP 连接相似,这里我们直接给出完整代码:
点击查看完整 UDP 连接代码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <winsock2.h>
#include <math.h>
#include "stdint.h"
#define length_8 8 //定义一个宏,为传入8位16进制数的个数
#define PORT 8001
#define SERVER_IP "123.56.90.74"
#define BUFFER_SIZE 4196
const char* kExitFlag = "exit";
/* 返回ch字符在sign数组中的序号 */
int getIndexOfSigns(char ch)
{
if(ch >= '0' && ch <= '9')
{
return ch - '0';
}
if(ch >= 'A' && ch <='F')
{
return ch - 'A' + 10;
}
if(ch >= 'a' && ch <= 'f')
{
return ch - 'a' + 10;
}
return -1;
}
/* 十六进制数转换为十进制数 */
int hexToDec(char *source)
{
int sum = 0;
char low,high;
for(int i=0,j=7;i<4;i++){
//TODO
high = (source[i] & 0xf0)>>4;
low = source[i] & 0x0f;
sum += high*pow(16,j--)+low*pow(16,j--);
}
return sum;
}
unsigned char *fromhex(const char *str)
{
static unsigned char buf[512];
size_t len = strlen(str) / 2;
if (len > 512) len = 512;
for (size_t i = 0; i < len; i++) {
unsigned char c = 0;
if (str[i * 2] >= '0' && str[i*2] <= '9')
c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i*2] & ~0x20) <= 'F')
c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9')
c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F')
c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}
uint16_t CRC_16(uint8_t *temp)
{
uint8_t i,j;
uint16_t CRC_1 = 0xFFFF; //声明CRC寄存区,也就是步骤1
for(i = 0;i < 6;i++) //这里的for循环说的是步骤6中的重复步骤 2 到步骤 5
{
CRC_1 ^= temp[i]; //这里就是步骤2,进行异或运算
for(j = 0;j < 8;j++) //用来将异或后的低八位全部移出的for循环
{
if(CRC_1 & 0x01) //判断低八位的最后一位是否为1,为1时执行下列语句,也就是步骤3说的移位判断与步骤5说的右移8次
{
/*一定要先移位,再异或*/
CRC_1 >>=1; //移位后再异或,就是步骤4
CRC_1 ^= 0xA001; //0xA001为0x8005的逆序
}
else //若不为1,则直接移位。
{
CRC_1 >>=1;
}
}
}
// CRC_1 = (((CRC_1 & 0xFF)<<8) + (CRC_1>>8));
// printf("%04x\r\n",CRC_1); //用于打印检测CRC校验码
return(CRC_1);
}
int main() {
printf("启动UDP连接!\n");
// 初始化socket dll。
WORD winsock_version = MAKEWORD(2,2);
WSADATA wsa_data;
if (WSAStartup(winsock_version, &wsa_data) != 0) {
printf("Failed to init socket!\n");
return 1;
}
SOCKET client_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (client_socket == INVALID_SOCKET) {
printf("Failed to create server socket!\n");
return 2;
}
char recv_data[BUFFER_SIZE+1];
while (true) {
// uint8_t data[length_8];
char *data = new char[length_8];
printf("具体指令给格式为0+传感器编号(1,2,3,4,5)+0300010002,请输入采集传感器的编号(0表示退出采集):\n");
scanf("%s",data);
if (strcmp(data,kExitFlag)==0) {
printf("Exit!\n");
break;
}
uint16_t crc;
unsigned char * cmd;
char crc1[8];
cmd = fromhex(data);
crc = CRC_16(cmd);
uint8_t a = 0xFF;
for(int i=0;i<6;i++){
//TODO
crc1[i] = cmd[i];
}
crc1[6] = a & crc;
crc1[7] = (crc >> 8) & a;
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
if (sendto(client_socket, crc1, 8, 0,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0) {
printf("Failed to send data!\n");
break;
}
int ret = recvfrom(client_socket, recv_data, BUFFER_SIZE, 0,NULL,NULL);
if (ret < 0) {
printf("Failed to receive data!\n");
break;
}
recv_data[ret]=0; // correctly ends received string
char var = cmd[5];
if(var == 2){
printf("两个传感器:温度,湿度\r\n");
//TODO
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float temp = hexToDec(yb)/100.0;
float hum = hexToDec(wd);
printf("温度:%4.2f\r\n",temp);
printf("湿度:%4.2f\r\n",hum);
}else if(var == 1){
//TODO
printf("一个传感器:静力水准仪\r\n");
char nd[4];
for(int i=0;i<4;i++){
//TODO
nd[i] = recv_data[4+i];
}
float water_level = hexToDec(nd)*10.0;
printf("挠度:%6.2f\r\n",water_level);
}
}
closesocket(client_socket);
WSACleanup();
return 0;
}