2、粘包现象(struct模块)
昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示。今天也会稍微讲解一下基于udp的套接字。
本篇导航:
一、基于udp的套接字
udp是无链接的,先启动哪一端都不会报错
udp服务端:
ss = socket() #创建一个服务器的套接字
ss.bind() #绑定服务器套接字
while True : #服务器无限循环
cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close() # 关闭服务器套接字
udp客户端:
cs = socket() # 创建客户套接字
while True : # 通讯循环
cs.sendto()/cs.recvfrom() # 对话(发送/接收)
cs.close() # 关闭客户套接字
1、udp套接字简单实例
服务端:
from socket import * udp_ss=socket(AF_INET,SOCK_DGRAM)
udp_ss.bind(('127.0.0.1',8080)) while True:
msg,addr=udp_ss.recvfrom(1024)
print(msg,addr)
udp_ss.sendto(msg.upper(),addr)
客户端:
from socket import * udp_cs=socket(AF_INET,SOCK_DGRAM) while True:
msg=input('>>: ').strip()
if not msg:continue
udp_cs.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
msg,addr=udp_cs.recvfrom(1024)
print(msg.decode('utf-8'),addr)
2、模拟聊天(由于udp无连接,所以可以同时多个客户端去跟服务端通信)
服务端:
from socket import * udp_ss=socket(AF_INET,SOCK_DGRAM)
udp_ss.bind(('127.0.0.1',8081)) while True:
msg,addr=udp_ss.recvfrom(1024)
print('来自[%s]的一条消息:%s' %(addr,msg.decode('utf-8')))
msg_b=input('回复消息: ').strip()
udp_ss.sendto(msg_b.encode('utf-8'),addr)
客户端1:
from socket import * udp_cs = socket(AF_INET,SOCK_DGRAM) while True :
msg = input('请输入消息,回车发送: ').strip()
if msg == 'quit' : break
if not msg : continue
udp_cs.sendto(msg.encode('utf-8'),('127.0.0.1',8081)) back_msg,addr = udp_cs.recvfrom(1024)
print('来自[%s]的一条消息:%s' %(addr,back_msg.decode('utf-8'))) udp_cs.close()
客户端2:
from socket import * udp_cs = socket(AF_INET,SOCK_DGRAM) while True :
msg = input('请输入消息,回车发送: ').strip()
if msg == 'quit' : break
if not msg : continue
udp_cs.sendto(msg.encode('utf-8'),('127.0.0.1',8081)) back_msg,addr = udp_cs.recvfrom(1024)
print('来自[%s]的一条消息:%s' %(addr,back_msg.decode('utf-8'))) udp_cs.close()
客户端3:
... ...
因为不同的客户端是向同一个服务端发送信息所以客户端的代码都相同,如果兴趣的可以用几台电脑来测试(电脑需要联网)
二、粘包现象
先做粘包现象:
服务端:
from socket import *
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr=phone.accept() data1=conn.recv(1024)
print('data1: ',data1)
data2=conn.recv(1024)
print('data2:',data2)
客户端:
from socket import *
phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) phone.send('hello'.encode('utf-8'))
phone.send('world'.encode('utf-8'))
我们再将上个随笔里的ssh例子拿出来(先执行 ipconfig /all 再执行 dir 看结果)
客户端:
from socket import *
import subprocess
cs=socket(AF_INET,SOCK_STREAM)
cs.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
cs.bind(('127.0.0.1',8082))
cs.listen(5) print('starting...')
while True:
conn,addr=cs.accept()
print('-------->',conn,addr) while True:
try:
cmd=conn.recv(1024)
res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=res.stdout.read()
stderr=res.stderr.read() #发送命令的结果
conn.send(stdout+stderr)
except Exception:
break
conn.close() #挂电话
cs.close() #关机
服务端:
from socket import *
ss=socket(AF_INET,SOCK_STREAM) #买手机
ss.connect(('127.0.0.1',8082)) #绑定手机卡 #发,收消息
while True:
cmd=input('>>: ').strip()
if not cmd:continue
ss.send(cmd.encode('utf-8'))
cmd_res=ss.recv(1024)
print(cmd_res.decode('gbk'))
ss.close()
注意:
subprocess模块的结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码
三、粘包
注意:只有TCP有粘包现象,UDP永远不会粘包,首先需要掌握一个socket收发消息的原理
应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。
例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
两种情况会粘包:
1、发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
拆包的发生情况:
当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。
四、解决粘包方法
粘包现象中第一个现象解决:
解决一:(需要知道每次发过来的数据大小 不现实)
from socket import *
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr=phone.accept() data1=conn.recv(10)
print('data1: ',data1)
data2=conn.recv(4)
print('data2:',data2)
服务端
from socket import *
phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) phone.send('helloworld'.encode('utf-8'))
phone.send('egon'.encode('utf-8'))
客户端
解决二:
from socket import *
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr=phone.accept() data1=conn.recv(1024)
print('data1: ',data1)
data2=conn.recv(1024)
print('data2:',data2)
服务端
from socket import *
import time
phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) phone.send('hello'.encode('utf-8'))
time.sleep(5)
phone.send('world'.encode('utf-8'))
客户端
ssh例子问题解决:
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据
from socket import *
import subprocess
import struct
ss=socket(AF_INET,SOCK_STREAM)
ss.bind(('127.0.0.1',8082))
ss.listen(5) print('starting...')
while True:
conn,addr=ss.accept()
print('-------->',conn,addr) while True:
try:
cmd=conn.recv(1024)
res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=res.stdout.read()
stderr=res.stderr.read() #先发报头(转成固定长度的bytes类型)
header = struct.pack('i',len(stdout)+len(stderr))
conn.send(header)
#再发送命令的结果
conn.send(stdout)
conn.send(stderr)
except Exception:
break
conn.close()
ss.close()
服务端
from socket import *
import struct
cs=socket(AF_INET,SOCK_STREAM)
cs.connect(('127.0.0.1',8082)) while True:
cmd=input('>>: ').strip()
if not cmd:continue cs.send(cmd.encode('utf-8'))
#先收报头
header_struct=cs.recv(4)
unpack_res = struct.unpack('i', header_struct)
total_size=unpack_res[0] #再收数据
recv_size=0 #10241=10240+1
total_data=b''
while recv_size < total_size:
recv_data=cs.recv(1024)
recv_size+=len(recv_data)
total_data+=recv_data
print(total_data.decode('gbk'))
cs.close()
客户端
五、struct模块(了解)
该模块可以把一个类型,如数字,转成固定长度的bytes
struct.pack('i',11111111)
#struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型)。它的函数原型为:struct.unpack(fmt, string)。
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。
2、粘包现象(struct模块)的更多相关文章
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
- 8.7 day28 网络编程 socket套接字 半连接池 通信循环 粘包问题 struct模块
前置知识:不同计算机程序之间的数据传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...
- python网络编程-socket套接字通信循环-粘包问题-struct模块-02
前置知识 不同计算机程序之间数据的传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...
- (day27)subprocess模块+粘包问题+struct模块+ UDP协议+socketserver
目录 昨日回顾 软件开发架构 C/S架构 B/S架构 网络编程 互联网协议 socket套接字 今日内容 一.subprocess模块 二.粘包问题 三.struct模块 四.UDP 五.QQ聊天室 ...
- 29、粘包现象(struct模块)
昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示.今天也会稍微讲解一下基于udp的套接字. 本篇导航: 基于udp的套接字 粘包现象 粘包 解决粘包方法 stru ...
- Python网络编程(2)-粘包现象及socketserver模块实现TCP并发
1. 基于Tcp的远程调用命令实现 很多人应该都使用过Xshell工具,这是一个远程连接工具,通过上面的知识,就可以模拟出Xshell远程连接服务器并调用命令的功能. Tcp服务端代码如下: impo ...
- socket模块粘包现象理解以及解决思路
粘包现象: 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的 ...
- python3全栈开发-什么是粘包、粘包现象、如何解决粘包
一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.decode( ...
- tcp的粘包现象与解决方案
粘包现象: 粘包1:连续的小包,会被优化机制给合并 粘包2:服务端一次性无法完全就收完客户端发送的数据,第二再次接收的时候,会接收到第一次遗留的内容 模拟一个粘包现象 服务端 import socke ...
随机推荐
- 算法起步之kmp算法
[作者Idlear 博客:http://blog.csdn.net/idlear/article/details/19555905] 这估计是算法连载文章的最后几篇了,马上就要 ...
- Java系列 - 用Java8新特性进行Java开发太爽了
本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome/ 前言 从开始写博客到现在已经过去3个 ...
- 响应式移动端去除css的hover和jq的hover还有input在苹果下的默认样式
去除css的hover: /*直接给body添加ontouchstart*/ <body ontouchstart> 去除jq的hover: var winW01 = $(window). ...
- 终于等到你!MobileTest免费公测,华为带你走出安卓适配大坑
一.安卓适配之痛真的无解吗? Android平台的诞生对智能手机的普及功不可没,但设备繁多.品牌众多.版本各异,芯片.摄像头.分辨率不统一等等,这些都逐渐成为Android系统发展的障碍,碎片化严重不 ...
- PB程序源码文件结构 pbl文件 pbd文件
最近公司给了一套PB的源码,一个8.0,一个9.0,让给一个客户做软件整合,之前只听过PB看过别人写代码,为了快速上手,了解了一下PB的文件,记录如下:pbl为pb源码文件 pbd为程序编译后的文件 ...
- HTTP消息中Header头部信息整理
1.HTTP请求方式 GET 向Web服务器请求一个文件 POST 向Web服务器发送数据让Web服务器进行处理 PUT 向Web服务器发送数据并存储在Web服务器内部 HEAD 检查一个对象是否存在 ...
- 小哈学Python第四课--运算符
运算符: 1.算数运算符: 2. 比较运算符 3.赋值运算符 4. 逻辑运算符: 5. 成员运算:
- MySQL中的联合索引学习教程
MySQL中的联合索引学习教程 这篇文章主要介绍了MySQL中的联合索引学习教程,其中谈到了联合索引对排序的优化等知识点,需要的朋友可以参考下 联合索引又叫复合索引.对于复合索引:Mysql从左到 ...
- 工程经验总结之吹水"管理大境界"
1.个人认为项目管理最核心的能力是预见风险和快速解决风险的能力. 从实践来看,没有百分百的完美计划,任何计划都有出现偏差的可能,或者说计划总是不会按照最初的设定去完美执行的. 项目经理存在的主要价值就 ...
- maven-编译速度优化
故障描述: 公司搭建了一个新jenkins持续集成环境,jenkins构建job时间越来越长. 原因分析: 系统CPU限制:判断依据,构建中查看日志 tail -f /var/log/messages ...