首页 > 系统相关 >粘包现象、进程

粘包现象、进程

时间:2022-11-17 18:33:43浏览次数:38  
标签:数据 len 粘包 现象 dict 长度 进程 报头 data

粘包现象

1.什么是沾包?
  TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
2.为什么出现沾包现象?
  (1)发送方原因
    TCP默认会使用Nagle算法。而Nagle算法主要做两件事:
    1)只有上一个分组得到确认,才会发送下一个分组;
    2)收集多个小分组,在一个确认到来时一起发送。
    所以,正是Nagle算法造成了发送方有可能造成粘包现象。
   (2)接收方原因
	TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不一定会立即处理;实际上,TCP将收到的分组保存至接收缓存里,然后应用程序主动从缓存里读收到的分组。这样一来,如果TCP接收分组的速度大于应用程序读分组的速度,多个包就会被存至缓存,应用程序读时,就会读到多个首尾相接粘到一起的包。
  	(1)如果发送方发送的多个分组本来就是同一个数据的不同部分,比如一个很大的文件被分成多个分组发送,这时,当然不需要处理粘包的现象;
  	(2)但如果多个分组本毫不相干,甚至是并列的关系,我们就一定要处理粘包问题了。比如,我当时要接收的每个分组都是一个有固定格式的商品信息,如果不处理粘包问题,每个读进来的分组我只会处理最前边的那个商品,后边的就会被丢弃。这显然不是我要的结果。
3.如何处理粘包现象
  (1)发送方
  对于发送方造成的粘包现象,我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。
  (2)接收方
  遗憾的是TCP并没有处理接收方粘包现象的机制,我们只能在应用层进行处理。
  (3)应用层处理
  应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题。
  解决方法就是循环处理:应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理;但是如何判断每条数据的长度呢?
  两种途径:
    1)格式化数据:每条数据有固定的格式(开始符、结束符),这种方法简单易行,但选择开始符和结束符的时候一定要注意每条数据的内部一定不能出现开始符或结束符;
    2)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。
       3)多发一个报头信息,在2)的基础上,可以增加更多的数据信息。

struct模块

import struct

# info = b'hello big baby'
# print(len(info))  # 数据真实的长度(bytes)  14
# res = struct.pack('i', len(info))  # 将数据打包成固定的长度 i是固定的打包模式
# print(len(res))  # 打包之后长度为(bytes)   4           报头

# real_len = struct.unpack('i', res)
# print(real_len)  # (14,)             根据固定长度的报头 解析出真实数据的长度


# desc = b'hello my baby I will take you to play big ball'
# print(len(desc))  # 数据真实的长度(bytes)  46
# res1 = struct.pack('i', len(desc))
# print(len(res1))  # 打包之后长度为(bytes)  4           报头

# real_len1 = struct.unpack('i', res1)
# print(real_len1)  # (46,)              根据固定长度的报头 解析出真实数据的长度


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

'''问题1:struct模块无法打包数据量较大的数据 就算换更大的模式也不行'''
# res = struct.pack('i', 12313213123)
# print(res)
'''问题2:报头能否传递更多的信息  比如电影大小 电影名称 电影评价 电影简介'''

'''终极解决方案:字典作为报头打包 效果更好 数字更小'''
# data_dict = {
#     'file_name': 'xxx老师教学.avi',
#     'file_size': 123132131232342342423423423423432423432,
#     'file_info': '内容很精彩 千万不要错过',
#     'file_desc': '一代神作 私人珍藏'
# }
# import json
# data_json = json.dumps(data_dict)
# print(len(data_json.encode('utf8')))  # 真实字典的长度  228
# res = struct.pack('i', len(data_json.encode('utf8')))
# print(len(res))

"""
黏包问题终极方案
    客户端 
        1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
        2.利用struct模块制作字典的报头
        3.发送固定长度的报头(解析出来是字典的长度)
        4.发送字典数据
        5.发送真实数据     
    服务端
        1.接收固定长度的字典报头
        2.解析出字典的长度并接收
        3.通过字典获取到真实数据的各项信息
        4.接收真实数据长度
"""

黏包代码实战

import socket
import struct
import json


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

sock, addr = server.accept()
# 1.接收固定长度的字典报头
data_dict_head = sock.recv(4)
# 2.根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i', data_dict_head)[0]
# 3.接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)  # 自动解码再反序列化
# 4.获取真实数据的各项信息
# total_size = data_dict.get('file_size')
# with open(data_dict.get('file_name'), 'wb') as f:
#     f.write(sock.recv(total_size))
'''接收真实数据的时候 如果数据量非常大 recv括号内直接填写该数据量 不太合适 我们可以每次接收一点点 反正知道总长度'''
# total_size = data_dict.get('file_size')
# recv_size = 0
# with open(data_dict.get('file_name'), 'wb') as f:
#     while recv_size < total_size:
#         data = sock.recv(1024)
#         f.write(data)
#         recv_size += len(data)
#         print(recv_size)



import socket
import os
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1', 8081))

'''任何文件都是下列思路 图片 视频 文本 ...'''
# 1.获取真实数据大小
file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt')
# 2.制作真实数据的字典数据
data_dict = {
    'file_name': '有你好看.txt',
    'file_size': file_size,
    'file_desc': '内容很长 准备好吃喝 我觉得营养快线挺好喝',
    'file_info': '这是我的私人珍藏'
}
# 3.制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
# 4.发送字典报头
client.send(data_dict_len)  # 报头本身也是bytes类型 我们在看的时候用len长度是4
# 5.发送字典
client.send(data_dict_bytes)
# 6.最后发送真实数据
with open(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt', 'rb') as f:
    for line in f:  # 一行行发送 和直接一起发效果一样 因为TCP流式协议的特性
        client.send(line)
import time
time.sleep(10)

UDP协议(了解)

1.UDP服务端和客户端'各自玩各自的'
2.UDP不会出现多个消息发送合并

并发编程理论

理论非常多 实战很少 但是一定要好好听
研究网络编程其实就是在研究计算机的底层原理及发展史

"""
计算机中真正干活的是CPU 
"""
操作系统发展史
	1.穿孔卡片阶段
  	计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多时候计算机都不工作
    	好处:程序员独占计算机 为所欲为
  		坏处:计算机利用率太低 浪费资源
  2.联机批处理系统
  	提前使用磁带一次性录入多个程序员编写的程序 然后交给计算机执行
    	CPU工作效率有所提升 不用反复等待程序录入
	3.脱机批处理系统
  	极大地提升了CPU的利用率
	总结:CPU提升利用率的过程

多道技术

"""
在学习并发编程的过程中 不做刻意提醒的情况下 默认一台计算机就一个CPU(只有一个干活的人)
"""
单道技术
	所有的程序排队执行 过程中不能重合
多道技术
	利用空闲时间提前准备其他数据 最大化提升CPU利用率
  
多道技术详细
	1.切换
  	计算机的CPU在两种情况下会切换(不让你用 给别人用)
    	1.程序有IO操作
      	输入\输出操作
        	input、time.sleep、read、write
  		2.程序长时间占用CPU	
      	我们得雨露均沾 让多个程序都能被CPU运行一下 

  2.保存状态
  	CPU每次切换走之前都需要保存当前操作的状态 下次切换回来基于上次的进度继续执行
 
"""
开了一家饭店 只有一个服务员 但是同时来了五桌客人
	请问:如何让五桌客人都感觉到服务员在服务他们
		让服务员化身为闪电侠 只要客人有停顿 就立刻切换到其他桌 如此往复
"""

进程理论

进程与程序的区别
	程序:一堆死代码(还没有被运行起来)
	进程:正在运行的程序(被运行起来了)
 
进程的调度算法(重要)
	1.FCFS(先来先服务)
  	对短作业不友好
	2.短作业优先调度
  	对长作业不友好
	3.时间片轮转法+多级反馈队列(目前还在用)
  	将时间均分 然后根据进程时间长短再分多个等级
    等级越靠下表示耗时越长 每次分到的时间越多 但是优先级越低

进程的并行与并发

并行
	多个进程同时执行 必须要有多个CPU参与 单个CPU无法实现并行
并发
	多个进程看上去像同时执行 单个CPU可以实现 多个CPU肯定也可以
 
判断下列两句话孰对孰错
	我写的程序很牛逼,运行起来之后可以实现14个亿的并行量
  	并行量必须要有对等的CPU才可以实现
  我写的程序很牛逼,运行起来之后可以实现14个亿的并发量
  	合情合理 完全可以实现	以后我们的项目一般都会追求高并发
ps:目前国内可以说是最牛逼的>>>:12306

进程的三状态

就绪态
	所有的进程在被CPU执行之前都必须先进入就绪态等待
运行态
	CPU正在执行
阻塞态
	进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态

标签:数据,len,粘包,现象,dict,长度,进程,报头,data
From: https://www.cnblogs.com/bnmm/p/16900404.html

相关文章

  • 黏包现象 并发编程
    目录黏包现象struct模块struct.pack()struct.unpack()文件过大无法打包黏包问题解决黏包实战UDP协议(了解)操作系统发展史穿孔卡片阶段联机批处理系统脱机批处理系统并发......
  • 黏包、udp协议、多道技术、进程并行与并发
    目录黏包现象struct模块黏包代码实战UDP协议(了解)并发编程理论多道技术进程理论进程的并行与并发进程的三状态黏包现象1.服务端连续执行三次recv2.客户端连续执行三次sen......
  • 黏包现象
    黏包现象我们先来看一个案例:server端:server=socket.socket()server.bind(('127.0.0.1',8081))server.listen(5)sock,addr=server.accept()data1=sock.rec......
  • 黏包现象及解决办法
    黏包现象1.服务端连续执行三次recv#服务端收3次2.客户端连续执行三次send#客户端发3次问题:服务端一次性接收到了客户端3次的消息,该现象称为'黏包现象'黏包现......
  • 排查http接口自动重试现象
    http接口自动重试现象的排查标题现象排查过程结论原因一是公司kong的配置:原因二:整体解释现象在公司开发时观察到一个奇怪的现象,一个运行时间较长(1分钟)的http接口会被自动......
  • Linux网络编程 使用socket实现简单服务器——多进程&多线程版本
    1.多进程版服务端#include<stdio.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<signal.h>#include<wait.h>#include......
  • 多任务--进程
    并发:计算机一个内核处理多个任务,其实就是内核在任务间不停的切换,达到好像多个任务同时在执行,实际上每个时刻只有一个任务在执行并行:多个任务利用计算机的多核同时执行,达......
  • 记录一次实验室linux系统的GPU服务器死机排查过程——某显卡满负荷导致内核进程超时导
    在自己没有管理多台高负荷的ubuntu显卡服务器之前,我是万万想不到linux服务器居然也是如此容易死机的。什么每个版本的TensorFlow调用显卡驱动时和内核不兼容,什么系统自动升......
  • 【检查ORACLE阻塞】如果阻塞超过N秒则发短信报警并KILL进程
    检查oracle阻塞,如果阻塞超过N秒则发短信报警阻塞超过分钟后自动kill进程检查oracle分布式事务预留锁,回滚事务/***注意:链接服务器clinicdb要设置RPC和RPCOut为true,否则......
  • python multiprocessing 多进程
    1获取进程id当我们运行py文件时,该程序的运行就是一个进程,如果在该进程中又创建了其他进程,那么该进程就是主进程,创建的其他进程就是子进程。下面我们通过通过os库中的方法......