网络编程 黏包
############# ========= 黏包 现象
#注意:只有TCP有粘包现象,UDP永远不会粘包
#同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的
#另一部分结果,这种显现就是黏包。
#黏包成因
# 应用程序所看到的数据是一个整体,或说是一个流(stream),
# 一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,
# 这也是容易出现粘包问题的原因。
#tcp协议的拆包机制
#当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆分成几个数据包发送出去。
#TCP是面向连接的,面向流的,提高可靠性服务。
#面向流的通信特点和Nagle算法
#收发两端(客户端和服务端)都要有----成对的socket,因此,发送端为了将多个发往
#接收端的包,更有效的发到对方,使用优化这样接受端,就难于分辨出来了,必须提供科学的
#拆包机制。即面向流的通信是无消息保护边界的。
#会发生黏包的两种情况。
#发送端需要等待缓冲区满或等待一段很少的时间,才发送出去,造成黏包。(发送数据时间时间
#间隔很短,数据很小,会合到一起,产生黏包)
#接受方不及时接受缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接受了一部分,
#服务端下次在收的时候还是从缓冲区那上次遗留的数据,产生黏包
#总结:
#1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
#2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
#黏包的解决方案
# 解决方案一
# 问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,
# 如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
# import os
# import json
# import struct
# import socket
# sk =socket.socket()
# sk.connect(("127.0.0.1",9555))
# def get_filename(filepath):
# filename = os.path.basename(filepath)
# return filename
# li = ["Upload","Download"]
# for num,i in enumerate(li,1):
# print(num,i)
# user = input("请按序号选择功能:")
# if user == "1":
# filepath = input("请输入要上传的文件路径:")
# file_getsize = os.path.getsize(filepath)
# filename = get_filename(filepath)
# dic = {"operate":"Upload","filename":filename,"file_getsize":file_getsize}
# byt_dic = json.dumps(dic).encode("utf-8")
# ret = struct.pack("i",len(byt_dic))
# sk.send(ret + byt_dic)
# with open(filepath,"rb")as f1:
# while file_getsize:
# content = f1.read(1024)
# sk.send(content)
# file_getsize -= len(content)
# 解决方案进阶 struct 模块
# 刚刚的方法,问题在于我们我们在发送
# 我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。
# 这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,
# 那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。
# import socket
# import struct
# import json
# sk = socket.socket()
# sk.bind(("127.0.0.1",9555))
# sk.listen()
# conn,addr = sk.accept()
# dic_len = conn.recv(4)
# dic_len = struct.unpack("i",dic_len)[0]
# content = conn.recv(dic_len).decode("utf-8")
# content_dic = json.loads(content)
# if content_dic["operate"] == "Upload":
# with open(content_dic["filename"],"ab")as f1:
# while content_dic["file_getsize"]:
# file = conn.recv(1024)
# f1.write(file)
# content_dic["file_getsize"] -= len(file)
# conn.close()
# sk.close()