不粘包,会重连的python TCP客户端代码(手打2021.12)

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员进化不脱发!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值