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

目录

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

    • 1.昨日内容回顾
    • 2.recv工作原理
    • 3.高大上版解决粘包方式(自定制报头)
      • 3.1 解决思路:
      • 3.2 服务端
      • 3.3客户端
    • 4.基于UDP协议的socket通信
      • 4.1服务端
      • 4.2客户端

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个字节,在发报头数据,在发总数据

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 socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 基于网络的UDP协议的socket
server.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 socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 基于网络的UDP协议的socket

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")}')

原文地址:https://www.cnblogs.com/liubing8/p/11366648.html

时间: 2024-08-02 22:11:55

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

Python网络编程03/ low版解决粘包问题

目录 Python网络编程03/ low版解决粘包问题 1.操作系统的缓存区 2.基于TCP协议的socket循环通信 2.1 服务端(server) 2.2客户端(client) 3.基于TCP协议的socket链接+循环 通信 3.1服务端(server) 3.2 客户端(client) 4.基于TCP协议的socket应用实例:执行远程命令 4.1服务端(server) 4.2客户端(client) 5.粘包现象 5.1服务端(server) 5.2客户端(client) 5.3展示收发问

第31篇 粘包的产生原理 以及如何解决粘包问题

内容回顾: 如何与另外一台电脑连接: ping 对方的ip地址 如何参电脑的ip配置 cmd-->ipconfig 内容概览: 粘包问题 粘包产生原理 如何解决粘包问题 粘包的产生: #server import socket sk = socket.socket() ip_port = ('127.0.0.1',8989) sk.bind(ip_port) sk.listen() conn,addr = sk.accept() conn.send(b'hello,') conn.send(b'

python 基于tcp协议的文件传输3_解决粘包问题

server import jsonimport structimport socket# 接收sk = socket.socket()sk.bind(('127.0.0.1',9001))sk.listen() conn,_ =sk.accept()msg_len = conn.recv(4)dic_len = struct.unpack('i',msg_len)[0]msg = conn.recv(dic_len).decode('utf-8')msg = json.loads(msg) w

PYTHON网络编程基础 pdf扫描版高清下载

PYTHON网络编程基础 pdf,本书全面介绍了使用PYTHON进行网络编程的基础知识,高级网络操作.WebServices.解析HTML和XHTML.XML.FTP.使用PYTHON操作数据库.SSL.几种服务器端框架,以及多任务处理等,实用性比较强,书中提供了175个实例,6600行以上的代码. 目录 第1部分 底层网络 第1章 客户/服务器网络介绍 第2章 网络客户端 第3章 网络服务器 第4章 域名系统 第5章 域名系统 第2部分 Web Service 第6章 Web客户端访问 第7章

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python网络编程—socket(一)

从今天开始python基础就介绍完毕了,下面我们将进阶到socket网络编程的介绍,那么socket是什么呢?我们带着这个问题开始今天的介绍: 一.socket初探 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket其实也是一种特殊的文件,一些socket函数就是对其进行的操作(读/写.打开.关闭) 那么socket对文件操作和file对文件操作有什么区别呢? fil

Python 网络编程——socket

一 客户端/服务器架构 客户端(Client)服务器(Server)架构,即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 理想/目标状态—— 最常用的软件服务器是 Web 服务器.一台机器里放一些网页或 Web 应用程序,然后启动 服务.这样的服务器的任务就是接受客户的请求,把网页发给客户(如用户计算机上的浏览器),然 后等待下一个客户请求.这些服务启动后的目标就是“永远运行下去”.虽然它们不可能实现这样的 目标,但只要没有关机或硬件出错等外力干扰,它们就能运

Python 网络编程

Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发. 什么是 Socket? Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. socket()函数 Pyt

Python 网络编程(一)

Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者. socket和file的区别: file模块是针对某个指定文件进行[打开][读写][关闭] socket模块是针对 服务器端 和 客户端Socket 进行[打开][读写][关闭] socket服务端和客户端的网