一、服务端代码:
1 import socket 2 import subprocess 3 import struct 4 import json 5 6 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 # phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 8 phone.bind(('127.0.0.1', 8081)) # 0-65535:0-1024给操作系统使用 9 phone.listen(5) 10 11 print('starting......') 12 while True: # 链接循环 13 conn, client_addr = phone.accept() 14 print(client_addr) 15 16 while True: # 通信循环 17 try: 18 # 1、收命令 19 cmd = conn.recv(1024) 20 if not cmd: # 适用于linux操作系统 21 break 22 print('客户端的数据', cmd) 23 24 # 2、执行命令,拿到结果 25 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, 26 stdout=subprocess.PIPE, 27 stderr=subprocess.PIPE) 28 29 stdout = obj.stdout.read() 30 stderr = obj.stderr.read() 31 # 3、把命令的结果返回给客户端 32 # 第一步:制作固定长度的报头 33 header_dic = { 34 'filename': 'a.txt', 35 'md5': 'xxdxx', 36 'total_size': len(stdout) + len(stderr) 37 } 38 39 header_json = json.dumps(header_dic) 40 header_bytes = header_json.encode('utf-8') 41 42 # 第二步:先发送报头的长度 43 conn.send(struct.pack('i', len(header_bytes))) 44 45 # 第三步:把包头发送给客户端 46 conn.send(header_bytes) 47 48 # 第三步:再发送真实的数据 49 conn.send(stdout) 50 conn.send(stderr) 51 52 except ConnectionResetError: # 适用于Windows操作系统 53 break 54 55 conn.close() 56 57 phone.close()
二、客户端代码:
1 import socket 2 import struct 3 import json 4 5 6 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 8 phone.connect(('127.0.0.1', 8081)) 9 10 while True: 11 # 1、发命令 12 cmd = input('>>:').strip() 13 if not cmd: 14 continue 15 phone.send(cmd.encode('utf-8')) 16 17 # 2、拿命令的结果,并打印 18 19 # 第一步:先收报头的长度 20 obj = phone.recv(4) 21 header_size = struct.unpack('i', obj)[0] 22 23 # 第二步:再收报头 24 header_bytes = phone.recv(header_size) 25 26 # 第三步:从报头中解析出对真实数据的描述信息 27 header_json = header_bytes.decode('utf-8') 28 header_dic = json.loads(header_json) 29 print(header_dic) 30 total_size = header_dic['total_size'] 31 32 # 第四步: 接收真实的数据 33 recv_size = 0 34 recv_data = b'' 35 while recv_size < total_size: 36 res = phone.recv(1024) # 1024是一个坑 37 recv_data += res 38 recv_size += len(res) 39 print(recv_data.decode('gbk')) 40 41 phone.close()
在客户端依次执行,ipconfig 和 dir
客户端结果为:
1 >>:ipconfig 2 {'filename': 'a.txt', 'md5': 'xxdxx', 'total_size': 1481} 3 4 Windows IP 配置 5 6 7 无线局域网适配器 本地连接* 3: 8 9 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 10 连接特定的 DNS 后缀 . . . . . . . : 11 12 以太网适配器 VMware Network Adapter VMnet1: 13 14 连接特定的 DNS 后缀 . . . . . . . : 15 本地链接 IPv6 地址. . . . . . . . : fe80::5d54:4c1:d7d6:c647%6 16 IPv4 地址 . . . . . . . . . . . . : 192.168.189.1 17 子网掩码 . . . . . . . . . . . . : 255.255.255.0 18 默认网关. . . . . . . . . . . . . : 19 20 以太网适配器 VMware Network Adapter VMnet8: 21 22 连接特定的 DNS 后缀 . . . . . . . : 23 本地链接 IPv6 地址. . . . . . . . : fe80::680e:7f79:aed1:fe62%10 24 IPv4 地址 . . . . . . . . . . . . : 192.168.254.1 25 子网掩码 . . . . . . . . . . . . : 255.255.255.0 26 默认网关. . . . . . . . . . . . . : 27 28 无线局域网适配器 WLAN: 29 30 连接特定的 DNS 后缀 . . . . . . . : DHCP HOST 31 本地链接 IPv6 地址. . . . . . . . : fe80::9c84:419c:e3af:89dd%11 32 IPv4 地址 . . . . . . . . . . . . : 192.168.0.106 33 子网掩码 . . . . . . . . . . . . : 255.255.255.0 34 默认网关. . . . . . . . . . . . . : 192.168.0.1 35 36 以太网适配器 蓝牙网络连接: 37 38 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 39 连接特定的 DNS 后缀 . . . . . . . : 40 41 隧道适配器 Teredo Tunneling Pseudo-Interface: 42 43 连接特定的 DNS 后缀 . . . . . . . : 44 IPv6 地址 . . . . . . . . . . . . : 2001:0:9d38:953c:20d3:83ef:d86a:f024 45 本地链接 IPv6 地址. . . . . . . . : fe80::20d3:83ef:d86a:f024%2 46 默认网关. . . . . . . . . . . . . : :: 47 48 >>:dir 49 {'filename': 'a.txt', 'md5': 'xxdxx', 'total_size': 472} 50 驱动器 C 中的卷是 OS 51 卷的序列号是 7849-BAF9 52 53 C:Usersxu516PycharmProjectsPython全栈开发第三模块网络编程 5 解决粘包问题终极版 的目录 54 55 2018/04/08 23:11 <DIR> . 56 2018/04/08 23:11 <DIR> .. 57 2018/04/08 22:08 601 struct模块的使用.py 58 2018/04/08 23:11 1,032 客户端.py 59 2018/04/08 23:11 1,796 服务端.py 60 3 个文件 3,429 字节 61 2 个目录 36,957,728,768 可用字节 62 63 >>:
代码显示结果正常,没有发生粘包现象,