一 两种发生粘包的情况:
- 接收方没有及时接受缓存区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
- 发送端需要等缓冲区满才发送出去,造成粘包 (发送数据时间间隔很短,数据也很小,会合到一起,造成粘包)
二 粘包的解决方案:
客户端循环接收,拿到接收数据的字节数的长度,用作判断结束循环的条件
调用struct模块,把一个类型变成固定长度的bytes类型
import struct
# 将一个数字转化成等长度的bytes类型。
ret = struct.pack('i', 183346)
print(ret, type(ret), len(ret))
# 通过unpack反解回来
ret1 = struct.unpack('i',ret)[0]
print(ret1, type(ret1))
# 但是通过struct 处理不能处理太大,数字太大会报错
2.1 low版解决粘包方案(远程执行命令)
服务器端:
import subprocess
import socket
import struct
phone = socket.socket()
phone.bind(('127.0.0.1',8848))
phone.listen()
print('已开机')
while 1:
conn, addr = phone.accept()
while 1:
try:
from_client_data = conn.recv(1024).decode("utf-8")
if from_client_data.upper() == 'Q':
print('客户端已经正常退出!')
break
else:
print(f'来自客户端的信息:{from_client_data}')
obj = subprocess.Popen(from_client_data,
shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
to_client_data = obj.stdout.read() + obj.stderr.read()
data_size = len(to_client_data)
print(data_size)
ret = struct.pack('i',data_size)
conn.send(ret)
conn.send(to_client_data)
except ConnectionResetError:
print('客户端异常断开连接!')
break
conn.close()
phone.close()
客户端:
import socket
import struct
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8848))
while 1:
try:
data = input('>>>')
if not data:
print('内容不能为空!')
continue
phone.send(data.encode('utf-8'))
if data.upper() == "Q":
break
else:
data_size = phone.recv(4)
ret = struct.unpack('i',data_size)[0]
data = b''
while len(data) < ret:
from_server_data = phone.recv(1024)
data += from_server_data
print(len(data))
print(f'来自服务器的消息:{data.decode("gbk")}')
except ConnectionResetError:
print('服务器异常断开连接')
break
phone.close()
2.2 自定制报头:
服务器端:
# 1. 自定义报头
head_dic = {
'file_name': 'test1',
'md5': 6567657678678,
'total_size': total_size,
}
# 2. json形式的报头
head_dic_json = json.dumps(head_dic)
# 3. bytes形式报头
head_dic_json_bytes = head_dic_json.encode('utf-8')
# 4. 获取bytes形式的报头的总字节数
len_head_dic_json_bytes = len(head_dic_json_bytes)
# 5. 将不固定的int总字节数变成固定长度的4个字节
four_head_bytes = struct.pack('i', len_head_dic_json_bytes)
# 6. 发送固定的4个字节
conn.send(four_head_bytes)
# 7. 发送报头数据
conn.send(head_dic_json_bytes)
# 8. 发送总数据
conn.send(result)
客户端:
# 1. 接收固定长度的4个字节
head_bytes = phone.recv(4)
# 2. 获得bytes类型字典的总字节数
len_head_dic_json_bytes = struct.unpack('i',head_bytes)[0]
# 3. 接收bytes形式的dic数据
head_dic_json_bytes = phone.recv(len_head_dic_json_bytes)
# 4. 转化成json类型dic
head_dic_json = head_dic_json_bytes.decode('utf-8')
# 5. 转化成字典形式的报头
head_dic = json.loads(head_dic_json)
三 基于网络的UDP协议的socket
客户端:
import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('192.168.14.198',9000))
while 1:
from_client_data = server.recvfrom(1024) # 阻塞,等待客户来消息
print(f' 33[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} 33[0m')
to_client_data = input('>>>').strip()
server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
server.close()
# 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.
# 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.
# 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.
客户端:
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while 1:
to_server_data = input('>>>:').strip()
client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',9000))
data,addr = client.recvfrom(1024)
print(f'来自服务端{addr}消息:{data.decode("utf-8")}')