1.缓冲区
每个socket被创建以后,都会分配两个缓冲区,输入缓冲区和输出缓冲区,默认大小都是8k,可以通过getsocket()获取,暂时存放传输数据,防止程序在发送的时候卡阻,提高代码运行效率.
首先看python的系统交互subprocess:
import subprocess
 
sub_obj = subprocess.Popen(
    'ls',  #系统命令
    shell = True,  #固定格式
    stdout=subprocess.PIPE,  #标准输出  PIPE管道,保存着指令的执行结果
    stderr=subprocess.PIPE   #标准错误输出
)
 
print('正确输出',sub_obj.stdout.read().decode('gbk'))
print('错误输出',sub_obj.stderr.read().decode('gbk'))
 
 
#测试byte长度
# print(len(b'hello'))
# print(bytes(str(2),encoding='utf-8'))
结果编码是以但钱所在系统为准的,诺为windo,则用GBK解码,且只能从管道里读取一次结果
2.黏包现象
   1.tcp两种黏包现象:
   a. 发送端需要等缓冲区满了才发送出去,造成黏包(发送时间的间隔很短,数据也很小,会被底层优化算法河道一起,产生黏包现象)
server端的代码示例额如下:
from socket import *
ip_port = ('127.0.0.1',8080)
 
tcp_socket_server =socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen()
conn,addr = tcp_socket_server.accept()
#服务端连接接收两个信息
data1=conn.recv(10)
data2=conn.recv(10)
 
print('------>',data1.decode('utf-8'))
print('------>',data2.decode('utf-8'))
 
conn.close()
client端的实例如下:
import socket
 
BUFSIZE= 1024
ip_port = ('127.0.0.1',8080)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 
res = s.connect(ip_port)
 
s.send('hi'.encode('utf-8'))
s.send('meinv'.encode('utf-8'))
 b. 接受方没有及时接受缓冲区的包,导致多个包接受,(客户端发送一段数据,服务端只收了一小部分,服务区 下次接受的时候还是从缓冲区拿上次遗留的数据,产生黏包)第一次如果发送的数据大小2000B,接受端一次性接受大小为1024,这样就导致剩下的内容会被下一次recv接收到,导致结果的错乱.
server端的代码示例额如下:
import socket
import subprocess
 
 
server = socket.socket()
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn, addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
    print(from_client_cmd.decode('utf-8'))
 
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
 
    std_msg = sub_obj.stdout.read()
    print('指令的执行结果长度>>>>',len(std_msg))
 
    conn.send(std_msg)
client端的实例如下:
import socket
 
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input("请输入指令:")
 
    client.send(cmd.encode('utf-8'))
    server_cmd_result = client.recv(1024)
 
    print(server_cmd_result.decode('gbk'))
解决tcp黏包的方案有两种,第一种是了解一下,第二种是必须记住.
1.方案一:由于接受方不知道发送端将要传送的自接力的长度,导致接收的尾喉,可能接收不全,或者多接收另外一次发送的内容,把自己将要送的字节流总大小让对方知晓,然后接收方发一个确认消息给发送端,然后发送端在发送过来后面的真实数据,接收方在来呢接收完
server端的代码示例额如下:
import socket
import subprocess
 
server = socket.socket( )
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn,addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
 
    print(from_client_cmd.decode('utf-8'))#接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
 
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE, #正确结果的存放的位置
        stderr=subprocess.PIPE  #错误结果的存放的位置
    )
    # 从管道里面拿结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()
    #为了解决黏包现象,我们统计一下消息的长度,现将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
    std_msg_len = len(std_msg)
  #首先将数据长度的数据类型装换成bytes类型
    std_bytes_len = str(len(std_msg)).encode('utf-8')
    print('指令的执行结果长度>>>>>:',len(std_msg))
 
    conn.send(std_bytes_len)
    status = conn.recv(1024)
    if status.decode('utf-8') == 'ok':
 
        conn.send(std_msg)
    else:
        pass
client端的实例如下:
import socket
 
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input('请输入指令:')
 
    client.send(cmd.encode('utf-8'))
 
    server_res_len = client.recv(1024).decode('utf-8')
 
    print('来自服务端的消息长度',server_res_len)
 
    client.send(b'ok')
 
    server_cmd_result = client.recv(int(server_res_len))
 
    print(server_cmd_result.decode('gbk'))`
2.方案二: 通过struck模块将需要发送的内容长度进行打包,打包成一个4字节长度的数据发送到对端,对端只要取出前4个字节,然后对前4个字节的数据进行捷豹,拿到发送的长度,然后通过这个长度来继续接受我们要发送的内容.
struck模块的使用:struck模块中最重的两个函数就是pack()打包,unpack()解包
pack(): 我们这里只介绍'i'这个int类型,上面的途中列举了处理可以打包的所有的数据类型,并且struck处理pack和unpack两个方法之外还有好多别的方法和用法.
import struct
num = 100
# num太大的话会报错,
# struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
# 打包,将int类型的数据打包成4个长度的bytes类型的数据
byt = struct.pack('i',num)
print(byt)
# 解包,将bytes类型的数据,转换为对应的那个int类型的数据
# 注:unpack返回的是truple
int_num = struct.unpack('i',byt)[0]
print(int_num)
解决黏包现象的第二种方案:
服务端:
import socket
import subprocess
import struct
 
server = socket.socket()
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn,addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
    print(from_client_cmd.decode('utf-8'))
    #接收到客户端发送过来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE, #正确结果的存放位置
        stderr=subprocess.PIPE  #错误结果的存放位置
    )
    # 从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()
    #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实的数据
    std_msg_len = len(std_msg)
    print('指令的执行结果长度>>>>>>',len(std_msg))
 
    msg_lenint_struct =struct.pack('i',std_msg_len)
 
    conn.send(msg_lenint_struct+std_msg)
客户端:
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input('请输入指令:')
    #发送指令
    client.send(cmd.encode('utf-8'))
    #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度
    server_res_len=client.recv(4)
    msg_len= struct.unpack('i',server_res_len)[0]
 
    print('来自服务端的消息长度',msg_len)
    #通过解包出来的长度,来接收后面的真实数据
    server_cmd_result =client.recv(msg_len)
 
    print(server_cmd_result.decode('gbk'))
查看自己的缓存的大小
# print(server.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF))
# print(server.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF))
3. udp是面向包的,所以udp是不存在黏包的。
在udp代码中,我们在server端接受返回消息的时候,我们设置的recvfrom(1024),那么当我们输入的执行指令为'dir'的时候,若dir在当前文件夹下输出的内容大于1024,然后就报错了,
解释原因:因为udp是面向报文的,每个消息是一个包,接收端设置接受大小的时候,必须要比你发的这个 包要大,不然一次接受不了就会报错,而tcp是不会报错的,这也是为什么udp会丢包的原因
 打印进度条(简易)
import time
 
for i in range(20):
 
    print('\r' + i*'*',end='')
    time.sleep(0.2)
1.缓冲区
每个socket被创建以后,都会分配两个缓冲区,输入缓冲区和输出缓冲区,默认大小都是8k,可以通过getsocket()获取,暂时存放传输数据,防止程序在发送的时候卡阻,提高代码运行效率.
首先看python的系统交互subprocess:
import subprocess
 
sub_obj = subprocess.Popen(
    'ls',  #系统命令
    shell = True,  #固定格式
    stdout=subprocess.PIPE,  #标准输出  PIPE管道,保存着指令的执行结果
    stderr=subprocess.PIPE   #标准错误输出
)
 
print('正确输出',sub_obj.stdout.read().decode('gbk'))
print('错误输出',sub_obj.stderr.read().decode('gbk'))
 
 
#测试byte长度
# print(len(b'hello'))
# print(bytes(str(2),encoding='utf-8'))
结果编码是以但钱所在系统为准的,诺为windo,则用GBK解码,且只能从管道里读取一次结果
2.黏包现象
   1.tcp两种黏包现象:
   a. 发送端需要等缓冲区满了才发送出去,造成黏包(发送时间的间隔很短,数据也很小,会被底层优化算法河道一起,产生黏包现象)
server端的代码示例额如下:
from socket import *
ip_port = ('127.0.0.1',8080)
 
tcp_socket_server =socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen()
conn,addr = tcp_socket_server.accept()
#服务端连接接收两个信息
data1=conn.recv(10)
data2=conn.recv(10)
 
print('------>',data1.decode('utf-8'))
print('------>',data2.decode('utf-8'))
 
conn.close()
client端的实例如下:
import socket
 
BUFSIZE= 1024
ip_port = ('127.0.0.1',8080)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 
res = s.connect(ip_port)
 
s.send('hi'.encode('utf-8'))
s.send('meinv'.encode('utf-8'))
 b. 接受方没有及时接受缓冲区的包,导致多个包接受,(客户端发送一段数据,服务端只收了一小部分,服务区 下次接受的时候还是从缓冲区拿上次遗留的数据,产生黏包)第一次如果发送的数据大小2000B,接受端一次性接受大小为1024,这样就导致剩下的内容会被下一次recv接收到,导致结果的错乱.
server端的代码示例额如下:
import socket
import subprocess
 
 
server = socket.socket()
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn, addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
    print(from_client_cmd.decode('utf-8'))
 
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
 
    std_msg = sub_obj.stdout.read()
    print('指令的执行结果长度>>>>',len(std_msg))
 
    conn.send(std_msg)
client端的实例如下:
import socket
 
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input("请输入指令:")
 
    client.send(cmd.encode('utf-8'))
    server_cmd_result = client.recv(1024)
 
    print(server_cmd_result.decode('gbk'))
解决tcp黏包的方案有两种,第一种是了解一下,第二种是必须记住.
1.方案一:由于接受方不知道发送端将要传送的自接力的长度,导致接收的尾喉,可能接收不全,或者多接收另外一次发送的内容,把自己将要送的字节流总大小让对方知晓,然后接收方发一个确认消息给发送端,然后发送端在发送过来后面的真实数据,接收方在来呢接收完
server端的代码示例额如下:
import socket
import subprocess
 
server = socket.socket( )
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn,addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
 
    print(from_client_cmd.decode('utf-8'))#接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
 
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE, #正确结果的存放的位置
        stderr=subprocess.PIPE  #错误结果的存放的位置
    )
    # 从管道里面拿结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()
    #为了解决黏包现象,我们统计一下消息的长度,现将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
    std_msg_len = len(std_msg)
  #首先将数据长度的数据类型装换成bytes类型
    std_bytes_len = str(len(std_msg)).encode('utf-8')
    print('指令的执行结果长度>>>>>:',len(std_msg))
 
    conn.send(std_bytes_len)
    status = conn.recv(1024)
    if status.decode('utf-8') == 'ok':
 
        conn.send(std_msg)
    else:
        pass
client端的实例如下:
import socket
 
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input('请输入指令:')
 
    client.send(cmd.encode('utf-8'))
 
    server_res_len = client.recv(1024).decode('utf-8')
 
    print('来自服务端的消息长度',server_res_len)
 
    client.send(b'ok')
 
    server_cmd_result = client.recv(int(server_res_len))
 
    print(server_cmd_result.decode('gbk'))`
2.方案二: 通过struck模块将需要发送的内容长度进行打包,打包成一个4字节长度的数据发送到对端,对端只要取出前4个字节,然后对前4个字节的数据进行捷豹,拿到发送的长度,然后通过这个长度来继续接受我们要发送的内容.
struck模块的使用:struck模块中最重的两个函数就是pack()打包,unpack()解包
pack(): 我们这里只介绍'i'这个int类型,上面的途中列举了处理可以打包的所有的数据类型,并且struck处理pack和unpack两个方法之外还有好多别的方法和用法.
import struct
num = 100
# num太大的话会报错,
# struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
# 打包,将int类型的数据打包成4个长度的bytes类型的数据
byt = struct.pack('i',num)
print(byt)
# 解包,将bytes类型的数据,转换为对应的那个int类型的数据
# 注:unpack返回的是truple
int_num = struct.unpack('i',byt)[0]
print(int_num)
解决黏包现象的第二种方案:
服务端:
import socket
import subprocess
import struct
 
server = socket.socket()
ip_port = ('127.0.0.1',8001)
 
server.bind(ip_port)
server.listen()
 
conn,addr = server.accept()
 
while 1:
    from_client_cmd = conn.recv(1024)
    print(from_client_cmd.decode('utf-8'))
    #接收到客户端发送过来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令
    sub_obj = subprocess.Popen(
        from_client_cmd.decode('utf-8'),
        shell=True,
        stdout=subprocess.PIPE, #正确结果的存放位置
        stderr=subprocess.PIPE  #错误结果的存放位置
    )
    # 从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
    std_msg = sub_obj.stdout.read()
    #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实的数据
    std_msg_len = len(std_msg)
    print('指令的执行结果长度>>>>>>',len(std_msg))
 
    msg_lenint_struct =struct.pack('i',std_msg_len)
 
    conn.send(msg_lenint_struct+std_msg)
客户端:
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1',8001))
 
while 1:
    cmd = input('请输入指令:')
    #发送指令
    client.send(cmd.encode('utf-8'))
    #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度
    server_res_len=client.recv(4)
    msg_len= struct.unpack('i',server_res_len)[0]
 
    print('来自服务端的消息长度',msg_len)
    #通过解包出来的长度,来接收后面的真实数据
    server_cmd_result =client.recv(msg_len)
 
    print(server_cmd_result.decode('gbk'))
查看自己的缓存的大小
# print(server.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF))
# print(server.getsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF))
3. udp是面向包的,所以udp是不存在黏包的。
在udp代码中,我们在server端接受返回消息的时候,我们设置的recvfrom(1024),那么当我们输入的执行指令为'dir'的时候,若dir在当前文件夹下输出的内容大于1024,然后就报错了,
解释原因:因为udp是面向报文的,每个消息是一个包,接收端设置接受大小的时候,必须要比你发的这个 包要大,不然一次接受不了就会报错,而tcp是不会报错的,这也是为什么udp会丢包的原因
 打印进度条(简易)
import time
 
for i in range(20):
 
    print('\r' + i*'*',end='')
    time.sleep(0.2)

day 28 黏包及黏包解决方案的更多相关文章

  1. Python 黏包及黏包解决方案

    粘包现象 说粘包之前,我们先说两个内容,1.缓冲区.2.windows下cmd窗口调用系统指令 1 缓冲区(下面粘包现象的图里面还有关于缓冲区的解释) 每个 socket 被创建后,都会分配两个缓冲区 ...

  2. 有关 Android Studio 重复引入包的问题和解决方案

    虽然相同包名相同类名的文件在不同 SDK 中出现的概率极低,但是一旦出现,处理起来就比较棘手.最好的解决方案就是联系提供 SDK 的技术人员反映问题,让其通过修改源码重新打包一个新的 Jar 包. 还 ...

  3. Netty中粘包和拆包的解决方案

    粘包和拆包是TCP网络编程中不可避免的,无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包和拆包 TCP是个“流”协议,所谓流,就是没有界限的一串 ...

  4. Flutter学习笔记(28)--使用第三方jar包

    如需转载,请注明出处:Flutter学习笔记(28)--使用第三方jar包 1.打开一个Flutter项目,点击编码窗口右上角的Open for Editing in Android Studio,这 ...

  5. java nio消息半包、粘包解决方案

    问题背景 NIO是面向缓冲区进行通信的,不是面向流的.我们都知道,既然是缓冲区,那它一定存在一个固定大小.这样一来通常会遇到两个问题: 消息粘包:当缓冲区足够大,由于网络不稳定种种原因,可能会有多条消 ...

  6. TCP 粘包 - 拆包问题及解决方案

    目录 TCP粘包拆包问题 什么是粘包 - 拆包问题 为什么存在粘包 - 拆包问题 粘包 - 拆包 演示 粘包 - 拆包 解决方案 方式一: 固定缓冲区大小 方式二: 封装请求协议 方式三: 特殊字符结 ...

  7. TCP粘"包"问题浅析及解决方案Golang代码实现

    一.粘"包"问题简介 在socket网络编程中,都是端到端通信,客户端端口+客户端IP+服务端端口+服务端IP+传输协议就组成一个可以唯一可以明确的标识一条连接.在TCP的sock ...

  8. 粘包处理现象及其解决方案——基于NewLife.Net网络库的管道式帧长粘包处理方法

    [toc] #1.粘包现象 每个TCP 长连接都有自己的socket缓存buffer,默认大小是8K,可支持手动设置.粘包是TCP长连接中最常见的现象,如下图 socket缓存中有5帧(或者说5包)心 ...

  9. day34 基于TCP和UDP的套接字方法 粘包问题 丢包问题

    TCP 基于流的协议 又叫可靠性传输协议 通过三次握手 四次挥手 来保证数据传输完毕 缺点效率低 正因为是基于流的协议 所以会出现粘包问题粘包问题:原因一:是应为数据是先发送给操作系统,在操作系统中有 ...

  10. 什么是"抓包"?怎样"抓包"?

    你是网络管理员吗?你是不是有过这样的经历:在某一天的早上你突然发现网络性能急剧下降,网络服务不能正常提供,服务器访问速度极慢甚至不能访问,网络交换机端口指示灯疯狂地闪烁.网络出口处的路由器已经处于满负 ...

随机推荐

  1. Microsoft.Exchange 发邮件

    //Microsoft.Exchange.WebServices.dll ExchangeService service = new ExchangeService(); // 获取身份验证, 能够尝 ...

  2. sql 连表

    左外 右外连接 select * from tb1 left   join tb2 on(tb1.id = tb2.id) select * from tb1 right join tb2 on(tb ...

  3. Docker技术三大要点:cgroup, namespace和unionFS的理解

    www.docker.com的网页有这样一张有意思的动画: 从这张gif图片,我们不难看出Docker网站想传达这样一条信息, 使用Docker加速了build,ship和run的过程. Docker ...

  4. Celery+redis实现异步

    目录 Celery+redis实现异步 安装redis 安装celery-with-redis 添加celery相关配置 创建异步运行任务tasks.py 启动 Celery+redis实现异步 安装 ...

  5. was缓存以致web.xml更改无效

    was缓存导致web.xml更改无效 在项目中经常遇见这样的问题:修改应用的配置文件web.xml后,无论重启应用还是重启WebSphere服务器,都不能重新加载web.xml,导致修改的内容无效. ...

  6. 对于view的深入理解,续篇(一)重写ViewGroup的onDraw

    在上一篇文章中,不仅熟悉了动态设定布局的方法,而且也对view的绘制流程有所了解.于是我继续做了下面几个实验,发现了一个问题.如果对view的绘制流程不是很明白,可以看看我的上一篇文章的介绍,点击下面 ...

  7. swift菜鸟入门视频教程-02-基本运算符

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/mengxiangyue/article/details/32435435 本人自己录制的swift菜 ...

  8. MySQL常用函数 一

    一.数学函数ABS(x)                    返回x的绝对值BIN(x) 返回x的二进制(OCT返回八进制,HEX返回十六进制)CEILING(x)                返 ...

  9. 22、整合mybatis

    搭建环境: 1).创建工程需要的maven坐标 这个mybatis的starter是mybatis官方出的适应springboot 2).数据连接池的使用 引入Druid数据连接池 <depen ...

  10. centos7修改主机名和ip映射

    1.修改主机名:命令:/etc/hostname文件输入:HOSTNAME=node3想永久修改,应该修改配置文件 /etc/sysconfig/network. 输入:NETWORKING=yes ...