zoukankan      html  css  js  c++  java
  • 粘包现象

    让我们基于tcp先制作一个远程执行命令的程序

    res=subprocess.Popen(cmd.decode('utf-8'),

    shell=True,

    stderr=subprocess.PIPE,

    stdout=subprocess.PIPE)

    的结果编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()独处的就是GBK编码的,在接收端需要用GBK编码

    且只能从管道里读一次结果

    只有TCP有粘包现象,udp永远你不会粘包,  tcp协议是面向流的协议, udp是面向消息的协议

    所谓粘包的问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少个字节的数据所造成的

    tcp为提高传输效率,tcp优化算法会把一些数据合成一个tcp段后一次发送出去,这样接收方就收到了粘包数据

    tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的

    而udp发送数据,对端是不会返回确认信息的,因此不可靠

    使用tcp协议远程执行命令

    from socket import *

    import subprocess

    ip_port=('127.0.0.1',8080)
    BUFSIZE=1024

    tcp_socket_server=socket(AF_INET,SOCK_STREAM)

    tcp_socket_server.bind(ip_port)

    tcp_socket_server.listen(5)

    while True:

        conn,addr=tcp_socket_server.accept()

        print('客户端‘,addr)

        

        while True:

            cmd=conn.recv(BUFSIZE)

            if len(cmd) == 0:break

       

            res = subprocess.Popen(cmd.decode('utf-8'),shell=True,

                                                 stdout=subprocess.PIPE,

                                                 stdin=subprocess.PIPE,

                                                  stderr=subprocess.PIPE)       

          stderr=res.stderr.read()

          stdout=res.stdout.read()

           conn,send(stderr)

           conn,send(stdout)

    客户端

    import socket

    BUFSIZE=1024

    ip_port=('127.0.0.1',8080)

    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    res=s.connect_ex(ip_port)

    while True:

        msg=input('>>:').strip()

        if len(msg) == 0:break

        s.send(msg.encode('utf-8'))

        act_res=s.recv(BUFSIZE)

        print(act_res.decode('utf-8'),end=")

    上述程序基于tcp的socket,在运行时会发生粘包

    小面基于udp制作一个远程执行命令的程序

     from socket import *

    import subprocess

    ip_port=('127.0.0.1',8080)

    bufsize=1024

    udp_server=socket(AF_INET,SOCK=DGRAM)

    udp_server.bind(ip_port)

    while True:

        cmd,addr=udp_server.recvfrom(bufsize)

        print('用户命令’,cmd)

        

        res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subpross.PIPE,stdout=subprocess.PIPE)

        stderr=res.stderr.read()

        stdout=res.stdout.read()

        udp_server.sendto(stderr,addr)

        udp_server.sendto(stdout,addr)

    udp_server.close()

    客户端

    from socket import *

    ip_port=('127.0.0.1',8080)

    bufsize=1024

    udp_client=socket(AF_INET,SOCK_DGRAM)

    while True:

        msg=input('>>: ').strip()

        udp_client.sendto(msg.encode('utf-8'),ip_port)

        data,addr=udp_client.recvfrom(bufsize)

        print(data.decode('utf-8'),end=")

    以上基于udp的socket,在运行时永远不会发生粘包

    解决粘包的办法

    问题的根源在于,接收端不知道发送端要传送的字节流的长度,所以解决粘包的办法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接受完所有数据

     low版解决方法

    from socket import *

    import subprocess

    ip_port=('127.0.0.1',8080)

    s=socket(AF_INET,SOCK.STREAM)

    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

    s.bind(ip_port)

    s.listen(5)

    while True:

        conn,addr=s.accept()

        print('客户端‘,addr)

        while True:

            msg=conn.recv(1024)

            if not msg:break

            res=subprocess.Popen(msg.decode('utf-8'),shell=True,

                                             stdin=subprocess.PIPE,

                                             stdout=subprocess.PIPE,

                                             stderr=subprocess.PIPE)

               err=res.stderr.read()

               if err:

                      ret=err

               else:

                    ret=res.stdout.read()

               data_length=len(ret)

                conn.send(str(data_lenth).encode('utf-8'))

                data=conn.recv(1024).decode('utf-8')

                if data == 'recv_ready'

                        conn.sendall(ret)

            conn.close()

    客户端

    import socket,time

    s=scoket.socket(socket.AF_INET,socket.SOCK.STRAM)

    res=s.connect_ex(('127.0.0.1',8080))

    while True:

        msg=input('>>:').strip()

        if len(msg) ==0:break

        if msg == 'quit':break

        s.send(msg.encode('utf-8'))

        length=int(s.recv(1024).decode('utf-8'))

        s.send('recv_ready'.encode('utf-8'))

        send_size=0

        recv_size=0

        data=b''

        while recv_size <length:

                data+=s.recv(1024)

                recv_size+=len(data)

    print(data.deode('utf-8'))

    程序运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

    海峰老师解决粘包的方法

    为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接受时,先从缓存中取出定长的报头,然后再取真实数据

  • 相关阅读:
    使用Jenkins自带功能(不用shell)构建Docker镜像并推送到远程仓库
    方法2:使用Jenkins构建Docker镜像 --SpringCloud
    在jenkins中使用shell命令推送当前主机上的docker镜像到远程的Harbor私有仓库
    解决跟Docker私有仓库登陆,推送,拉取镜像出现的报错
    Linux 内存占用大排查
    方法1:使用Jenkins构建Docker镜像 --SpringCloud
    使用Jenkins编译打包SpringCloud微服务中的个别目录
    使用Jenkins的Git Parameter插件来从远程仓库拉取指定目录的内容
    稀疏检出-使用git检索出仓库里的某一个目录文件,而不是整个仓库的所有文件
    通过 Kubeadm 安装 K8S 与高可用,版本1.13.4
  • 原文地址:https://www.cnblogs.com/mayicai/p/9220958.html
Copyright © 2011-2022 走看看