粘包:
半包:
注:只有TCP协议才有粘包现象, UDP协议永远不会粘包
TCP协议是面向连接的,面向流的,提供高可靠性服务。
UDP协议是无连接的,面向消息的,提供高效率服务。
两种情况会发生粘包
思路:
还存在的问题:
解决办法:
代码演示:接收大数据文件
# 服务端
# 导入模块
import socket
import struct
# 1320KB的数据内容
big_data = ("重要信息" * 110).encode("utf8")
# 计算大小
data_size = len(big_data)
# struct生成四字节流的信息
data_size_struct = struct.pack("i", data_size)
# 创建socket对象
server = socket.socket()
server.bind(("localhost", 5656))
server.listen()
conn, addr = server.accept()
# 先发送数据的大小
conn.send(data_size_struct)
# 发送大数据包
conn.send(big_data)
# 关闭
conn.close()
server.close()
# 客户端
# 导入模块
import socket
import struct
# 创建socket对象
client = socket.socket()
client.connect(("localhost", 5656))
# 读取文件大小
head = client.recv(4)
total = struct.unpack("i", head)[0]
# 根据大小接收数据
have = 0
data = bytes()
while have < total:
data += client.recv(1024)
have += 1024
print(data.decode("utf8"))
# 关闭
client.close()
参考代码:
# 服务端
# 导入模块
import os
import pickle
import socket
import struct
# 创建电影资源路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
MOVIE_DIR = os.path.join(BASE_DIR, "server_movie")
os.makedirs(MOVIE_DIR, exist_ok=True)
movie_list = os.listdir(MOVIE_DIR)
# 生成电影资源字典
movie_dict = {index: data for index, data in enumerate(movie_list, start=1)}
# 将字典转换为字节流数据
movie_pickle = pickle.dumps(movie_dict)
# 开启服务端
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
server.bind(("0.0.0.0", 9696))
server.listen(5)
# 长时间没有客户端连接
server.settimeout(5)
while True:
# 进行时间尝试捕获
try:
conn, addr = server.accept()
print(f"{addr}发送链接请求")
# 发送定影资源信息
conn.send(movie_pickle)
except socket.timeout:
print("长时间没有客户端连接,服务端自动关闭")
break
while True:
# 接收选择信息
res = conn.recv(1024).decode("utf8")
# 退出或断开连接
if res == "q" or not res:
print(f"客户端{addr}断开连接")
conn.close()
break
# 获取资源路径
choice_index = int(res)
movie_dir = os.path.join(MOVIE_DIR, f"{movie_dict.get(choice_index)}")
# 读取电影资源
with open(movie_dir, "rb") as fp:
movie_data = fp.read()
# 计算大小并发送
head = struct.pack("i", len(movie_data))
conn.send(head)
conn.send(movie_data)
print(f"向{addr}发送{movie_dict.get(choice_index)}完成")
# 一个客户端完成
conn.close()
# 关闭服务端
server.close()
# 客户端
# 导入模块
import os.path
import pickle
import socket
import struct
# 创建保存资源路径
DB_DIR = os.path.dirname(os.path.abspath(__file__))
MOVIE_DIR = os.path.join(DB_DIR, "client_movie")
os.makedirs(MOVIE_DIR, exist_ok=True)
# 开始客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
client.connect(("localhost", 9696))
# 接收资源信息字典
movie_pickle = client.recv(1024)
movie_dict = pickle.loads(movie_pickle)
while True:
# 打印可获取的资源
print("可以从服务端拿到的资源信息如下:")
for key, value in movie_dict.items():
print(f"编号【{key}】 资源信息:{value}")
# 选择判断
choice = input("根据编号进行资源选择(Q/q:退出):>>>").strip()
if choice.lower() == "q":
client.send(choice.encode("utf8"))
break
elif choice not in [str(i) for i in range(1, len(movie_dict) + 1)]:
print("输入有误,请重新检查")
continue
# 发送选择编号
client.send(choice.encode("utf8"))
# 获取资源名字
movie_name = movie_dict.get(int(choice))
# 获取资源大小
head_pack = client.recv(4)
total = struct.unpack("i", head_pack)[0]
# 下载接收文件
have = 0
movie_data = bytes()
print(f"正在下载{movie_name}")
while have < total:
movie_data += client.recv(1024)
have += 1024
# 进度条显示
progress = have / total
bar_length = 30
bar = '=' * int(progress * bar_length) + '-' * (bar_length - int(progress * bar_length))
percentage = progress * 100
print(f'\r[{bar}] {percentage:.2f}% Complete', end='', flush=True)
# 保存下载的资源
movie_dir = os.path.join(MOVIE_DIR, movie_name)
with open(movie_dir, "wb") as fp:
fp.write(movie_data)
print(f"\n{movie_name} 保存成功")
# 关闭
client.close()