zoukankan      html  css  js  c++  java
  • Python--网络编程-----粘包的底层原理分析

    一、send是不是直接把数据发给服务端

    不是,要想发数据,必须得通过网卡发送数据,应用软件是无法直接通过网卡发送数据的,它需要调用操作系统接口,

    也就是说,应用软件把要发送的数据由应用系统内存copy到操作系统内存,进而由操作系统控制数据的发送,copy到

    操作系统内存也意味着send已经发送完毕了,它是无法控制操作系统怎样发送数据的。

    二、recv是不是直接从客户端接收数据

    不是,与send数据相同,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,应用软件把要接收的数

    据由操作系统内存copy到应用系统内存。

    三、不管是recv还是send都不是直接接收对方的数据,而是操作自己操作系统内存--------》不是一个send对应一个recv

    1、recv:

        wait data:耗时非常长

        copy data

    2、send:

        copy data

    3、socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方。若连续几次需要send的数据都很少,

    通常TCP socket 会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

    4、发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,

    合并成一个大的数据块,然后进行封包。

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

    四、粘包不一定会发生,在数据量小,时间间隔短的情况下会发生,代码示例如下:

    1、客户端粘包

     1 客户端代码:
     2 import socket
     3 
     4 
     5 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     6 
     7 client.connect(('127.0.0.1', 9903))
     8 
     9 client.send('hello'.encode('utf-8'))
    10 client.send('world'.encode('utf-8'))
    11 
    12 服务端代码:
    13 import socket
    14 
    15 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    16 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
    17 server.listen(5)
    18 
    19 conn, addr = server.accept()
    20 
    21 res1 = conn.recv(1024)
    22 print('第一次结果:', res1)
    23 
    24 res2 = conn.recv(1024)
    25 print('第二次结果:', res2)
    26 
    27 启动之后服务端结果为:
    28 第一次结果: b'helloworld'
    29 第二次结果: b''

    两次send在客户端已经粘包,一次性发给服务端,所以服务端第一次就从服务端的操作系统内存中取出了两次send内容

    如果在客户端两次send中间加一个time.sleep(1),服务端结果为:

    1 第一次结果: b'hello'
    2 第二次结果: b'world'

    可以看出,客户端不会等待1秒钟,而直接把hello发过去了,1秒钟后,再把world发送到服务端,这样客户端和服务端都没有发生粘包

    2、服务端粘包

     1 服务端代码为:
     2 
     3 import socket
     4 
     5 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     6 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
     7 server.listen(5)
     8 
     9 conn, addr = server.accept()
    10 
    11 res1 = conn.recv(1)
    12 print('第一次结果:', res1)
    13 
    14 res2 = conn.recv(1024)
    15 print('第二次结果:', res2)
    16 
    17 客户端代码为:
    18 
    19 import socket
    20 
    21 
    22 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    23 
    24 client.connect(('127.0.0.1', 9903))
    25 
    26 client.send('hello'.encode('utf-8'))
    27 client.send('world'.encode('utf-8'))
    28 
    29 服务端运行结果为:
    30 
    31 第一次结果: b'h'
    32 第二次结果: b'elloworld'

    第一次recv只接收了一个byte的数据h,ello四个字节的数据遗留在管道中,会和后面的数据在服务端发生粘包现象,

    如果知道发送端的数据量大小,第一次recv5个byte的数据,第二次接收5个byte的数据,也不会发生粘包现象,

    或者服务端代码改为如下:

     1 import socket
     2 
     3 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     4 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
     5 server.listen(5)
     6 
     7 conn, addr = server.accept()
     8 
     9 res1 = conn.recv(1)
    10 res2 = conn.recv(1)
    11 res3 = conn.recv(1)
    12 res4 = conn.recv(1)
    13 res5 = conn.recv(1)
    14 print('第一次结果:', res1+res2+res3+res4+res5)
    15 
    16 res2 = conn.recv(1024)
    17 print('第二次结果:', res2)

    服务端也不会发生粘包现象,因为保证了第一次结果把第一次send的数据接收完整,

    所以说,要想避免粘包现象,必须知道发送端send的数据量的大小,然后服务端根据send的数据量的大小recv数据,

  • 相关阅读:
    linux命令查询大全
    常用的Linux命令
    网络工程师必备知识点
    Android app ADB命令
    (转)网络工程师笔记(二)
    (转)网络工程师笔记(一)
    心理学各大分支
    日常生活英语单词大全(转)
    oracle 迁移数据文件
    com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'jeewx.weixin_account_user_relation' doesn't exist
  • 原文地址:https://www.cnblogs.com/xudachen/p/8735554.html
Copyright © 2011-2022 走看看