昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示。今天也会稍微讲解一下基于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模块)的更多相关文章

  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. 29、粘包现象(struct模块)

    昨天我们所做的套接字是有漏洞的,它会出现粘包现象,没有发现这个问题的我们今天会进行演示.今天也会稍微讲解一下基于udp的套接字. 本篇导航: 基于udp的套接字 粘包现象 粘包 解决粘包方法 stru ...

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

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

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

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

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

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

  9. tcp的粘包现象与解决方案

    粘包现象: 粘包1:连续的小包,会被优化机制给合并 粘包2:服务端一次性无法完全就收完客户端发送的数据,第二再次接收的时候,会接收到第一次遗留的内容 模拟一个粘包现象 服务端 import socke ...

随机推荐

  1. Symbol() 的使用方法

    简介:ES5的对象属性名都是字符串,这就很容易造成属性名的冲突,比如一个项目很庞大,又不是一个人开发 的,就有可能会造成变量名冲突,如果有一个独一无二的名字就好了,这样就能从根本上防止属性名冲突.这就 ...

  2. jpg、jpeg、png... 的区别

    对于做设计这一行的人来说,这几个图片格式是最常用的,也是最常见的,几乎每一天都要与他们打交道. 刚刚入门的新人通常不知道在什么地方如何使用他们或者说如何更有效的使用他们. 那他们到底是有什么区别?(一 ...

  3. JavaWeb 后端 <八> 之 JDBC基礎(全)

    一.JDBC简介 1.JDBC是SUN公司为了简化操作数据推出一套规范.数据库厂商的驱动就是对JDBC的实现. 2.Java Data Base Connectivity(java数据库连接),它主要 ...

  4. 扩展SQLite使其能从apk文件中读取db

    游戏中会大量使用到配置文件,每个项目组根据自己不同的需求会选择不同的存储格式,比如使用Json或者SQLite来存储数据.此处我们只对使用SQLite的情况来做讨论.一般情况下会选择把它放在可读写目录 ...

  5. MongoDB 系列(一) C# 简易入门封装

    之前写过一篇关于MongoDB的封装 发现太过繁琐 于是打算从新写一篇简易版 1:关于MongoDB的安装请自行百度,进行权限认证的时候有一个小坑,3.0之后授权认证方式默认的SCRAM-SHA-1模 ...

  6. 开始编写寄几的 CSS 基础库

    前言 在现在的互联网业务中,前端开发人员往往需要支持比较多的项目数量.很多公司只有 1-2 名前端开发人员,这其中还不乏规模比较大的公司.这时前端同学就需要独挡一面支持整个公司上下的前端业务,项目如流 ...

  7. sqlserver提高篇

    Microsoft SQL Server2008复习提高 一.Microsoft SQL Server 系统的体系结构 1.Microsoft SQL Server2008由4个主要的部分组成,即4个 ...

  8. TestFlight使用方法

    1 说明 目前IOS版本APP上架前,很少有项目对上架包进行测试,而上架包是人工打包的,很有可能之前的测试包没有问题,可打出的上架包出现问题.苹果市场是有提供对上架包内部测试的,可以对上架包进行测试, ...

  9. SQL注入的各种类型的检测方式

    #SQL注入各个类型检测方式 http://127.0.0.1/day6/1.php?id=1 union select 1,name,pass from admin 数字型 数字型不用特意加字符,直 ...

  10. vue-axios配置token,上传图片

    vue1.0用 vue-resource 目前不维护了 vue2.0用 axios 使用 1.安装axios, npm install axios 2.在main.js中配置 import axios ...