今日内容:1、流式协议=》粘包问题(***)解决方案:自定义应用层协议注意:head+datahead的长度必须固定2、基于udp协议的套接字通信先启动客户端是否可以???服务端是否需要listen???udp协议应用场景3、socketserver模块==========================================================================================打印socket对象 得到的 不是地址  因为pyhon在str__中进行了  优化,显示的是一些配置信息accept()请求建立之后,会返回了两个值这里是函数的返回值 ,而不是解压赋值conn 和 sever=socket(AF_INET,SOCK_STREAM)
都是相同套接字对象sever是用来和 服务端进行通信的
但是conn是用来和客户端进行通信的阻塞: 像这些函数的功能都是接收数据,如果数据没到,就在原地一直等有数据过来就不阻塞了input accept recv 都会阻塞(得到运行条件后,就继续恢复运行)accept接收到数据就会继续运行所有的I/O操作,都会引起阻塞阻塞问题----实例剖析:代码段while n<10:print(f'当前是第{n+1}次,取数据------------------------')data = client.recv(128)header += dataprint(data.decode('utf-8'))n+=1print('嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤')这段代码的执行结果为:当前是第1次,取数据------------------------东风夜放花千树。更吹落、星如雨。宝马雕车香满路。凤箫声动,玉壶光当前是第2次,取数据------------------------转,一夜鱼龙舞。蛾儿雪柳黄金缕。笑语盈盈暗香去。当前是第3次,取数据------------------------众里寻他千百度。蓦然回首,那人却在,灯火阑珊处。当前是第4次,取数据------------------------没有打印出剩下次数的  当前是第X次取数据 这个测试内容的原因:因为:当缓存中没有数据的时候,程序就被阻塞了,不会再执行下面的代码而当前次咨询就没有办法结束解决 无法跳出阻塞 的方法:1、测试着玩儿,不能用于实际应用中指定 取出数据的大小,一次性将所有 缓存接收到的所有数据 取出来这种方式显然有很大缺陷:如果要进行传送的文件很大,而缓存的大小是有限制的,所以此种方法只适用于自己测试着玩儿,不能实际应用2、只能是通过 报头,首先指定报头的大小,取出反馈数据中的报头,然后根据报头中指出的,真正数据的大小,把 current_size < total_size  作为判断条件,从而实现  [将本次接收过来的所有数据,精准的取出]流式协议的特点客户端发送了一个空  可以正常发出去服务端接收不到空,因为对服务端而言,收到一个 空 和没收到任何数据没有差别可以在客户端  进行过滤,如果是空 则重新输入服务端只有收到东西之后  才能 作出响应{由于需要减少取数据的次数,从而减少I/O操作引起的延时所以收到的数据,都按照先来后到的顺序放在缓存中,但是,取数据时 无法知晓 到底应该取多少长度的数据}引入 货车取货送货 辅佐理解 [客户端和服务端的 发数据 和 从缓存取数据]: 须知: 固定班货车:满和不满 都发车recv(1024):这里的1024就是货车一个车厢的最大装载量这里的1024,指的是 存放 收到数据 的缓存大小为1024字节发数据 :相当于 往货车上装货取数据 :相当于 从货车上取货如果发送的内容>1024 ,例如为 1025取出1024个后还有1个在自己的缓存中1、装货时:在装车的时间内,几次发送的内容都装在一辆货车上连续两次快速send发送内容 达到了 拼接字符串 的运送效果, 并且不会引起一个完整的数据分成两次发送(而且拼接字符串也存在效率问题)装不下的部分,放到下一班货车上2、取货时:只有有货时,才取出货物而且只能按照 到货的先后顺序 取货要喝新水时,老水没喝干净,就继续喝缓存中的老水流式采用的算法是Nagele,Nagle 的目标是减少网络I/0前一辆货车上的货,都取完时,才能去除下一辆车上的货粘包问题: 一次未能取出本次的所有数据,导致 两次操作的数据 粘在 一起:默认每次取数据的时候,只取出一个车厢的全部数据 即1024个字节,如果传过来的数据太多,多余的数据会留在后面的车厢里,待下一次操作时,取出来的首先是上一次操作的数据引入报头的必要性: (解决粘包问题),一次取出本次的所有数据原始版 报头: (报头中内容是 序列化的数字)因此发送方发数据时,必须要告诉接收方,本次发送的数据的总长度用 从头开始的固定长度字节(这个长度 收发双方必须事先约定好) 告知 接收方 真正的数据的长度,从而实现接收所有发过来的数据这个 从头开始的固定长度字节,就叫 报头加强版 报头: (报头中的内容是 序列化的字典)通过,事先约定 从头开始的固定长度字节 表示报头的长度,报头中放的是 pickle序列化的字典,在字典中指明,后面真正的数据的长度len方法:len(bytes型数据): 得到的是字节数len(python数据类型): 得到的是字符的个数bytes类型应用点:在文件中存储的只能是bytes类型,python的数据类型如果想要存储在文件中,首先要进行编码,转换成对应编码规则的bytes类型两个主机之间进行通信的数据格式 也 必须是bytes类型重要示例:# 注意这里面total_size表示的数值是一个天文数字,地球上任何一个文件大小都打不到,然而存储这个字典的大小也才400个byte,所以这个字典可以表示任何一个文件header_dic = {"total_size": 12322222222222222222222222222222221111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222,"file_name": 'timg',"md5": "982345793457894573249579784534899"}# 1、# 通过pickle方式, 得到的header_file文件大小仅为 300 Byte,更小!with open('header_file', 'wb') as f:# pickle得到的直接就是Bytes类型,所以直接进行存储就行 f.write(pickle.dumps(header_dic))f.flush()# 2、# 通过json方式, 得到的header文件大小也不过是 500 Bytewith open('header_file','wb') as f:# 由于json得到的是字符串,所以欲存储,需先进行编码转成Bytes类型f.write(json.dumps(header_dic).encode('utf-8'))f.flush()pickle模块常用的方法有:dumps、loads、dump、load带s1、pickle.dumps(obj) — 把 obj 对象序列化后以 bytes 对象返回,不写入文件2、pickle.loads(bytes_object) — 从 bytes 对象中读取一个反序列化对象,并返回其重组后的对象不带s, 涉及文件操作3、pickle.dump( python_obj , f ) — 序列化对象,并将结果数据流写入到文件对象中4、python_data = pickle.load(f) — 反序列化对象,将文件中的数据解析为一个Python对象强调: json同理,只不过json操作的对象是 json格式的字符串 json的 dumps 和 loads:json.dumps()  将python数据类型       ---转换为--->  json格式的str字符串json.loads()  将json格式的str字符串  ---反解成--->  python数据类型pickle的 dumps 和 loads:dumps(python数据类型的数据)将python数据类型     ---转换成--->  序列化的bytes类型loads(bytes类型的数据)  将序列化的bytes类型  ---反解成--->  python的数据类型struct模块的 unpack方法 和 pack方法    # 数据 ①序列化的Bytes类型的整数  /  ②python中int类型的整数1、pack方法: 发数据时用到,      将python的int         ---转换为---> 为固定字节数的bytes类型struct.pack(模式, int型整数)        # 将int类型的整数         ---序列化成-->  bytes类型2、unpack: 收数据时用到,        将固定个数的bytes类型 ---转换为---> python的intstruct.unpack(模式, 序列化的bytes)  # 将序列化的bytes类型的数  ---转换称---->  int类型的整数例1:struct.pack('i',header_size) 例2:total_size = struct.unpack('i',header)[0]header: 其就是一个序列化的bytes类型的整数(这个整数表示的就是 真正的数据的长度)total_size: 就是真正数据的长度(int类型)参数'i': 指定 数据的类型 ,从而限定长度的范围[0] :表示就是报头的长度得到的数据  直接是bytes类型引入 加强版报头 收发数据的过程:1.发1、先发报头的长度  2、再发报头报头中内容: 文件长度、文件名字、   3、再发 真正的数据2.收 (按照发的顺序收)1、先发报头的长度  2、再发报头3、再发 真正的数据传送的数据内容分两种:1、命令(字符串):C、S收/发的是命令的长度2、文件:   get(下载)   文件在服务端上的路径服务端以读的方式,发送文件input(上传) 文件在客户端上的路径客户端以写的方式,写入文件os.isfile: 判断是否是文件,如果是文件夹 则为False传输文件时,通过不关闭文件的情况下,可以直接在后面进行 追加(关闭,则指针会到起始位置,进行的操作操作是 覆写)使用subprocess时, 不要忘记把管道中的内容read一下读出来obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)out = obj.stdout.read()err = obj.stderr.read()报头的必须注意点:不管是使用  ①简单的报头(报头内容: int型整数, 记录真正信息的长度大小)  or  ②复杂的报头(报头内容: dict型字典, 记录文件名、文件长度大小、md5值等信息)收发双方(客户端 与 服务端)都必须约定 一个统一的 报头的长度大小,双方都必须要遵守,因此便有了协议用于规范收发双方, 如果没有协议规范收发双方, 粘包问题就无法根治收发时注意:1、在一段内容(字符串)的传输中,发送方报头中记录的长度 应该是 这段内容序列化后的 byte的个数收取方收数据时, 判断 是否收完要接收的所有数据  的条件也应该是:  当前接收的byte的个数 < 真正内容的byte的个数2、在文件传输中,同理======================================> 代码 <===========================================
————————————————————————上节课代码————————————————————————客户端:from socket import *client = socket(AF_INET, SOCK_STREAM)# print(client)client.connect(('127.0.0.1', 8080))while True:cmd = input(">>: ").strip()if len(cmd) == 0:continueclient.send(cmd.encode('utf-8'))print('=======>')data = client.recv(1024)print('111111')print(data.decode('utf-8'))client.close()服务端:from socket import *# AF_INET: 指定本套接字是基于网络的套接字,# SOCK_STREAM:server = socket(AF_INET, SOCK_STREAM)# print(server),打印sever可以看到这个本应该是地址,不过此处是python优化后的结果# 127.0.0.1 本地换回地址:只有本机自己能访问;隔离了外部的网络环境,多用于测试server.bind(('127.0.0.1', 8080))server.listen(5)# 从半连接池中取出请求,建立连接(服务端 到 客户端 的桥)while True:conn, client_addr = server.accept()print(conn)print(client_addr)while True:try:data = conn.recv(1024)conn.send(data.upper())except Exception:breakconn.close()server.close()———————————————————————— 简单的报头(报头内容: 只是单纯的长度大小)  --解决远程执行命令程序中的 粘包问题————————————————————————客户端:import structfrom socket import *client = socket(AF_INET, SOCK_STREAM)# print(client)client.connect(('127.0.0.1', 8888))while True:cmd = input('请输入指令:').strip()if len(cmd) == 0:continueclient.send(pickle.dumps(cmd))'''报头的长度为4,其记录真正数据的长度所以,要先取出报头''''''最严谨时,还要考虑下列情况:在网络不顺畅的  情况下  表示 报头的部分 可能会在两次的货车中此时  要先把报头的长度的字节 收集完得到长度后,再收报头指定长度的实际数据网络通畅时,则不会发生报头在两个货车的情况'''# 先收数据的长度严谨的 获取简单报头的写法n = 0header = b''while n < 4:data = client.recv(1)header += datan += len(data)'''# 一般的(不严谨) 获取简单报头的写法header=client.recv(4)total_size = struct.unpack('i', header)[0]# 收真正的数据recv_size = 0recv_data = b''while recv_size < total_size:part_data = client.recv(1024)recv_data += datarecv_size += len(part_data)print(res.decode('gbk'))client.close()服务端:import subprocessimport structfrom socket import *server = socket(AF_INET, SOCK_STREAM)# print(server)server.bind(('127.0.0.1', 8888))server.listen(5)while True:conn, client_addr = server.accept()print(conn)print('当前客户端地址:',client_addr)while True:try:cmd = conn.recv(1024)obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)# 要read()一下,把管道中的内容读出来# 这个是 正确结果的输出内容stdout = obj.stdout.read()# 这个是 错误结果的输出内容stderr = obj.stdout.read()total_size = len(stdout) + len(stderr)# 先发送数据的长度conn.send(struct.pack('i',total_size))# 再发送真正的数据conn.send(stdout)conn.send(stderr)except Exception:breakconn.close()server.close()————————————————————————制作简单的报头————————————————————————import structfrom socket import *client = socket(AF_INET, SOCK_STREAM)# print(client)client.connect(('127.0.0.1', 8082))while True:cmd = input(">>: ").strip()  # get 文件路径if len(cmd) == 0:continueclient.send(cmd.encode('utf-8'))# 先收数据的长度n = 0header = b''while n < 8:data = client.recv(1)header += datan += len(data)total_size = struct.unpack('q', header)[0]print(total_size)# 再收真正的数据recv_size = 0with open('aaa.jpg', mode='wb') as f:while recv_size < total_size:part_data = client.recv(1024)f.write(data)recv_size += len(part_data)client.close()服务端:import subprocessimport osimport structfrom socket import *server = socket(AF_INET, SOCK_STREAM)server.bind(('127.0.0.1', 8082))server.listen(5)while True:conn, client_addr = server.accept()print(conn)print(client_addr)while True:try:msg = conn.recv(1024).decode('utf-8')cmd,file_path=msg.split()if cmd == "get":# 先发送报头total_size=os.path.getsize(file_path)conn.send(struct.pack('q',total_size))# 再发送文件with open(f'{file_path}}', mode='rb') as f:for line in f:conn.send(line)except Exception:breakconn.close()server.close()————————————————————————定制复杂的报头(通过字典) --实现文件的上传下载 ————————————————————————单独拎出来的 报头:header_dic = {'file_name': "嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤嘤",'md5': "12322222222222222222222222222222221111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222",'total_size': 12322222222222222222222222222222221111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222}pickle_size = len( pickle.dumps(header_dic) )json_size = len( json.dumps(header_dic) )print('pickle_size:', pickle_size)  # 800 Byteprint('json_size:', json_size)  # 1200 Byte# 可以看到, 平均1kb便可存储 很多的文件信息客户端:import structimport jsonfrom socket import *client = socket(AF_INET, SOCK_STREAM)# print(client)client.connect(('127.0.0.1', 8082))while True:cmd = input(">>: ").strip()  # get 文件路径if len(cmd) == 0:continueclient.send(cmd.encode('utf-8'))# 1、先接收报头的长度res=client.recv(4)header_size=struct.unpack('i',res)[0]# 2、再接收报头header_json_bytes=client.recv(header_size)header_json=header_json_bytes.decode('utf-8')header_dic=json.loads(header_json)print(header_dic)# 3、最后接收真实的数据total_size=header_dic['total_size']filename=header_dic['file_name']recv_size = 0with open(r"D:\python全栈15期\day32\代码\03 定制复杂的报头\版本2\download\%s" %filename, mode='wb') as f:while recv_size < total_size:part_data = client.recv(1024)f.write(part_data)recv_size += len(data)client.close()服务端:import subprocessimport osimport structimport jsonfrom socket import *server = socket(AF_INET, SOCK_STREAM)server.bind(('127.0.0.1', 8888))server.listen(5)while True:conn, client_addr = server.accept()print(client_addr)while True:try:msg = conn.recv(1024).decode('utf-8')# 客户端发送的信息 格式为: get/input 文件路径cmd, file_path = msg.split() # file_path 为服务端上的文件路径if cmd == "get":# 一、制作报头# 1、编辑 报头字典中的内容header_dic = {"total_size": os.path.getsize(file_path), # 12322222222222222222222222222222221111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"file_name": 'timg',"md5": "12312222222222222222222222222222222222222222222222222222231231232132131232311"}# 2、将报头序列化得到 序列化的报头# 转换成 json格式的字符串header_json = json.dumps(header_dic)  # 将json格式的字符串 编码成对应编码 的bytes类型header_json_bytes = header_json.encode('utf-8') # 二、发送数据#  由于当前的 报头 是变长的,长度不固定# 1、所以先发送 序列化的 报头的长度# 上面的这个字典的大小也才 400+ 个字节,所以'i'模式 足够容纳下它了header_size = len(header_json_bytes)  # 447conn.send(struct.pack('i', header_size))# 2、再发送 序列化的报头conn.send(header_json_bytes)# 3、最后发送文件中的 真实的数据with open(r'%s' % file_path, mode='rb') as f:for line in f:conn.send(line)except Exception:breakconn.close()server.close()

8-14-粘包问题、(通过简单报头)解决粘包问题、定制复杂报头相关推荐

  1. 使用包定长FixedLengthFrameDecoder解决半包粘包

    四.使用包定长FixedLengthFrameDecoder解决半包粘包 4.1 试验 由于客户端发给服务器端的是hello server,im a client字符串,该字符串占用24字节,所以在服 ...

  2. VMOS+小黄鸟无root抓包(解决抓包无网络问题)(附工具)

    前言: 最近在搞手机安卓抓包,看了网上很多教程都没有解决抓包无网络问题.于是费了一上午时间找遍全网终于解决了,在此分享给大家参考. 准备工具(文末附工具): 1. vmos pro破解版 2. 小黄鸟 ...

  3. 学习笔记(12):Python网络编程并发编程-解决粘包问题-简单版本

    立即学习:https://edu.csdn.net/course/play/24458/296243?utm_source=blogtoedu 粘包现象的解决:简单版 1.思路:       在服务器 ...

  4. 【Python】网络编程--解决粘包问题--简单版:

    网络编程–解决粘包问题–简单版: 客户端: import struct import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STRE ...

  5. 10.python网络编程(解决粘包问题 part 2)

    一.什么时候会产生粘包现象. 只有在使用tcp协议的情况下才会产生粘包现象!udp协议永远不会! 发送端可以1k1k的把数据发送出去,接收端,可以2k2k的的去接收数据,一次可能会接收3k,也有可能1 ...

  6. Socket 套接字和解决粘包问题

    ---恢复内容开始--- Socket 套接字: 什么是socket: Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实 就是一个门面模式,它 ...

  7. 并发编程知识总结,软件开发架构,socket套接字模板,粘包问题,struct解决粘包问题,上传大文件数据,socketserver,关于操作系统的发展史,进程,线程。...

    并发编程知识总结 软件开发架构 C/S: client:客户端 server:服务端 优点:占用网络资源少,软件的使用稳定 缺点:服务端更新后,客户端也要更新,需要使用多个软件,需要下载多个客户端 B ...

  8. 【Netty】Netty解决粘包和拆包问题的四种方案

    在RPC框架中,粘包和拆包问题是必须解决一个问题,因为RPC框架中,各个微服务相互之间都是维系了一个TCP长连接,比如dubbo就是一个全双工的长连接.由于微服务往对方发送信息的时候,所有的请求都是使 ...

  9. Netty 解决粘包和拆包问题的四种方案

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://my.oschina.net/ ...

最新文章

  1. Python+OpenCV实现AI人脸识别身份认证系统(2)—人脸数据采集、存储
  2. 输入两个数,计算它们的最大公约数和最小公倍数
  3. TM4C123核心板焊接须知
  4. 技术实践丨列存表并发更新时的锁等待问题原理
  5. python怎么查看安装了哪些库_如何查看Python 安装位置以及已经安装的库
  6. UcOS-II 和linux比较
  7. css3径向渐变详解-遁地龙卷风
  8. H5中启动Android app
  9. 微信小程序轮播图常用炫酷样式----swiper
  10. 中国银联Apple Pay 支付集成
  11. PIE-Engine 教程:水稻面积提取1(宿迁市)
  12. cad抛物线曲线lisp_CAD如何画抛物线啊?
  13. ChinaVis2019中国可视化与可视分析大会成都站(7.21-7.24)
  14. python编写程序计算三角形的面积_编程题:编写程序输入三角形的3条边长,计算并输出三角形的面积。...
  15. web开发常见的几大安全问题
  16. Tomcat报ClassFormatException: Invalid byte tag in constant pool: 19解决方法
  17. 计算机搜索不到PDF,电脑找不到microsoft print to pdf打印机怎么办?
  18. Java面试题及答案(阿里京东美团滴滴)
  19. 华为交换机 tagged 与 untagged 的关系
  20. 6-5 普通账户和支票账户 (10 分)

热门文章

  1. 2022(秋)工程伦理答案 第一章
  2. mysql 瘦身_Mysql瘦身方法
  3. 鲍鱼数据案例(岭回归 、LASSO回归)
  4. servlet修改用户头像_Java上传文件实现更换头像
  5. 打造店铺爆款的玩法方式解析
  6. kubernetes健康检查配置解析
  7. 手把手看如何制作本地yun源
  8. 上财计算机专业全国排名,2021软科财经类大学排名,上海财经遥遥领先,东财仅排第六...
  9. python语言编程-Python成为2018年度编程语言,遥遥领先于其他语言
  10. pandas 玩转 Excel 操作总结