一、复习

# ip地址:一台机器在网络上的位置
# 公网ip 私网ip
# TCP协议:可靠,面向连接的,耗时长#三次握手#四次挥手
# UDP协议:不可靠,无连接,效率高
# ARP协议:通过ip找mac的过程
# ip协议属于网络osi中的网络层
# TCP协议和UDP协议属于传输层
# arp协议属于数据链路层

二、黏包(第一条和第二条数据合并发送)

tcp:不会丢包会黏包

udp:会丢包不会黏包

tcp黏包案例:

server_tcp端:

#server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()conn,addr=sk.accept()
while True:cmd=input('>>>')conn.send(cmd.encode('utf-8'))ret1=conn.recv(1024).decode('utf-8')ret2 = conn.recv(1024).decode('utf-8')print(ret1)print(ret2)conn.close()
sk.close()

client_tcp端:

#client端
import socket
import subprocess
sk=socket.socket()sk.connect(('127.0.0.1',8090))
while True:cmd=sk.recv(1024).decode('utf-8')ret=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out='stdout:'+(ret.stdout.read()).decode('gbk')std_err='stderr:'+(ret.stderr.read()).decode('gbk')sk.send(std_out.encode('utf-8'))sk.send(std_err.encode('utf-8'))
sk.close()

运行结果:

udp丢包案例:(udp起server和client)

server_udp端:


import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8090)
sk.bind(ip_port)
msg, addr = sk.recvfrom(10240)
while True:cmd = input('>>>')if cmd == 'q':breaksk.sendto(cmd.encode('utf-8'),addr)msg1,addr = sk.recvfrom(20480)print(msg1.decode('utf-8'))msg2, addr = sk.recvfrom(20480)print(msg2.decode('utf-8'))
sk.close()

client_udp端:

import socket
import subprocess
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8090)
sk.sendto(b'hi',ip_port)
while True:cmd,addr = sk.recvfrom(1024)cmd = cmd.decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = 'stdout:'+(ret.stdout.read()).decode('gbk')std_err = 'stderr:'+(ret.stderr.read()).decode('gbk')print(std_out)print(std_err)sk.sendto(std_out.encode('utf-8'),addr)sk.sendto(std_err.encode('utf-8'),addr)
sk.close() #不会黏包,会丢包

运行结果:

运行结果显示,如果udp的接收数据量大小满足发送数据量大小,那么就不会丢包,若是不满足发送数据量大小,则就会报错。而不是丢包

三 、黏包的触发

情况一:发送方的缓存机制:发送端要等缓冲区满才发送出去。造成黏包(发送数据时间间隔很短,数据很小,会合并一起造成粘包)

如:

#server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
ret=conn.recv(12)
ret2=conn.recv(12)
print(ret)
print(ret2)
conn.close()
sk.close()#关闭时会发空消息
# 多个send小的数据连在一起,可能会发生黏包现象,是tcp内部的优化算法引起的
#client端
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
sk.send(b'hello')
import time
# time.sleep(0.01)
sk.send(b'world')
sk.send(b'dd')
sk.close()

运行结果:(有时会黏包有时不会)

情况二:接收方的缓冲机制

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接收了一小部分,服务器下次再接收时还是从缓冲区拿上次遗留的数据,产生粘包)

如:

# server端
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()conn,addr=sk.accept()
ret=conn.recv(2)
ret2=conn.recv(10)
print(ret)
print(ret2)
conn.close()
sk.close()# 黏包的本职问题:不知道发送数据的长度
#  连续的小数据包会被合并
# client端
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8080))sk.send(b'hello,egg')
sk.close()

运行结果:

总结

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

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

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

四、黏包的解决方案

解决方案一:

问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

# server
# server 下发命令 给client
import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
while True:cmd=input(">>>")if cmd=='q':conn.send(b'q')breakconn.send(cmd.encode('gbk'))num=conn.recv(1024).decode('utf-8')conn.send(b'ok')res=conn.recv(int(num)).decode('gbk')print(res)
conn.close()
sk.close()
# client
# 接收server端的命令后在自己的机器上执行
import socket
import subprocesssk=socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd=sk.recv(1024).decode('gbk')if cmd=='q':breakres=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out=res.stdout.read()std_err=res.stderr.read()sk.send(str((len(std_out)+len(std_err))).encode('utf-8'))sk.recv(1024)  #oksk.send(std_out)sk.send(std_err)
sk.close()# 好处:确定我到底要接收多大的数据
# recv的大小一般不超过4096
# 要在文件中配置一个配置项:就是每次recv的大小 buffer=4096
# 当我们发送大数据量的时候,要明确的告诉接收方要发送多大数据以便接收方能准确接收到所有数据
# 多用在文件传输过程中#大文件的传输一定是按照字节读 每次读固定的字节# 传输的过程中 一边读一边传 接收端:一边收一边写# send大文件之前,告知大小。大小-4096-4096....-->0  文件传输完# recv大文件,先接受大小。再recv2048.不会丢 大小-2048-2048  -->0  文件接收完
# 不好的地方:多了一次交互
# 5个g数据
# send 和sendto在超过一定范围后,都会报错

运行结果:

存在的问题: 程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

解决方案进阶

刚刚的方法,问题在于我们我们在发送

我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes

>>> struct.pack('i',1111111111111)struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

# server 下发命令 给client
import socket
import struct
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr=sk.accept()
while True:cmd=input(">>>")if cmd=='q':conn.send(b'q')breakconn.send(cmd.encode('gbk'))num=conn.recv(4) #4  2048num = struct.unpack('i', num)[0]res=conn.recv(int(num)).decode('gbk')  #2048print(res)
conn.close()
sk.close()
# 连续send两个小数据
# 两个recv,第一个recv特别小
# 远程执行命令的程序:ipconfig--> 2000,只接收1024.就会缓存,下次继续接收上次未接收完的数据#连续send两个小数据2+8=10
# 2
# 8
#两个recv,第一个recv特别小
# recv(数据的长度)
# 接收server端的命令后在自己的机器上执行
import socket
import subprocess
import structsk=socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd=sk.recv(1024).decode('gbk')if cmd=='q':breakres=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out=res.stdout.read()std_err=res.stderr.read()len_num=len(std_out)+len(std_err)num_by=struct.pack('i',len_num)sk.send(num_by)  # 4  2048sk.send(std_out)  # 1024sk.send(std_err)   #1024
sk.close()

运行结果:

五、ftp发送视频

sever:

import socket
import struct
import json
buffer=1024
# ip地址和端口号需要写在配置文件中
sk=socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()conn,addr=sk.accept()
# 接收
head_len=conn.recv(4)   # 报头长度
head_len=struct.unpack('i',head_len)[0]
head_json=conn.recv(head_len).decode('utf-8')
head=json.loads(head_json)
filesize=head['filesize']
with open(head['filename'],'wb') as f:while filesize:print(filesize)if filesize>=buffer:content=conn.recv(buffer)f.write(content)filesize-=bufferelse:content=conn.recv(filesize)f.write(content)filesize=0break
f.close()
conn.close()
sk.close()
#发送端
import socket
import os
import json
import struct
sk=socket.socket()
sk.connect(('127.0.0.1',8080))
# 发送文件
buffer=2046
# 改成4096文件大小会变:发送和接收时间不匹配
# 读操作快,写速度慢。缓冲数据多head={'filepath':r'E:\test','filename': '[反贪风暴3]BD国语.mp4','filesize':None}
file_path=os.path.join(head['filepath'],head['filename'])
filesize=os.path.getsize(file_path)
head['filesize']=filesize
json_head=json.dumps(head) #字典转成了字符串
byte_head=json_head.encode('utf-8')  # 字符串转成了bytes
head_len=len(byte_head)   # 报头的长度
pack_len=struct.pack('i',head_len)
sk.send(pack_len)  # 先发报头的长度
sk.send(byte_head)  # 再发送bytes类型的报头
with open(file_path,'rb') as f:while filesize:print(filesize)if filesize>=buffer:content=f.read(buffer)   # 每次读取出来的大小sk.send(content)filesize-=bufferelse:content=f.read(filesize)sk.send(content)filesize=0break
f.close()
sk.close()

参考自https://www.cnblogs.com/Eva-J/articles/8244551.html#_label5

python网络编程2-黏包问题相关推荐

  1. python网络编程之黏包问题

    一 : 概念 发送端发送数据,接收端不知道应该如何去接收,造成的一种数据混乱的现象. 二 : 起因 在TCP协议中,存在两个机制: 合包机制 : 通过nagle算法,将多次连续发送且间隔较小的数据,打 ...

  2. 网络编程- 解决黏包现象方案一(六)

    队列 利用队列的思路来解决黏包问题 总结 转载于:https://www.cnblogs.com/mys6/p/10797907.html

  3. python 网络编程 struct解包时报错 struct.error: unpack requires a buffer of 4 bytes

    报错信息 D:\Donta_tensorflow-yolov3\python\python.exe D:/10_gitee/network_programming/避障程序信号及图像接收端/20100 ...

  4. 【Python】网络编程--解决粘包问题--简单版:

    网络编程–解决粘包问题–简单版: 客户端: import struct import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STRE ...

  5. python网络编程--socket简单实现

    python网络编程                                                                                           ...

  6. python网络编程(苦肝一夜,近万字)

    文章目录 一.TCP/IP简介 二.网络设计模块 1.Socket简介 2.python中的socket模块,使用该模块建立服务器需要6个步骤. 1.创建socket对象. 2.将socket绑定(指 ...

  7. python网络编程证书_《Python网络编程基础》笔记

    python网络编程基础 ================== Author: lujun9972 Date: 2013-03-08 22:29:20 CST Table of Contents == ...

  8. python/socket编程之粘包

    python/socket编程之粘包 粘包 只有TCP有粘包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 发 ...

  9. [转载] PYTHON 网络编程

    参考链接: Python | 使用openpyxl在Excel文件中进行三角运算 Socket 编程实战 文章目录 Socket库 0.1. 什么是 Socket? 0.2. socket()函数 0 ...

  10. python网络编程内容_图解Python网络编程

    Python Python开发 Python语言 图解Python网络编程 本篇索引 (1)基本原理 本篇指的网络编程,仅仅是指如何在两台或多台计算机之间,通过网络收发数据包:而不涉及具体的应用层功能 ...

最新文章

  1. php发布商品信息逻辑,php – 逻辑思考一个数据库结构:为用户发布的东西添加“标签” – 一个单独的表或……?...
  2. 【青少年编程】【四级】数字反转
  3. linux 创建虚拟IP
  4. LeetCode周赛191
  5. [LeetCode] Binary Tree Postorder题解
  6. 推荐一个以动画效果显示github提交记录的黑科技工具:Gource
  7. RK方案 manifest.xml编译问题,并解决方案
  8. 印度不只有开挂火车,还有一开挂的数学家,凭一己之力单刷数学界
  9. 天天在用消息队列,却还不知道为啥要用 MQ ,这就尴尬了
  10. 外贸独立站VS第三方平台?
  11. 为什么这款 CPU 在亚马逊上卖得最火?
  12. python 如何运行程序
  13. 2021年12月中国A股钢铁行业上市企业市值排行榜:行业总市值较11月增长1.18%,金洲管道增幅最大(附月榜TOP44详单)
  14. 中国崛起让西方乱了阵脚
  15. android 测光模式,安卓手机里的专业模式究竟该怎么拍?
  16. 华为手机助手 android,华为手机助手(安卓版)
  17. 自学编程,痛并快乐着
  18. 微信小程序--实现番茄钟功能
  19. 计算机机房监控系统上海,上海机房监控-上海机房环境监控系统
  20. 强一致性 弱一致性 最终一致性

热门文章

  1. Spring初始化:org.springframework.we...ContextLoaderListener的作用
  2. 乐高机器人教室布置图片大全_圣诞节手抄报内容简单图片漂亮
  3. 计算机一级某学校师资情况表,2011年计算机一级考试MS OFFICE上机考试指导
  4. android 命令使用详解,Android下pm 命令详解
  5. java阻塞锁_java – 阻止锁与非阻塞锁
  6. 怎么修改ppt的虚线间隔_还有一小时下班,领导交给我一份ppt,做不完不许走!...
  7. python csv文件复制时的编码问题_使用python读取CSV文件时的编码问题
  8. java 密码生成器_Java课程设计-随机密码生成器
  9. slope one 推荐算法python 代码_协同推荐算法实践之Slope One的介绍(转)
  10. java中execquery(),javaee登录界面