粘包问题

内容概要

  • 粘包现象
  • 解决粘包逻辑思路
  • 代码实操
  • UDP基本代码使用
  • 并发编程理论之操作系统发展史
  • 多道技术
  • 进程理论即调度算法

粘包现象

当发送端在短时间内发送多次较短的数据的时候

接收端会一次性把这些数据全部接收

  • 粘包问题产生的原因

    1. 不知道每次的数据到底多大
    2. TCP也称之为流式协议:数据像水流一样不断没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
  • 避免粘包现象的核心思路\关键点

    如何明确即将接收的数据具体有多大

ps:如何将长度变化的数据全部制作成固定长度的数据

struct模块

import json
import struct

l1 = [1, 2, 3, 4, 5, 6, 7]
d1 = {
    "name": "jason",
    "age": "123"
}
# 1.因为字典没有办法转成字节类型 只有字符串可以转成字节类型
# 2.因为我们是为了发做准备 所以先把他转换成json格式 让接收方 但是解开的还是 原来的类型
# 比如 我传的字典 传过去打开还是字典
# res = struct.pack("i", json.dumps(l1).__len__())  # 第一参数传模式  第二个参数传数字 int类型
# res1 = struct.pack("i", json.dumps(d1).__len__())
#
# print(len(res))  # 4
# print(len(res1))  # 4

# res = struct.pack("i", len(l1))  
# res1 = struct.pack("i", len(d1))  
# print(len(res))  # 4
# print(len(res1))  # 4

不管多少 压缩后的长度都是 4

但是他也是有长度的限制的

解决粘包问题

服务端:

import struct
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)

sock, addr = server.accept()  # 三次握手 建连接 返回管道  和 客户端的地址

# 发送先发送头  也就是 先告诉对方我要发送的数据的大小
data = "hello"
# 因为发送的字节形式  所以要发送的字节的长度
data_head = struct.pack("i", len(data.encode("utf8")))
sock.send(data_head)
sock.send(data.encode("utf8"))
data1 = "world"
data1_head = struct.pack("i", len(data1.encode("utf8")))
sock.send(data1_head)
sock.send(data1.encode("utf8"))

客户端:

import struct
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))

data_head = client.recv(4)
data_len = struct.unpack("i", data_head)[0]
print(data_len)
data = client.recv(data_len)
print(data.decode("utf8"))

data1_head = client.recv(4)
data1_len = struct.unpack("i", data1_head)[0]
print(data1_len)
data1 = client.recv(data1_len)
print(data1.decode("utf8"))

"""
解决粘包问题初次版本
	服务端:
		1.将真实数据转化成bytes类型并计算长度
		2.利用struct模块将真实长度制作一个固定长度的报头
		3.将固定长度的报头发送给客户端 客户端只需要在recv括号内填写固定长度的报头数即可
		4.然后再发送真实数据
	客户端:
		1.服务端先接收固定长度的报头
		2.利用struct模块反向解析出真实数据的长度
		3.recv接收真实数据长度即可


问题1:struct模块无法打包大量较大的数据 就算换更大的模式也不行
问题2:报头能否传递更多的信息 比如视频的大小 电影的名称
		电影的简介


解决方案:字典作为报头打包 效果更好 数字更小


"""

终极方案

服务端:

import json
import struct
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)

sock, addr = server.accept()
dict_pack_head = sock.recv(4)
dict_pack_len = struct.unpack("i", dict_pack_head)[0]
dict_pack = sock.recv(dict_pack_len)
print(dict_pack)
data_dict = json.loads(dict_pack)
file_size = data_dict.get("file_size")
res_size = 0
with open(r"F:\pythonProject1\day36\aa\间谍过家家第12集.mp4", "wb")as f:
    while res_size < file_size:
        if (file_size - res_size) < 8192:
            recv_size = (file_size - res_size)
        else:
            recv_size = 8192
        data = sock.recv(recv_size)
        f.write(data)
        res_size += recv_size

客户端:

import json
import time
import struct
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# client.connect(("192.168.1.116", 8080))
client.connect(("127.0.0.1", 8080))
# client.connect(("192.168.1.51", 8080))



file_size = os.path.getsize("a1.mp4")
data_dict = {"file_name": "间谍过家家",
             "file_size": file_size,
             "file_desc": "嘎嘎好看"}

data_dict = json.dumps(data_dict)
data_head_len = struct.pack("i", len(data_dict.encode("utf8")))
client.send(data_head_len)
client.send(data_dict.encode("utf8"))
print(file_size)
res_size = 0
with open("a1.mp4", "rb")as f:

    while res_size < file_size:
        if (file_size - res_size) < 8192:
            recv_size = (file_size - res_size)
        else:
            recv_size = 8192
        time.sleep(0.00000000001)
        data = f.read(recv_size)
        client.send(data)
        res_size += recv_size
        print(res_size)

UDP协议

udp协议 他只管发 不管你收没有 服务端有没有运行

udp不会出现粘包问题

服务端:

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 8080))

data, addr = server.recvfrom(1024)
print(data.decode("utf8"))
print(addr)

客户端:

import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ("127.0.0.1", 8080)
date = "哈哈"
client.sendto(date.encode("utf8"), addr)

并发编程理论

研究网络编程其实就是研究计算机的底层原理及发展史

计算机中真正干活的是cpu

  • 操作系统的发展史

    1. 穿孔卡片阶段

      计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多时候计算机都不工作

      好处:程序员可以独占计算机 为所欲为

      坏处:计算机利用率太低 浪费资源

    2. 联机批处理系统

      提前使用磁带一次性录入多个程序编写的程序 然后交给计算机执行

      CPU工作效率有所提升 不用反复等待程序录入

    3. 脱机批处理系统

      极大提升了CPU的利用率

    4. 时间片轮转法加多级反馈队列

      他会这设置一个时间片,每个进程的只有这个时间片的时间拥有CPU当时间片到了进程有两种情况

      1. 时间到了进程刚好结束,那么会去运行下一个进程
      2. 时间到了进程还没有运行完会下方,然后去运行下一个进程,越往下的进程的优先级越低。

总结:操作系统的发展史 就是提升cpu利用率的过程

多道技术

  • 单道技术

    就是一个一个来 ,结束一个然后再运行下一个

  • 多道技术

    利用空闲的时间准备其他数据,最大化提升CPU利用率

多道技术详细

  1. 切换

    计算机的CPU再两种情况下会切换

    1. 当程序有IO操作

      输入输出操作

      input、time.sleep、read、write

    2. 程序长时间占用CPU

      让多个程序都能被CPU运行一下

  2. 保存状态

    CPU每次切走之前都需要保存当前的操作的状态 下次切换回来基于上次的进度继续执行

进程的并行于并发

  • 并行

    多个进程同时执行 必须要有多个CPU参与 单个CPU无法实现并行

    也就并行几个进程 就需要多少个CPU

  • 并发

    多个进程看上去像同时进行 单个CPU可以实现 多个CPU肯定也可以

进程的三种状态

就绪态

​ 所有的进程再被CPU执行之前都必须先进入就绪态等待

运行态

​ CPU正在执行

阻塞态

​ 进程运行过程中出现了IO操作 阻塞无法直接进入运行态 需要先进入就绪态

posted @ 2022-11-17 20:17  可否  阅读(23)  评论(0)    收藏  举报