• 0507黏包


    什么是黏包?

    同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是黏包。

    二、黏包的成因

    tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制
    无边界 所有在连接建立的基础上传递的数据之间没有界限
    收发消息很有可能不完全相等
    缓存机制,导致没发过去的消息会在发送端缓存,没接收完的消息会在接收端缓存

    注意:只有TCP有粘包现象,UDP永远不会粘包

    tcp协议的拆包机制

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。
    如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

      面向流的通信特点和Nagle算法

    TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。 收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),

    将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。 这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。 对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,
    即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。 可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。

    例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束

    此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

    总结

    黏包现象只发生在tcp协议中:

    1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

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

    解决:
        给应用层定制协议
     解决方案一:只发送一条信息
    先发送一个定长表示待发送数据长度的bytes     先接收一个固定长度
    再发送要发送的数据                          再按照长度接收数据
    
    server
    import struct
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    conn,addr = sk.accept()
    while True:
    ret = conn.recv(4)
    length = struct.unpack('i',ret)[0]
    msg = conn.recv(length)
    print(msg.decode('utf-8'))
    conn.close()
    client端
    import socket
    import struct
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    while True:
    msg = 'hello world'
    length = struct.pack('i',len(msg))
    sk.send(length)
    sk.send(msg.encode('utf-8'))
    sk.close()
    解决方案二 :发送的多条信息 先发送一个定长表示待发送字典长度的bytes 先接收一个固定长度 再发送要发送字典(字典中存储的是文件信息) 再按照长度接收字典 再发送文件 再根据字典中的信息接收相应的内容

    server端
    import os
    import json
    import struct
    import socket

    # D:python11day351.复习.py
    sk = socket.socket()
    sk.bind(('192.168.11.92',9000))
    sk.listen()

    conn,addr = sk.accept()
    print(addr)
    dic = {'filename':'1.复习.py',
    'filesize':os.path.getsize(r'D:python11day351.复习.py')}
    str_dic = json.dumps(dic).encode('utf-8')
    dic_len = struct.pack('i',len(str_dic))
    conn.send(dic_len)
    conn.send(str_dic)
    with open(r'D:python11day351.复习.py','rb') as f:
    content = f.read()
    conn.send(content)
    conn.close()
    sk.close()

    client端
    import json
    import struct
    import socket

    sk = socket.socket()
    sk.connect(('192.168.11.92',9000))

    dic_len = sk.recv(4)
    dic_len = struct.unpack('i',dic_len)[0]
    str_dic = sk.recv(dic_len).decode('utf-8')
    dic = json.loads(str_dic)

    with open(dic['filename'],'wb') as f:
    content = sk.recv(dic['filesize'])
    f.write(content)
    sk.close()

    验证客户端链接的合法性

     

     socketserver并发编程

    import socketserver
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                print(self.request)
                self.request.send(b'hello')
                print(self.request.recv(1024))
    if __name__=='__main__':
        socketserver.TCPServer.allow_reuse_address = True
        server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Myserver)
        server.serve_forever()
  • 相关阅读:
    CLOUD COMPUTING MADE EASY by Cary Landis and Dan Blacharski
    浅析JSONP
    Xpages 执行的生命周期
    LotusScript类的继承
    苹果公司的UI交互设计师Bret Victor演讲 Inventing on Principle
    ajax
    Aptana 汉化方法
    删除数据库连接脚本
    让Visual Studio 也支持JS代码折叠 [ Visual Studio | #region | #endregion ]
    power designer简单教程
  • 原文地址:https://www.cnblogs.com/Mr-Murray/p/9005426.html
Copyright © 2020-2023  润新知