Python--网络编程-----解决粘包问题-终极版

一、服务端代码:

 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:\Users\xu516\PycharmProjects\Python全栈开发\第三模块\网络编程\05 解决粘包问题\终极版 的目录
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 >>:

代码显示结果正常,没有发生粘包现象,

posted @ 2018-04-08 23:16  xudachen  阅读(246)  评论(0编辑  收藏  举报