1.通过3个互斥锁与一个布尔变量进行进行管理,避免使用轮询方法,增加cpu占用率
如下:
self.mutex_client_link = threading.Lock()
self.mutex_client_send = threading.Lock() # 当没有连接接上服务端的时候,通过该锁,使得发送线程阻塞
self.mutex_client_recv = threading.Lock() # 当没有连接接上服务端的时候,通过该锁,使得接受线程阻塞
self.limit_bool = False # 通过这个变量控制先进行客户端地址的接收再进行发送,避免由于短时短线,造成连续开启与回收线程的错误发送
2.通过try方法捕捉与跳过报错的方法,判断与保证通过绑定指定端口与服务端的断线重连
def link(self):
"""为防止连接服务端失败,这里拉起一个连接线程持续守护,当客户端收发线程,由于无法连接服务端失效的时候执行重新连接"""
while True:
try:
mutexFlag = self.mutex_client_link.acquire(True) # True表示堵塞
self.client = socket(AF_INET, SOCK_STREAM)
self.client.bind(('', 4440))
self.client.connect((self.HOST, self.PORT))
print("成功连接客户端,开启监听线程......")
self.limit_bool = True # 当该变量为真的时候,才可以使用 接收与发送接口
self.mutex_client_send.release()
self.mutex_client_recv.release()
except OSError as e:
if e.errno == 10048:
print("已成功连接一号关节电机端口:4440,自动跳过该重复连接" )
continue
self.limit_bool = False
try:
self.mutex_client_link.release()
except:
pass
print("重连失败......")
客户端代码正文,需要删除 status_total_data 与 detect_logic 文件的引用部分才能用
from socket import *
import json
import struct
import threading
import time
from status_total_data import s_data # 注意这里 status_total_data 文件的位置,如果为python2环境 的话会有引用错误
from detect_logic import det_l
global_motor1_logic = None # 一号(末端)电机的逻辑变量
"""关节电机的客户端参数设置"""
class Client_link():
"""用于初始化。tcp客户端"""
def __init__(self,HOST = '127.0.0.1',PORT = 8081):
# self.HOST = HOST
# self.PORT = PORT
self.HOST = "自定义" # 控制服务器地址
self.PORT = 8888
self.repeat_num = 10 #设置当没有接收到完整数据帧的时候,重复连接的次数
self.init_motor()
def init_motor(self):
"""用于初始化,关节电机控制参数"""
self.m1_angle_speed = 0
self.m1_cnt_speed = 0
self.m1_angle_dis = 0
self.m1_cnt_dis= 0
self.m2_angle_speed = 0
self.m2_cnt_speed = 0
self.m2_angle_dis = 0
self.m2_cnt_dis = 0
def infor_analysis(self,json_buf):
"""通过分析接收到的,下位机发送的json数据,对现有控制数据进行更新"""
if json_buf["status"] == "Motor_c":
self.extrect_and_turn(json_buf)
#self.save_motor_c(json_buf["dict"]) 预留接口,方便后续该巡检项目的二次开发
# elif json_buf["Dete_m"] == "Odom_m":
def extrect_and_turn(self,json_buf):
"""提取服务端发送来的的,控制命令,并执行对应的动作"""
global global_motor1_logic
self.m1_angle_speed = json_buf["dict"]["m1_a_speed"]
self.m1_angle_dis = json_buf["dict"]["m1_a_dis"]
global_motor1_logic.set_zero_moudle_speed(self.m1_angle_speed)
global_motor1_logic.turn_cnt_distance(self.m1_angle_dis) # 输入运动编码器计数量
def save_motor_c(self, data_dict):
"""预留接口用于显示等"""
s_data.m1_angle_speed = data_dict["m1_a_speed"]
s_data.m1_cnt_speed = data_dict["m1_cnt_speed"]
s_data.m1_angle_dis = data_dict["m1_a_dis"]
s_data.m1_cnt_dis = data_dict["m1_cnt_dis"]
s_data.m2_angle_speed = data_dict["m2_a_speed"]
s_data.m2_cnt_speed = data_dict["m2_cnt_speed"]
s_data.m2_angle_dis = data_dict["m2_a_dis"]
s_data.m2_cnt_dis = data_dict["m2_cnt_dis"]
def start_client(self):
"""开启客户端"""
self.mutex_client_link = threading.Lock()
self.mutex_client_send = threading.Lock() # 当没有连接接上服务端的时候,通过该锁,使得发送线程阻塞
self.mutex_client_recv = threading.Lock() # 当没有连接接上服务端的时候,通过该锁,使得接受线程阻塞
self.limit_bool = False # 通过这个变量控制先进行客户端地址的接收再进行发送,避免由于短时短线,造成连续开启与回收线程的错误发送
t_link = threading.Thread(target=self.link)
#t_link.setDaemon(True) # 当断开后,通过守护线程自动回收
t_link.start()
self.t_client_recv = threading.Thread(target=self.run_recv)
#self.t_client_recv.setDaemon(True) # 当断开后,通过守护线程自动回收
self.t_client_recv.start()
self.t_client_send = threading.Thread(target=self.listen_send)
# self.t_client_recv.setDaemon(True) # 当断开后,通过守护线程自动回收
self.t_client_send.start()
def link(self):
"""为防止连接服务端失败,这里拉起一个连接线程持续守护,当客户端收发线程,由于无法连接服务端失效的时候执行重新连接"""
while True:
try:
mutexFlag = self.mutex_client_link.acquire(True) # True表示堵塞
self.client = socket(AF_INET, SOCK_STREAM)
self.client.bind(('', 4440))
self.client.connect((self.HOST, self.PORT))
print("成功连接客户端,开启监听线程......")
self.limit_bool = True # 当该变量为真的时候,才可以使用 接收与发送接口
self.mutex_client_send.release()
self.mutex_client_recv.release()
except OSError as e:
if e.errno == 10048:
print("已成功连接一号关节电机端口:4440,自动跳过该重复连接" )
continue
self.limit_bool = False
try:
self.mutex_client_link.release()
except:
pass
print("重连失败......")
def run_recv(self):
"""客户端监听程序"""
print('开始连接下位机服务端......') # 等待cilent的连接
while True:
try:
if self.limit_bool:
print("等待接受服务端数据------")
length = self.client.recv(4)
len_data = struct.unpack('i', length)
len_data = len_data[0]
print('接收到的原始长度字符%s:' % length)
print('数据长度为%s:' % len_data)
buf = self.receive_real(len_data)
print("接收到的真实数据为: " + str(buf))
json_buf = json.loads(buf)
self.infor_analysis(json_buf)
time.sleep(0.01)
else:
self.mutex_client_recv.acquire(True) # True表示堵塞
except:
try:
self.mutex_client_link.release()
print("接收连接失败,解锁连接线程")
except:
pass
print("连接服务端,断线,关闭监听流程,等待重连......")
self.limit_bool = False
def receive_real(self,len_data):
"""接收真实数据"""
""" len_data 为预先发过来的真实数据长度 """
buf = b''
recv_size = 0
recv_num = 0 # 接收次数
buf = self.client.recv(len_data)
while (len(buf) <len_data and recv_num < self.repeat_num ):
buftmp = self.client.recv(len_data - len(buf))
buf = buf + buftmp
recv_num = recv_num + 1
if len(buf) != len_data:
print("服务端应发送长度为" + str(len_data) +", 实际接收长度为: " + str(len(buf)) )
time.sleep(100)
return buf
def listen_send(self):
"""用于发送关节电机回收到的状态值"""
data = "cs_send"
print("准备开启发送数据线程------")
while True:
print("发送cs")
self.send_data(data)
time.sleep(0.02)
def send_data(self,data):
"""用于发送数据"""
try:
if self.limit_bool:
data = json.dumps(data)
length = len(data)
len_data = struct.pack('i', length)
# 固定字节长度,告知数据长度为多少
self.client.send(len_data)
self.client.send(data.encode('utf-8'))
print("发送数据完成")
else:
self.mutex_client_send.acquire(True) # True表示堵塞
except:
try:
self.mutex_client_link.release()
print("发送连接失败,解锁连接线程")
time.sleep(1)
except:
pass
print("连接服务端,断线,关闭监听流程,等待重连......")
self.limit_bool = False