目录
Python网络编程03/ low版解决粘包问题
1.操作系统的缓存区
1.为什么存在缓冲区 1. 暂时存储一些数据. 2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.缺点: 造成了粘包现象之一.
2.基于TCP协议的socket循环通信
2.1 服务端(server)
# import socket# # phone = socket.socket()# # phone.bind(('127.0.0.1',8848))# # phone.listen()# listen: 允许5个人链接我,剩下的链接也可以链接,等待.# # conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中# print(f'链接来了: {conn,addr}')# # while 1:# try:# from_client_data = conn.recv(1024) # 最多接受1024字节# # if from_client_data.upper() == b'Q':# print('客户端正常退出聊天了')# break# # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')# to_client_data = input('>>>').strip().encode('utf-8')# conn.send(to_client_data)# except ConnectionResetError:# print('客户端链接中断了')# break# conn.close()# phone.close()
2.2客户端(client)
# import socket# # 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# from_server_data = phone.recv(1024) # 最多接受1024字节# print(f'来自服务端消息:{from_server_data.decode("utf-8")}')# # phone.close()# # # s1 = 'q'# # s2 = b'q'# # s3 = '中国'# # print(s3.encode('utf-8'))# # print(type(s1),type(s2))# # # s1 = 'q'# # print(s1.encode('utf-8'))# # # bytes类型:# # ASCII字符: 在字符串前面b''# # 非ASCII字符: encode 转化成 bytes类型
3.基于TCP协议的socket链接+循环 通信
3.1服务端(server)
# import socket# # 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) # 最多接受1024字节# # if from_client_data.upper() == b'Q':# print('客户端正常退出聊天了')# break# # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')# to_client_data = input('>>>').strip().encode('utf-8')# conn.send(to_client_data)# except ConnectionResetError:# print('客户端链接中断了')# break# conn.close()# phone.close()
3.2 客户端(client)
# import socket# # 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# from_server_data = phone.recv(1024) # 最多接受1024字节# print(f'来自服务端消息:{from_server_data.decode("utf-8")}')# # phone.close()
4.基于TCP协议的socket应用实例:执行远程命令
4.1服务端(server)
# import socket# import subprocess# 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) # 最多接受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()# # conn.send(result)# except ConnectionResetError:# print('客户端链接中断了')# break# conn.close()# phone.close()# # # # # # shell: 命令解释器,相当于调用cmd 执行指定的命令。# # stdout:正确结果丢到管道中。# # stderr:错了丢到另一个管道中。# # windows操作系统的默认编码是gbk编码。#
4.2客户端(client)
# import socket# # 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# from_server_data = phone.recv(1024) # 最多接受1024字节# print(f'{from_server_data.decode("gbk")}')# # phone.close()
5.粘包现象
5.1服务端(server)
# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。# import socket# import subprocess# 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) # 最多接受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()# print(f'总字节数:{len(result)}')# conn.send(result)# except ConnectionResetError:# print('客户端链接中断了')# break# conn.close()# phone.close()# s1 = '太白jx'# # print(len(s1))# b1 = s1.encode('utf-8')# # print(b1)# print(len(b1))''' 客户端 服务端第一次: ipconfig 317字节 300个字节 17个字节 客户端 服务端第二次: dir 376字节 17字节 376字节 '''# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.# import socket## phone = socket.socket()## phone.bind(('127.0.0.1',8848))## phone.listen(5)### conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中## from_client_data = conn.recv(1024) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')# conn.close()# phone.close()# 展示一些收发的问题。
5.2客户端(client)
# import socket## 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# from_server_data = phone.recv(300) # 最多接受1024字节# # print(f'{from_server_data.decode("gbk")}')# print(len(from_server_data))## phone.close()# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.# import socket## phone = socket.socket()## phone.connect(('127.0.0.1',8848))### phone.send(b'he')# phone.send(b'll')# phone.send(b'o')### phone.close()# Nigle算法
5.3展示收发问题的服务端(server)
# 发多次收一次# import socket## phone = socket.socket()## phone.bind(('127.0.0.1',8848))## phone.listen(5)### conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中## from_client_data = conn.recv(1024) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')## from_client_data = conn.recv(1024) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')# conn.close()# phone.close()# 发一次收多次# import socket## phone = socket.socket()## phone.bind(('127.0.0.1',8848))## phone.listen(5)### conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中## from_client_data = conn.recv(3) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')## from_client_data = conn.recv(3) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')## from_client_data = conn.recv(3) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')## from_client_data = conn.recv(3) # 最多接受1024字节# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')## conn.close()# phone.close()
5.4 展示收发问题的客户端(client)
# 发多次收一次# import socket## phone = socket.socket()## phone.connect(('127.0.0.1',8848))### phone.send(b'he')# phone.send(b'llo')### phone.close()# Nigle算法# 发一次收多次# import socket## phone = socket.socket()## phone.connect(('127.0.0.1',8848))### phone.send(b'hello world')### phone.close()
6.如何解决粘包现象
解决粘包现象的思路:服务端发一次数据 10000字节, 客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕.将接收的数据拼接在一起,最后解码.1. 遇到的问题: recv的次数无法确定. 你发送总具体数据之前,先给我发一个总数据的长度:5000个字节。然后在发送总数据。 客户端: 先接收一个长度。 5000个字节。 然后我再循环recv 控制循环的条件就是只要你接受的数据< 5000 一直接收。2. 遇到的问题: 总数据的长度转化成的字节数不固定
服务端:conn.send(total_size) conn.send(result)total_size int类型客户端:total_size_bytes = phone.recv(4)total_sizedata = b''while len(data) < total_size: data = data + phone.recv(1024)
你要将total_size int类型转化成bytes类型才可以发送387 ---- > str(387) '387' ---->bytes b'387' 长度 3bytes4185 ----> str(4185) '4185' ---->bytes b'4185' 长度 4bytes18000------------------------------------------------------> 长度 5bytes我们要解决: 将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。
struct模块
5.low版解决粘包现象
5.1服务端
# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。import socketimport subprocessimport structphone = 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) print(f'总字节数:{total_size}') # 1. 制作固定长度的报头 head_bytes = struct.pack('i',total_size) # 2. 发送固定长度的报头 conn.send(head_bytes) # 3. 发送总数据 conn.send(result) except ConnectionResetError: print('客户端链接中断了') break conn.close()phone.close()# import struct# # 将一个数字转化成等长度的bytes类型。# ret = struct.pack('i', 180000000)# # print(ret, type(ret), len(ret))## # 通过unpack反解回来# ret1 = struct.unpack('i',ret)[0]# # print(ret1)# print(ret1, type(ret1))# 总数据:总数据长度# s1 = 'lagfdkjglkhjklh'# b1 = s1.encode('utf-8')# print(b1)# print(len(b1))
5.2客户端(client)
import socketimport structphone = 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. 接收报头 head_bytes = phone.recv(4) # 2. 反解报头 total_size = struct.unpack('i',head_bytes)[0] total_data = b'' while len(total_data) < total_size: total_data += phone.recv(1024) print(len(total_data)) print(total_data.decode('gbk'))phone.close()