昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示。今天也会稍微讲解一下基于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),该函数返回一个元组

29、粘包现象(struct模块)的更多相关文章

  1. python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议

    socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...

  2. 8.7 day28 网络编程 socket套接字 半连接池 通信循环 粘包问题 struct模块

    前置知识:不同计算机程序之间的数据传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...

  3. python网络编程-socket套接字通信循环-粘包问题-struct模块-02

    前置知识 不同计算机程序之间数据的传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...

  4. (day27)subprocess模块+粘包问题+struct模块+ UDP协议+socketserver

    目录 昨日回顾 软件开发架构 C/S架构 B/S架构 网络编程 互联网协议 socket套接字 今日内容 一.subprocess模块 二.粘包问题 三.struct模块 四.UDP 五.QQ聊天室 ...

  5. 2、粘包现象(struct模块)

    昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示.今天也会稍微讲解一下基于udp的套接字. 一.基于udp的套接字 udp是无链接的,先启动哪一端都不会报错 ud ...

  6. Python网络编程(2)-粘包现象及socketserver模块实现TCP并发

    1. 基于Tcp的远程调用命令实现 很多人应该都使用过Xshell工具,这是一个远程连接工具,通过上面的知识,就可以模拟出Xshell远程连接服务器并调用命令的功能. Tcp服务端代码如下: impo ...

  7. socket模块粘包现象理解以及解决思路

    粘包现象: 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的 ...

  8. Python之网路编程之粘包现象

    一.什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生 如果发生了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了 首先需要掌握一个socket收发消息的原 ...

  9. python3全栈开发-什么是粘包、粘包现象、如何解决粘包

    一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.decode( ...

随机推荐

  1. Evaluation map and reflexive space

    For a normed space \(X\), an isometric isomorphism can be defined from \(X\) to its second dual spac ...

  2. Python_collections_OrderedDict有序字典部分功能介绍

    OrderedDict():实现字典的固定排序,是字典的子类 import collections dic = collections.OrderedDict() dic['k1'] = 3 dic[ ...

  3. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-3项目架构说明

    本文目录1. 摘要2. 框架介绍 3. 权限管理之多一点说明4. 总结 1.  摘要 NCMVC角色权限管理框架是由最近练习Net Core时抽时间整理的系统,后续能不能发展成一个cms还要看朋友们是 ...

  4. Codeforces 986D Perfect Encoding FFT 分治 高精度

    原文链接https://www.cnblogs.com/zhouzhendong/p/9161557.html 题目传送门 - Codeforces 986D 题意 给定一个数 $n(n\leq 10 ...

  5. 写面向对象的新Process

    import multiprocessing class mypro(multiprocessing.Process): def  __init__(self,a,b): super().__init ...

  6. 1301 邻值查找(set 平衡树 | 链表)

    描述 给定一个长度为 n 的序列 A,A 中的数各不相同.对于 A 中的每一个数 A_i,求: min(1≤j<i) ⁡|A_i-A_j| 以及令上式取到最小值的 j(记为 P_i).若最小值点 ...

  7. zoj 1002 Fire Net 碉堡的最大数量【DFS】

    题目链接 题目大意: 假设我们有一个正方形的城市,并且街道是直的.城市的地图是n行n列,每一个单元代表一个街道或者一块墙. 碉堡是一个小城堡,有四个开放的射击口.四个方向是面向北.东.南和西.在每一个 ...

  8. 李宏毅机器学习笔记5:CNN卷积神经网络

    李宏毅老师的机器学习课程和吴恩达老师的机器学习课程都是都是ML和DL非常好的入门资料,在YouTube.网易云课堂.B站都能观看到相应的课程视频,接下来这一系列的博客我都将记录老师上课的笔记以及自己对 ...

  9. String、StringBuffer、StringBuilder的比较

    看String类的定义:public final class String...{private final char value[];} 看AbstractStringBuilder类的定义:abs ...

  10. Android动画曲线库AndroidEasingFunctions

    Android动画曲线库AndroidEasingFunctions AndroidEasingFunction是基于Easing Function(缓动函数)的Android动画曲线库.它提供了九大 ...