IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> python3-网络编程及粘包问题 -> 正文阅读

[网络协议]python3-网络编程及粘包问题

系列文章目录

一、套接字(socket)介绍

  • 概念:

    在应用层和传输层之间,还存在着一层Socket抽象层,它不属于互联网通信协议,它是对传输层及其以下各层的封装。

  • 作用:

    我们在写网络相关的应用程序时,只需要与套接字(操作系统)打交道即可。不需要处理复杂的系统调用和网络协议,无论接发数据都通过套接字来完成。

二、半连接池

? 半连接池是一个起缓存作用的“容器”,用来暂时存放连接请求。

? 半连接池的大小,就是允许等待连接的最大请求个数。

三、socket模块

1. TCP套接字

  • 流程图:
    TCP连接

  • 客户端代码:

    import socket
    
    # 1.创建套接字对象
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2.请求连接,传入服务端的IP地址和端口号
    # 没有返回值,连接对象就是client_socket自己
    client_socket.connect('127.0.0.1', 8001)
    
    # 3.通过连接收发数据,收发的数据皆为bytes类型
    # 发数据
    client_socket.send('发送的数据'.encode('utf-8'))
    # 收数据
    data = client_socket.recv(1024)
    
    # 关闭套接字
    client_socket.close()
    
  • 服务端代码:

    import socket
    
    # 1.创建套接字对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2.绑定IP地址和端口号
    server_socket.bind('127.0.0.1', 8000)
    
    
    # 3.监听端口,即上面绑定的端口
    # 传入的参数为半连接池的大小
    server_socket.listen(5)
    
    
    # 4.从半连接池取出连接请求并接受,返回连接对象和客户端地址
    conn, client_addr = server_socket.accept()
    
    
    # 5.通过连接收发数据,收发的数据皆为bytes类型
    # 收数据,参数为最大接收数据量,单位为bytes
    data = conn.recv(1024)
    
    
    # 发数据,需要编码为bytes类型
    conn.send('回复的数据'.encode('utf-8'))
    
    
    # 6.关闭连接
    conn.close()
    
    # 7.关闭套接字(实际上永远不会关闭)
    server_socket.close()
    
  • socket的参数:

    socket类型描述
    socket.AF_UNIX只能够用于单一的Unix系统进程间通信
    socket.AF_INET使用IPv4网络层协议
    socket.AF_INET6使用IPv6网络层协议
    socket.SOCK_STREAM数据流式socket , 即 TCP
    socket.SOCK_DGRAM数据报式socket , 即 UDP
    socket.SOCK_RAW原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
    socket.SOCK_SEQPACKET可靠的连续数据包服务

2. UDP套接字

  • 流程图:

    与TCP不同,UDP没有连接的概念, 实现更加简单。但在每次收发数据时,都要附带上主机地址。

UPD连接

  • 客户端代码:

    import socket
    
    
    # 1.创建套接字对象
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 不需要请求建立连接
    
    # 2.收发数据
    # 发数据,需要以元组类型传入服务端的地址
    client_socket.sendto('发送的数据'.encode('utf-8'), ('127.0.0.1', 8000))
    
    # 收数据,返回数据和服务端地址
    data, server_addr = client_socket.recvfrom(1024)
    
    # 关闭套接字
    client_socket.close()
    
  • 服务端代码:

    import socket
    
    
    # 1.创建套接字对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 2.绑定IP地址和端口号
    server_socket.bind('127.0.0.1', 8000)
    
    # 不需要监听端口,也不需要产生连接对象
    
    # 3.收发数据,不需要连接
    # 收数据,返回数据和客户端地址
    data, client_addr = server_socket.recvfrom(1024)
    
    # 发数据,需要以元组形式传入客户端地址
    server_socket.sendto('回复的数据'.encode('utf-8'), ('127.0.0.1', 8001))
    
    # 4.关闭套接字(实际上永远不会关闭)
    server_socket.close()
    

四、粘包问题及解决方法

1. 粘包问题:

在TCP协议中,数据是流式传输的,就像水流一样,数据包之间没有明确的“边界”。套接字在接收数据时,可能会将上一条消息末尾的数据可能会和下一条消息的开头“粘”在一起接收,导致得到的最终数据混乱。这就是TCP协议的粘包问题。

而在UDP协议中,数据是以消息为单位的传输的,数据包之间有着明确的“边界”,不会出现粘包问题。

此外,TCP协议为了优化性能,在底层采用了nagle算法。它会将短时间内收到的多个小的数据包,组合成一个大的数据包后再接收。这也是造成粘包问题的另一原因。

2. 解决方法:

  • 发送方:
  1. 先组织一个头部信息,包含整个数据的总大小;

  2. 然后将头部信息的长度,发送给接收方;

  3. 因为头部信息长度的大小是固定的,所以接收方可以很容易获取到头部信息的长度;

  4. 接收方通过该长度信息,获取头部信息,从而得知整个实际数据的大小,然后再发送实际要发送的数据:

    import json
    import struct
    
    # 头部信息
    header = {
        'size': 2048;
        ...
    }
    
    # 将头部信息转换为bytes类型的JSON数据
    header_json_bytes = json.dumps(头部信息).encode('utf-8')
    
    # 将头部的长度,打包为bytes类型,长度固定为4个字节
    header_size = struct.pack('i', len(header_json_bytes))
    
    # 发送头部的长度信息
    conn.send(header_size)
    
    # 发送头部信息
    conn.send(header_json_bytes)
    
    # 发送实际数据
    ...
    
  • 接收方:
  1. 先获取头部的长度信息

  2. 获取头部信息

  3. 获取数据的总大小

  4. 在已接收数据少于总数居的情况下,循环接收数据:

    import json
    import struct
    
    # 接收头部的长度信息
    header_size_bytes = client.recv(4)
    # 获取头部长度,返回值为元组,但我们只需要第一个元素
    header_size = struct.unpcak('i', header_size_bytes)[0]
    
    # 获取头部信息
    header_json_bytes = client.recv(header_size)
    header_json = header_json_bytes.decode('utf-8')
    header = json.loads(header_json)
    
    
    # 获取实际数据的总大小
    total_size = header.get('total_size')
    
    # 保存收到的数据
    res_data = b''
    # 已经接收到数据的大小
    recv_size = 0
    
    # 如果已接收数据少于总数据
    while recv_size < total_size:
       # 接收实际数据
       recv_data = client.recv(1024)
       # 计算已经接收到数据的大小
       recv_size += len(recv_data)
       # 将收到的数据拼接到一起
       res_data += recv_data
    
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 12:01:15  更:2021-07-29 12:01:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 13:29:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计