博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python网络编程03/ low版解决粘包问题
阅读量:5263 次
发布时间:2019-06-14

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

目录

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

1.操作系统的缓存区

1.为什么存在缓冲区    1. 暂时存储一些数据.    2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.缺点: 造成了粘包现象之一.

1730001-20190816221025627-413983461.png

1730001-20190816221034882-1042328668.png

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模块

1730001-20190816221229315-1240358978.png

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()

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

你可能感兴趣的文章
10个著名的思想实验1
查看>>
composer 报 zlib_decode(): data error
查看>>
linux下WPS的使用
查看>>
Web Api 利用 cors 实现跨域
查看>>
hdu 3938 并查集
查看>>
instanceof
查看>>
《深入分析Java Web技术内幕》读书笔记之JVM内存管理
查看>>
python之GIL release (I/O open(file) socket time.sleep)
查看>>
2015/8/4 告别飞思卡尔,抛下包袱上路
查看>>
软件开发与模型
查看>>
161017、SQL必备知识点
查看>>
kill新号专题
查看>>
MVC学习系列——Model验证扩展
查看>>
C# GC 垃圾回收机制
查看>>
mysqladmin 修改和 初始化密码
查看>>
字符串
查看>>
vue2.x directive - 限制input只能输入正整数
查看>>
实现MyLinkedList类深入理解LinkedList
查看>>
自定义返回模型
查看>>
C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 客户端多网络支持
查看>>