• 网络编程之-----粘包


    一.什么是粘包:

      TCP是可靠的面向连接的协议,传输效率低,全双工通信,面向字节流.

      UDP是不可靠的无连接的协议,传输效率高,无阻塞控制.

      粘包的成因是由于TCP协议本身造成的,TCP为了提高传输效率,发送方往往会收集到足够多的数据才发送一个TCP段.如果连续几次发送的数据都很少,TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样,接收方接收到的数据就发生了粘包.

      发生粘包的两种情况:

        1.由于发送端的缓存机制导致的粘包

          发送端需要等缓存区满以后才发送,造成粘包(发送数据时间间隔短,数据少,就会合到一起,产生粘包.)

    import socket
    server = socket.socket()
    ip_port = ('127.0.0.1', 8000)
    server.bind(ip_port)
    server.listen(5)
    conn,addr = server.accept()
    data1 = conn.recv(10)
    data2 = conn.recv(10)
    print('data1', data1.decode('utf-8'))
    print('data2', data2.decode('utf-8'))
    conn.close()
    服务端
    import socket
    client = socket.socket()
    ip_port = ('127.0.0.1', 8000)
    client.connect(ip_port)
    client.send('hello'.encode('utf-8'))
    client.send('world'.encode('utf-8'))
    客户端

        此时服务端收到的消息:(data1:helloworld),(data2: )

        2.由于服务端的缓存机制导致的粘包

          接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接受了一部分,服务端下次接收数据的时候还是从缓存区上次剩下的地方开始拿,产生了粘包.)

    import socket
    server = socket.socket()
    ip_port = ('127.0.0.1', 8000)
    server.bind(ip_port)
    server.listen(5)
    conn,addr = server.accept()
    data1 = conn.recv(2)
    data2 = conn.recv(10)
    print('data1', data1.decode('utf-8'))
    print('data2', data2.decode('utf-8'))
    conn.close()
    服务端
    import socket
    client = socket.socket()
    ip_port = ('127.0.0.1', 8000)
    client.connect(ip_port)
    client.send('hello'.encode('utf-8'))
    client.send('world'.encode('utf-8'))
    客户端

        此时服务端收到的消息:(data1:he), (data2:lloworld)

      总结:

        1.粘包只发生在TCP协议中.

        2.粘包不一定会发生

        3.表面上看,粘包问题主要是因为发送方和接收方分缓存机制,TCP面向连接通信的特点产生的.

        4.实际上,主要因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的.

    粘包的解决方案

      

    import socket
    import subprocess
    import struct       #struct 可以定制报头
    server = socket.socket()
    ip_port = ('127.0.0.1', 8018)
    server.bind(ip_port)
    server.listen(5)
    while True:
        print('server is working......')
        conn,addr = server.accept()
        while True:
            try:            #处理客户端断开连接异常
                cmd = conn.recv(1024)
                if cmd == b'exit':
                    break
                res = subprocess.Popen(cmd.decode('utf-8'),
                                       shell=True,
                                       stderr=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       )
                err = res.stderr.read()
                if err:
                    data = err
                else:
                    data = res.stdout.read()
                conn.send(struct.pack('i', len(data)))     #自定制报头
                conn.send(data)                            #发送所有报文
            except Exception as e:
                break
        conn.close()
    服务端
    import socket
    import struct
    client = socket.socket()
    ip_port = ('127.0.0.1', 8018)
    client.connect(ip_port)
    while True:
        cmd = input('请输入命令:')
        if cmd == '':
            continue
        elif cmd == 'exit':
            break
        client.send(cmd.encode('utf-8'))
    
        header_pack = client.recv(4)
        header = struct.unpack('i', header_pack)[0]
        data_length = 0
        data_num = b''
        while data_length < header:
            data = client.recv(1024)
            data_num += data
            data_length += len(data)
        print(data_num.decode('gbk'))
    客户端
  • 相关阅读:
    大二下学期团队项目(app端web请求)
    大二下每周总结
    大二下学期团队项目(手机端分类查询前端)
    Windows 2008 R2 64位下安装Oracle 10.2.0.5
    Win7 32位下安装Oracle 10g
    SQL Developer安装使用教程
    Oracle使用DBCA建立数据库实例
    史上最全Oracle安装配置图文教程,含TNS配置
    Oracle9i完美安装、配置及操作
    安装MySQL启动服务时报错解决办法,错误代码1045
  • 原文地址:https://www.cnblogs.com/Virous1887/p/9588386.html
Copyright © 2020-2023  润新知