上一章对于TCP通信的黏包现象有了一个初步的认识,可以对黏包下个结论什么叫做黏包?
其实就是基于TCP通信的时候当我们发送的数据大于接收端可接受的最大数据范围时,这个时候就会产生拆包现象。在了解了这一现象之后就能够解决黏包。
黏包的两种情况 :一是两个数据特别小,这样会被一次发送出去
二是一条数据特别大啊,会被拆分成很多部分多次发送出去
那么到底要如何解决啦?
黏包的问题的本质:我们不知道要接受数据的大小
首先发送一条信息,显示待发送数据的大小。另一端接收信息,根据显示的大小来 recv( )多大的值
解决黏包现象
未使用struck模块解决黏包
server端
22
1
import socket
2
3
4
sk = socket.socket()
5
sk.bind(('127.0.0.1', 8080))
6
sk.listen()
7
8
conn, addr = sk.accept()
9
10
while True:
11
cmd = input('>>>')
12
if cmd == 'q':
13
conn.send(b'q')
14
break
15
conn.send(cmd.encode('gbk'))
16
num = conn.recv(1024).decode('utf-8')
17
conn.send(b'ok')
18
res = conn.recv(int(num)).decode('gbk') # recv()里面接收的为数字
19
print(res)
20
21
conn.close()
22
sk.close()
client端
25
1
import socket
2
import subprocess
3
4
5
sk = socket.socket()
6
sk.connect(('127.0.0.1', 8080))
7
8
while True:
9
cmd = sk.recv(1024).decode('gbk')
10
if cmd == 'q':
11
break
12
res = subprocess.Popen(cmd, shell=True,
13
stdout=subprocess.PIPE,
14
stderr=subprocess.PIPE
15
)
16
std_out = res.stdout.read() # 读取出来的是队列,调用len方法
17
# print(std_out.decode('gbk'))
18
# print(len(std_out))
19
std_err = res.stderr.read()
20
sk.send(str(len(std_out) + len(std_err)).encode('utf-8')) # 这里要注意数字是不能encode的
21
sk.recv(1024)
22
sk.send(std_out)
23
sk.send(std_err)
24
25
sk.close()
导入struct模块解决黏包问题
struct模块的作用与用法?
在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,不必担心太多的问题,而当传递诸如int、char之类的基本数据的时候,就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。
server
23
1
import socket
2
import struct
3
4
5
sk = socket.socket()
6
sk.bind(('127.0.0.1', 8080))
7
sk.listen()
8
9
conn, addr = sk.accept()
10
11
while True:
12
cmd = input('>>>')
13
if cmd == 'q':
14
conn.send(b'q')
15
break
16
conn.send(cmd.encode('gbk'))
17
num = conn.recv(4)
18
num = struct.unpack('i', num)[0]
19
res = conn.recv(int(num)).decode('gbk')
20
print(res)
21
22
conn.close()
23
sk.close()
client端
22
1
import socket
2
import struct
3
import subprocess
4
5
6
sk = socket.socket()
7
sk.connect(('127.0.0.1', 8080))
8
while True:
9
cmd = sk.recv(1024).decode('gbk')
10
if cmd == 'q':
11
break
12
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
13
std_out = res.stdout.read()
14
std_err = res.stderr.read()
15
len_num = len(std_out)+len(std_err)
16
num_bytes = struct.pack('i', len_num)
17
sk.send(num_bytes)
18
sk.send(std_out)
19
sk.send(std_err)
20
21
sk.close()
22
补充知识点
基于struct 模块 实现文件的下载
sever端
38
1
# 实现一个大文件的上传和下载
2
# 匹配文件 IP地址 端口号
3
4
import socket
5
import json
6
7
8
sk = socket.socket()
9
sk.bind(('127.0.0.1', 8090))
10
sk.listen()
11
12
buffer = 1024
13
conn, addr = sk.accept()
14
15
head_len = conn.recv(4) # 接收四个字节
16
head_len = conn.unpack('i', head_len)[0]
17
json_head = conn.recv(head_len).decode('utf-8')
18
head = json.loads(json_head)
19
filesize = head['filesize']
20
print(filesize)
21
22
with open(r'dir\%s' % head['filename'], 'wb') as f:
23
while filesize:
24
if filesize >= buffer:
25
print(filesize)
26
content = conn.recv(buffer)
27
f.write(content)
28
filesize -= buffer
29
else:
30
content = conn.recv(filesize)
31
f.write(content)
32
filesize = 0
33
print('===>', len(content))
34
print(filesize)
35
36
print('服务器。。。')
37
conn.close()
38
sk.close()
client端
x
1
import os
2
import json
3
import struct
4
import socket
5
6
7
sk = socket.socket()
8
sk.connect(('127.0.0.1', 8090))
9
buffer = 4096
10
# 发送文件
11
head = {'filepath': r'D:DocumentsoCam',
12
'filename': r'test.mp4',
13
'filesize': None}
14
15
file_path = os.path.join(head['filepath'], head['filename'])
16
filesize = os.path.getsize(file_path)
17
head['filesize'] = filesize
18
json_head = json.dumps(head)
19
bytes_head = json_head.encode('utf-8')
20
# 计算head的长度
21
head_len = len(bytes_head)
22
pack_len = struct.pack('i', head_len)
23
sk.send(pack_len)
24
sk.send(bytes_head)
25
26
with open(file_path, 'rb') as f:
27
while filesize:
28
if filesize >= buffer:
29
content = f.read(buffer)
30
print('====>', len(content))
31
sk.send(content)
32
filesize -= buffer
33
else:
34
content = f.read(filesize)
35
sk.send(content)
36
filesize = 0
37
38
sk.close()
39