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展示收发问题的服务端(server)
      • 5.4 展示收发问题的客户端(client)
    • 6.如何解决粘包现象
    • 5.low版解决粘包现象
      • 5.1服务端
      • 5.2客户端(client)

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_size
data = b''
while len(data) < total_size:
    data = data + phone.recv(1024)
你要将total_size int类型转化成bytes类型才可以发送

387    ---- > str(387)  '387'  ---->bytes   b'387'        长度   3bytes

4185  ---->  str(4185)  '4185'  ---->bytes   b'4185'  长度   4bytes

18000------------------------------------------------------>  长度   5bytes

我们要解决:
将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。
struct模块

5.low版解决粘包现象

5.1服务端

# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
import socket
import subprocess
import struct
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)
            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 socket
import struct
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. 接收报头
    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

时间: 2024-10-12 03:38:36

Python网络编程03/ low版解决粘包问题的相关文章

python 网络编程(远程执行命令与粘包)

远程执行命令 先来学习一个新模块 , 一会用到的.. 新模块: subprocess 执行系统命令 r = subprocess.Popen('ls',shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.Popen(a,b,c,d) a: 要执行的系统命令(str) b: shell = True 表示确定我当前执行的命令为系统命令 c: 表示正确信息的输出管道 d: 表示错误信息的输出管道 下边直接上代码,

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. 粘包现象

python 网络编程第三版

为服务端增加多线程解决方案 1.服务端代码如下: ***这个版本并没有真正的起到多线程的作用,主要原因在于t.join():以后的版本会改进这个问题*** #!/usr/bin/python #!coding:utf-8 import os,sys,time from socket import * import threading def handleClient(conn): print '[info] handleClient is :{0}'.format(os.getpid()) wh

Python网络编程_TCP(简略版)

TCP监听套接字: 当新的客户端请求连接时,服务器端监听套接字收到消息,会分配一个新的套接字对应于客户端,用于接收客户端的消息, 但是当服务器端要回传消息时,使用的仍是监听套接字 1 #tcp客户端 2 import socket 3 4 def cilent(): 5 #1.创建TCP套接字 6 # socket.AF_INET:IPV4 socket.SOCK_STREAM:TCP 7 socket_tcp_cilent=socket.socket(socket.AF_INET,socket

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

一.什么时候会产生粘包现象. 只有在使用tcp协议的情况下才会产生粘包现象!udp协议永远不会! 发送端可以1k1k的把数据发送出去,接收端,可以2k2k的的去接收数据,一次可能会接收3k,也有可能1次接收6k. TCP协议是面向流的协议,这也是容易出现粘包问题的原因.而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的.怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方

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

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

Python网络编程—socket(一)

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

Python 网络编程(二)

Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端 服务端 1 2 3 4 5 6 7 8