From ce045f0c860db49974da3fca4909e5fcf437ab08 Mon Sep 17 00:00:00 2001 From: j <907782764@qq.com> Date: Thu, 4 Mar 2021 10:01:05 +0800 Subject: [PATCH] add python client --- python客户端代码/readme.md | 74 ++++++++++++ python客户端代码/weaving_app.py | 70 ++++++++++++ python客户端代码/weaving_client.py | 177 +++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 python客户端代码/readme.md create mode 100644 python客户端代码/weaving_app.py create mode 100644 python客户端代码/weaving_client.py diff --git a/python客户端代码/readme.md b/python客户端代码/readme.md new file mode 100644 index 0000000..0728aa5 --- /dev/null +++ b/python客户端代码/readme.md @@ -0,0 +1,74 @@ +# python 版 weaving_client + +#### 文件介绍: + +**weaving_client.py:** python 版的 weaving_client 框架,使用 python3 + +**weaving_app.py:** 作为示例程序,调用了 weaving_client.py + + + +#### 使用步骤: + +##### 1.用一个IP地址加端口(如 192.168.0.20:5678)初始化一个 WeavingClinet 的对象: + +```python +server_addr = '192.168.0.20:5678' +wc = WeavingClient(server_addr) +``` + + + +##### 2.使用 wc.reg_wcmd 为你感兴趣的命令字注册无返回值的回调函数(协议解析出的有效数据字节串将作为参数传递给它): + +```python +WCMD_HEARTBEAT = 0x99 # WCMD 指 weaving 框架里的命令字 "weaving cmd" +WCMD_SOMECMD + +def some_function(bdata:bytes): + print('解析出的有效数据字节序列:{}'.format(bdata)) + +wc.reg_wcmd(WCMD_HEARTBEAT,None) # 不处理心跳包,其实不使用 reg_wcmd 注册的命令字默认不处理 +wc.reg_wcmd(WCMD_SOMECMD,some_function) + +``` + + + +##### 3.指定连接到服务端时的回调函数,通常需要用这个来注册客户端 + +```python +reg_msg = {'cmd':'reg','id':'12345678'} # 注意这个 cmd 相对于 wcmd 只是一个普通数据的一部分 + +def send_data(bdata:bytes): + wc.send_data(CMD_SOMECMD,bdata) + +def send_msg(msg:dict): + bdata = json.dumps(msg).encode() + send_data(bdata) + +def reg_self(): + self.send_msg(reg_msg) + +wc.on_connect = reg_self +``` + + + +##### 4.现在完成了基本配置,可以启动了: + +```python +def start(): + wc.start() + print('Now we can do other things...') + print('This is just one example, if you want exit, you can:') + print('on windows: print Ctrl + Pause Break') + print('on linux: print Ctrl + C') + +start() +``` + + + +##### 5.更详细的使用方法参见 weaving_app.py 或自由定制 + diff --git a/python客户端代码/weaving_app.py b/python客户端代码/weaving_app.py new file mode 100644 index 0000000..0febafc --- /dev/null +++ b/python客户端代码/weaving_app.py @@ -0,0 +1,70 @@ + +import weaving_client +from weaving_client import * +import json + +flag_test = False + +# ------------------ app define begin ------------------------------ +WCMD_HEARTBEAT = 0x99 +WCMD_SOMECMD = 0x02 + + +server_addr = '192.168.0.20:5678' +reg_msg = {'cmd':'reg','id':'12345678'} + +# ------------------ app define end -------------------------------- + + +class WeavingApp(): + def __init__(self, server_addr): + self.cmd_dict = {} + self.lock_playing = False + self.wc = WeavingClient(server_addr) + self.wc.reg_wcmd(WCMD_HEARTBEAT,None) + self.wc.reg_wcmd(WCMD_SOMECMD,self.proc_msg) + self.wc.on_connect = self.reg_self + self.reg_cmd('echo',self.cmd_echo) + + def send_data(self,bdata:bytes): + self.wc.send_data(WCMD_SOMECMD,bdata) + + def send_msg(self,msg:dict): + bdata = json.dumps(msg).encode() + self.send_data(bdata) + + def reg_cmd(self,cmd,func): # this cmd is self cmd, the difference in weaving cmd(wcmd) + self.cmd_dict[cmd] = func + + def start(self): + self.wc.start() + print('Now we can do other things...') + print('This is just one example, if you want exit, you can:') + print('on windows: print Ctrl + Pause Break') + print('on linux: print Ctrl + C') + + def reg_self(self): + self.send_msg(reg_msg) + + def proc_msg(self,bmsg:bytes): + dmsg = json.loads(bmsg.decode()) + cmd = dmsg.get('cmd','') + data = dmsg.get('data','') + func = self.cmd_dict.get(cmd,None) + if(func): + func(data) + + def cmd_echo(self,data:str): + wclog('data is: '+data) + if(data): + os.system('echo {}'.format(data)) + +def main(): + weaving_client.flag_use_wclog = True # 可以在这种位置开关 weaving_client 里的 wclog 函数的打印功能 + wa = WeavingApp(server_addr) + wa.start() + +if __name__ == '__main__': + main() + + diff --git a/python客户端代码/weaving_client.py b/python客户端代码/weaving_client.py new file mode 100644 index 0000000..4044320 --- /dev/null +++ b/python客户端代码/weaving_client.py @@ -0,0 +1,177 @@ +import socket +import threading +import time +import traceback +import os + +# ------------------ function for weaving port begin --------------------- + +flag_use_wclog = False + +def get_time(): + a = time.time() # such like 1579141285.7733445 + b = int(a) # such like 1579141285 + e = time.localtime(b) + f = time.strftime("%y%m%d-%H:%M:%S",e) # such like '200116102125' + return f + +def wclog(*x): + if(flag_use_wclog): + s = ''.join(x) + print(get_time(),s,flush=True) + +# crc16_xmodem +# 0x11 0x22 0x33 0x44 crc is 0xdd 0x33 +# 0x12 0x34 0x56 0x78 crc is 0xb4 0x2c +def crc16_xmodem(bdata:bytes)->bytes: + wcrc = 0 + for b in bdata: + for j in range(8): + treat = b & 0x80 + b <<= 1 + bcrc = (wcrc >> 8) & 0x80 + wcrc <<= 1 + wcrc = wcrc & 0xffff + if (treat != bcrc): + wcrc ^= 0x1021 + return wcrc.to_bytes(2,'big') + +def con_crc(bdata:bytes)->bytes: + return bdata + crc16_xmodem(bdata) + +def chk_crc(bdata:bytes)->bool: + if(len(bdata)<3): + return False + if(bdata[-2:] == crc16_xmodem(bdata[:-2])): + return True + else: + return False + +def weaving_pack(icmd:int,bdata:bytes)->bytes: + len_bdata = len(bdata) + blen = len_bdata.to_bytes(4,'little').rstrip(b'\x00') + len_blen = len(blen) + ret = bytes([icmd,len_blen]) + blen + ret = con_crc(ret) + bdata + return ret + +def weaving_unpack(bdata:bytes)->(bool,int,bytes,bytes): + wclog('into weaving_unpack') + flag_result = False + icmd = 0x00 + bmsg = b'' + + while(True): + if(len(bdata) < 4): + return flag_result,icmd,bmsg,bdata + len_blen = bdata[1] + if(len(bdata) < len_blen+4): + return flag_result,icmd,bmsg,bdata + blen = bdata[2:2+len_blen] + len_bdata = int.from_bytes(blen,'little') + if(len(bdata) < len_blen+4+len_bdata): + return flag_result,icmd,bmsg,bdata + if(not chk_crc(bdata[:4+len_blen])): + bdata = bdata[1:] + continue + else: + flag_result,icmd,bmsg,bdata = True, bdata[0], bdata[4+len_blen:4+len_blen+len_bdata], bdata[4+len_blen+len_bdata:] + return flag_result,icmd,bmsg,bdata + +# ------------------ function for weaving port end ------------------------ + + +class WeavingClient(threading.Thread): + + def __init__(self, addr, recv_timeout=10, reconnection_delay=10, client_id='1'): + threading.Thread.__init__(self, name="WeavingClient" + client_id) + self.addr = addr + self.recv_timeout = recv_timeout + self.reconnection_delay = reconnection_delay + self.host, self.port = addr.split(':') + self.port = int(self.port) + self.all_bdata = b'' + self.wcmd_dict = {} # wcmd is weaving cmd, the difference in app cmd + self.on_connect = None + + def reg_wcmd(self,cmd,func): + self.wcmd_dict[cmd] = func + + def run(self): + self.do_connect() + while True: + try: + wclog('recving data...') + self.recv_data() + except OSError: + traceback.print_exc() + wclog('{} connect error, reconnection after {}s'.format(self.addr,self.reconnection_delay)) + time.sleep(self.reconnection_delay) + self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.do_connect() + except Exception as e: + traceback.print_exc() + wclog('other error, reconnection after {}s'.format(self.reconnection_delay)) + time.sleep(self.reconnection_delay) + self.do_connect() + + def do_connect(self): + while True: + try: + self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sck.settimeout(self.recv_timeout) + wclog('try to connect {}'.format(self.addr)) + self.sck.connect((self.host, self.port)) + wclog('client start connect to {}'.format(self.addr)) + if(callable(self.on_connect)): + self.on_connect() + break + except ConnectionRefusedError: + wclog('{} refused or not started, reconnection after {}s'.format(self.reconnection_delay, self.addr)) + time.sleep(self.reconnection_delay) + except Exception as e: + traceback.print_exc() + wclog('do connect error: {}'.format(str(e))) + wclog('reconnection after {}s'.format(self.reconnection_delay)) + time.sleep(self.reconnection_delay) + + def send_data(self,cmd,bdata): + bdata = weaving_pack(cmd,bdata) + self.sck.send(bdata) + + def recv_data(self): + try: + data = self.sck.recv(1024) + if data: + wclog('recv data:{}'.format(data)) + self.all_bdata += data + flag_need_proc = True + while(flag_need_proc): + flag_need_proc = self.proc_data() + else: + wclog('the socket may be closed, reconnection after {}s'.format(self.reconnection_delay)) + time.sleep(self.reconnection_delay) + self.do_connect() + except socket.timeout: + wclog('recv timeout, reconnection after {}s'.format(self.reconnection_delay)) + time.sleep(self.reconnection_delay) + self.do_connect() + except Exception as e: + traceback.print_exc() + self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.do_connect() + + def proc_data(self): + wclog('into proc_data') + wclog('all_bdata is: {}'.format(self.all_bdata)) + flag_result, icmd, bmsg, self.all_bdata = weaving_unpack(self.all_bdata) + if(flag_result): + wclog('proc_data, flag_result is True') + func = self.wcmd_dict.get(icmd,None) + if(callable(func)): + threading.Thread(target=func,args=(bmsg,)).start() + return True + else: + return False +