博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python网络编程04/recv原理/高大上版解决粘包方式
阅读量:5262 次
发布时间:2019-06-14

本文共 7478 字,大约阅读时间需要 24 分钟。

目录

Python网络编程04/recv原理/高大上版解决粘包方式

1.昨日内容回顾

1. 通信循环2. 链接通信循环3. 远程执行命令: subprocess.Popen()#   bytes: 网络传输, 文件存储时.4. 粘包现象   1. 对方发来的数据大于自己recv的上线,下一次在recv会读取剩余的数据.   2. 连续多次send数据量较小的数据,这些数据会粘在一起发送出去.5. 缓冲区: 输入缓冲区,输出缓冲区. 存储少量数据,避免网络不稳,造成你传输数据时的卡顿,保持相对平稳,稳定.#6. 收发的本质:    不一定是一收一发.7. 如何解决粘包?   low版: 制作一个固定的报头.   获取总数据的长度. 7878   利用struct模块将int 7878 ---> ret = 4个字节   send(ret)   send(总数据)   客户端:   head_bytes = recv(4)   head = struct.unpack('i',head_bytes)[0]   7878   利用while循环判断:    循环recv.

2.recv工作原理

源码解释:Receive up to buffersize bytes from the socket.接收来自socket缓冲区的字节数据,For the optional flags argument, see the Unix manual.对于这些设置的参数,可以查看Unix手册。When no data is available, block untilat least one byte is available or until the remote end is closed.当缓冲区没有数据可取时,recv会一直处于阻塞状态,直到缓冲区至少有一个字节数据可取,或者远程端关闭。When the remote end is closed and all data is read, return the empty string.关闭远程端并读取所有数据后,返回空字符串。
1,验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。(服务端)# import socket## phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)## phone.bind(('127.0.0.1',8080))## phone.listen(5)## conn, client_addr = phone.accept()# from_client_data1 = conn.recv(2)# print(from_client_data1)# from_client_data2 = conn.recv(2)# print(from_client_data2)# from_client_data3 = conn.recv(1)# print(from_client_data3)# conn.close()# phone.close()验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。(客户端)# import socket# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# phone.connect(('127.0.0.1',8080))# phone.send('hello'.encode('utf-8'))# phone.close()2,验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态。(服务端)## import socket## phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)## phone.bind(('127.0.0.1',8080))## phone.listen(5)## conn, client_addr = phone.accept()# from_client_data = conn.recv(1024)# print(from_client_data)# print(111)# conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭。# print(222)## conn.close()# phone.close()验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态(客户端)# import socket# import time# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# phone.connect(('127.0.0.1',8080))# phone.send('hello'.encode('utf-8'))# time.sleep(20)## phone.close()#3 验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。## import socket## phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)## phone.bind(('127.0.0.1',8080))## phone.listen(5)## conn, client_addr = phone.accept()# from_client_data1 = conn.recv(1024)# print(from_client_data1)# from_client_data2 = conn.recv(1024)# print(from_client_data2)# from_client_data3 = conn.recv(1024)# print(from_client_data3)# conn.close()# phone.close()验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。# import socket# import time# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# phone.connect(('127.0.0.1',8080))# phone.send('hello'.encode('utf-8'))# phone.close()recv空字符串: 对方客户端关闭了,且服务端的缓冲区没有数据了,我再recv取到空bytes.

3.高大上版解决粘包方式(自定制报头)

3.1 解决思路:

我们要制作固定的报头你现在有两段不固定长度的bytes类型,我们要固定的报头,所以    1. 你获取不固定报头的长度    2. 利用struct模块将不固定的长度转化成固定的字节数 4个字节    3. 先发4个字节,在发报头数据,在发总数据

1730001-20190816221530210-1335463773.png

3.2 服务端

#  FTP 应用层自定义协议# '''# 1. 高大上版: 自定制报头# dic = {'filename': XX, 'md5': 654654676576776, 'total_size': 26743}# 2. 高大上版:可以解决文件过大的问题.# # # '''# # import struct# # # ret = struct.pack('Q',21321432423544354365563543543543)# # print(ret)# # import socket# import subprocess# import struct# import json# phone = socket.socket()# # phone.bind(('127.0.0.1',8848))# # phone.listen(2)# # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错# # while 1:#     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中#     # print(f'链接来了: {conn,addr}')# #     while 1:#         try:# #             from_client_data = conn.recv(1024)  # 接收命令# # #             if from_client_data.upper() == b'Q':#                 print('客户端正常退出聊天了')#                 break# #             obj = subprocess.Popen(from_client_data.decode('utf-8'),#                                    shell=True,#                                    stdout=subprocess.PIPE,#                                    stderr=subprocess.PIPE,# #                                    )#             result = obj.stdout.read() + obj.stderr.read()#             total_size = len(result)# #             # 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)# #         except ConnectionResetError:#             print('客户端链接中断了')#             break#     conn.close()# phone.close()# # # # import json# import struct# dic = {'filename': 'test1',#        'md5': 654654676576776,#        'total_size': 1024*1024*1024*1024*1024*1024*1024}# # dic_json_bytes = json.dumps(dic).encode('utf-8')# # print(dic_json_bytes)# len_dic_json_bytes = len(dic_json_bytes)# print(len_dic_json_bytes)# print(struct.pack('i',len_dic_json_bytes))# print(struct.pack('Q',1024*1024*1024*1024*1024*1024*1024)

3.3客户端

# import socket# import struct# import json# phone = socket.socket()# # phone.connect(('127.0.0.1',8848))# while 1:#     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')#     if not to_server_data:#         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送#         print('发送内容不能为空')#         continue#     phone.send(to_server_data)#     if to_server_data.upper() == b'Q':#         break# #     # 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)#     '''#     head_dic = {#                 'file_name': 'test1',#                 'md5': 6567657678678,#                 'total_size': total_size,# #             }#     '''#     total_data = b''#     while len(total_data) < head_dic['total_size']:#         total_data += phone.recv(1024)# #     # print(len(total_data))#     print(total_data.decode('gbk'))# # phone.close()

4.基于UDP协议的socket通信

4.1服务端

import socketserver = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 基于网络的UDP协议的socketserver.bind(('192.168.14.198',9000))while 1:    from_client_data = server.recvfrom(1024)  # 阻塞,等待客户来消息    print(f'\033[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} \033[0m')    # to_client_data = input('>>>').strip()    # server.sendto(to_client_data.encode('utf-8'),from_client_data[1])# 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.# 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.# 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.

4.2客户端

import socketclient = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 基于网络的UDP协议的socketwhile 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")}')

转载于:https://www.cnblogs.com/liubing8/p/11366648.html

你可能感兴趣的文章
[Leetcode] maximun subarray 最大子数组
查看>>
URL参数获取/转码
查看>>
【通过反射去获取有参构造方法并使用】
查看>>
网站分析师如何让分析报告更具价值
查看>>
蛋疼的ACM
查看>>
Java 之 static的使用方法(6)
查看>>
Effective c++ 小结
查看>>
手把手教你开发Chrome扩展二:为html添加行为
查看>>
30岁菜鸟涛学习VB.net 第四天
查看>>
2017.4.10-morning
查看>>
php7.0支持调用lua脚本
查看>>
Fiddler可以支持Websocket抓包了
查看>>
判断 切换
查看>>
ocrosoft Contest1316 - 信奥编程之路~~~~~第三关 问题 C: 挂盐水
查看>>
ACM_并查集
查看>>
Centos7.x破解密码
查看>>
Beta答辩总结
查看>>
MATLAB学习笔记10-10-24
查看>>
个人冲刺1
查看>>
A Tour of C++(Chapter 2 of The C++ Programming Language)
查看>>