socket基于TCP(粘包现象和处理)
6socket套接字
socket网络套接字
什么是socket
- 每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
- sock是处于应用层和传输层之间的抽象层,1他是一组操作起来非常简单的接口(接收数据)此接口接收数据之后,交由操作系统
- 为什么存在socket抽象层
- 如果直接与操作系统数据交互非常麻烦,繁琐,socket对这些繁琐的操作高度的封装和简化
- socket在python中就是一个模块。
7基于TCP协议的socket简单的网络通信
import socket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
一对一
简单的socket抽象层 定义一些简单的接口 留下一个接口
1.socket处于应用层和传输层之间,提供了一些简单的接口,避免与操作系统直接对接,省去了相当繁琐复杂的过程
2.socket 在python中就是一个模块
不考虑缓存区的大小 发送是没有限制的虽然不限制但是不会一次性发送过多
有收必有发,send发--recv接收
AF_UNIX
基于套接字家族的名字:
unix一切皆文件,基于文件的套接字(dicheng1wen),运行同一机器,通过访问同一个文件系统完成通信
AF_INET(应用最广泛的一个)
(AF_INET6 ipv6)
encode转码 类字节 base()转码类字节
执行的时候端口被占有 长连接
如果进入需要等待
收发是成对的 有发必有收,
遇到一个recv 就阻塞 写两个就卡住
base类型:
ASCII字符:在字符串前面b' '
非ASCII字符:encode 转化成 bytes类型
报错类型
ConnectionRefusedError #服务端没开 报错
ConnectionResetError #服务端崩溃
ConnectionAbortedError#客户端退出
单一
#server端
import socket#调用socket
sk=socket.socket()#使用socket里面的socket
#相当于买手机
sk.bind(('127.0.0.1',8080))#bind(里面放入一个元组('ip',端口号))绑定手机卡
sk.listen(5)#监听 等着有人给我打电话#阻塞 5是允许5个人 链接我,剩下的链接也可以链接,等待
#最大等待链接数5个
#允许5个进入半链接池
#()是和电脑性能有关 #
conn,addr=sk.accept()#接收到别人的电话 connection 连接管道 address地址 来电显示 #阻塞
ret=conn.recv(1024)#收听别人说话的长度#1024个字节#等消息
print(ret)
ret=conn.recv(1024)#等消息
print(ret.decode('utf-8'))#要解码
conn.send(b'1')#和别人说话,必须传一个bytes类型
conn.close()#挂电话#关闭链接
sk.close()#关手机#关闭socket对象
##重用ip地址
#client端
import socket
sk=socket.socket()#买手机
“”“
sk.setsockopt(socket.SQL_SOCKET,socket.SO_REUSEADDR,1)#允许socket重用避免服务重启的时候 address already in use
”“”“
sk.connect(('127.0.0.1',8080))#拨别人的号
sk.send(b'hello')#发送消息
sk.send('是我的'.encode('utf-8'))#中文要转码成为bit
ret=sk.recv(1024)
print(ret)
conn.close()#挂电话
sk.close()#关手机
链接+循环通信
#client
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#使用网络传输 tcp
try:
phone.connect(('127.0.0.1', 8848))
except ConnectionRefusedError:
exit('服务端没开')
while 1:
try:
data=input('强输入')
phone.send(data.encode('gbk'))
if not data:
print('不能为空')
elif data.upper() == 'Q':
print('您退出了')
break
else:
from_server_data=phone.recv(1024)
print(f'来自服务的消息:{from_server_data.decode("gbk")}')
except ConnectionResetError:
print('服务端崩溃了连不上')
break
except ConnectionAbortedError:
pass
phone.close()
#server
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8848))
#开机监听
while 1:#循环通信
phone.listen()
print('开启通信')
conn,addr=phone.accept()#阻塞
# s = input('请输入')
# conn.send(s.encode('utf-8'))
print(conn,addr)
#from_client_data.upper()b
while 1:
try:
from_client_data = conn.recv(1024) # 至多接受1024个字节 阻塞
if from_client_data.upper()==b'Q':
print('客户正常退出了')
print(f'来自可无端的消息:{from_client_data.decode("utf-8")}')
from_server_data=input('》》').strip().encode('utf-8')
conn.send(from_server_data)
except ConnectionResetError:
print('客户端强行断开了')
break
远程命令
传输的就是cmd是 gbk类型的base
半连接池 listen
9.tcp 实例:远程执行命令
subprocesss#远程执行命令
# shell: 命令解释器,相当于调用cmd 执行指定的命令。
# stdout:正确结果丢到管道中。
# stderr:错了丢到另一个管道中。
# windows操作系统的默认编码是gbk编码。
#server端
import socket#引用socket模块
import subprocess#引用远程执行模块
import struct
server_config=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#执行tcp默认不写
server_config.bind(('127.0.0.1',8848))#写一个地址
while 1:
server_config.listen(5)
print('开始通信')
conn,addr=server_config.accept()
while 1:
try:
form_client_data=conn.recv(1024)
if form_client_data.upper()==b'Q':
#设置一个退出的按钮
print('客户端退出了')
else:
obj=subprocess.Popen(form_client_data.encode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result=obj.stdout.read()+obj.stderr.subprocess.read()
#拼接到一起进行输出 如果输入的是错的正确的命令为空,如果输入的是正确的 错误的信息为空
total_size=len(result)
head_bytes=struct.pack('i',total_size)
conn.send(head_bytes)
conn.send(result)
except ConnectionAbortedError:
print(f'客户端{addr}断开')
break
conn.close()
server_config.close()
#client #客户端
import socket
import struct
client_cofig=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#AF_INET#SOCK_STREAM
client_cofig.connect(('127.0.0.1',8848))
while 1:
s=input('1212')
client_cofig.send(s.encode('utf-8'))
head.client=client_cogif.recv(4)
total.size=struct.unpack('i',head.client)
total.ppt=b''
while len(total.ppt)<total.size:
total.ppt+=client.recv(1024)
print(total.ppt.decode('gbk'))
client_cofig.close()
10.粘包现象
11.操作系统的缓存区
1.为什么出现粘包
缓冲区
缓冲区优点:
1.暂时存储一些数据
缓冲区存在 如果你的网络波动,保证数据的收发稳定,匀速
缓冲区一般大小是8k
缺点:造成了粘包现象之一
防止数据丢失
不会一次性发很多 缓冲区满了就先不发
没收完的东西存储在了缓冲区里 等下次取直接取 因为取的时间比发的时间快
send完直接recv从缓冲区取 取出剩余
12.什么情况下出现粘包
1.出现粘包的情况
粘包只会出现在tcp中
2.收发的本质
不一定要一收一发
1.连续短暂的send多次(数据量很小),数据会统一发送出去(因为会等缓冲区满了之后封包才发送出去s)
会有一个停留 nigle算法 连续send多次
#client
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'll')
# phone.send(b'o')
#
#
# phone.close()
# Nigle算法
#server
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
# conn.close()
# phone.close()
2.send数量过大 send超过限制对方recv接受的数量时 会暂存缓存区。
第二次recv时会将上一次没有recv完的剩余数据
收发现象
发多次收一次
发一次收多次
13.low解决粘包现象
如何解决 服务端发一次数据 10000字节,
客户端接受数据时,循环接受,1每次(至多)接收1024个字节直至将所有的字节全部接收完毕,将接收的数据拼接在一起,最后解码
遇到的问题:recv次数无法确定
你发送总数据之前,先给我发一个总数据的长度:len()
然后在发送总数据
客户端:先接收一个长度接收字节因为有粘包,50000个字节
encode转字符串 int转出数字
struct模块 加个类似tcp 固定head头 是固定长度头 之后解包
然后再循环recv 控制循环条件是反解出来的在服务端制作的长度
14.recv工作原理
'''
源码解释:
Receive up to buffersize bytes from the socket.
接收来自socket缓冲区的字节数据,
For the optional flags argument, see the Unix manual.
对于这些设置的参数,可以查看Unix手册。
When no data is available, block untilat least one byte is available or until the remote end is closed.
当缓冲区没有数据可取时,recv会一直处于阻塞状态,直到缓冲区至少有一个字节数据可取,或者远程端关闭。
When the remote end is closed and all data is read, return the empty string.
关闭远程端并读取所有数据后,返回空字符串。
'''
----------服务端------------:
# 1,验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
import socket
phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn, client_addr = phone.accept()
from_client_data1 = conn.recv(2)
print(from_client_data1)
from_client_data2 = conn.recv(2)
print(from_client_data2)
from_client_data3 = conn.recv(1)
print(from_client_data3)
conn.close()
phone.close()
# 2,验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态。
import socket
phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn, client_addr = phone.accept()
from_client_data = conn.recv(1024)
print(from_client_data)
print(111)
conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭。
print(222)
conn.close()
phone.close()
# 3 验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。
import socket
phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn, client_addr = phone.accept()
from_client_data1 = conn.recv(1024)
print(from_client_data1)
from_client_data2 = conn.recv(1024)
print(from_client_data2)
from_client_data3 = conn.recv(1024)
print(from_client_data3)
conn.close()
phone.close()
------------客户端------------
# 1,验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)
phone.close()
# 2,验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态。
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)
phone.close()
# 3,验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。
import socket
import time
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()
15.高大上版 解决粘包方式(自定制包头)
服务端
import socket
import subprocess
import json
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('192.168.14.97',8848))
#开机监听
while 1:
phone.listen(2)
print('开启通信')
conn,addr=phone.accept()#阻塞
# s = input('请输入')
# conn.send(s.encode('utf-8'))
print(conn,addr)
#from_client_data.upper()b
while 1:
try:
from_client_data = conn.recv(1024) # 至多接受1024个字节 阻塞
if from_client_data.upper()==b'Q':
print('客户正常退出了')
break
else:
# print(f'来自可无端的消息:{from_client_data.decode("utf-8")}')
# from_server_data=input('》》').strip().encode('utf-8')
#
obj = subprocess.Popen(from_client_data.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,#正确命令
stderr=subprocess.PIPE,# 错误命令
)
# s= # 正确命令
# s2=obj.stderr.read().decode('gbk') # 错误命令
total_base=obj.stdout.read()+obj.stderr.read()#原本就是gbk格式的
total_size=len(total_base)
#1.制作自定义报头
head_dic={'file_name':'text1',
'md5':54645898788,
'total_size':total_size}
#2.json形式的报头
head_dic_json=json.dumps(head_dic)
#3.bytes形式包头(把json形式字符串改成字节)
head_dic_json_bytes = json.dumps(head_dic).encode('utf-8')
#4获取bytes的总字节数(int类型)
len_head_dic_json_bytes=len(head_dic_json_bytes)
#5将不固定的int总字节变成固定长度的4个字节
four_head_bytes=struct.pack('i',len_head_dic_json_bytes)
#6 发送固定的4个字节
conn.send(four_head_bytes)
#7发送包头数据
conn.send( head_dic_json_bytes)
#8发送总数据
conn.send(total_base)
except ConnectionResetError:
print('客户端强行断开了')
break
conn.close()
phone.close()
客户端
import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
phone.connect(('192.168.14.97', 8848))
except ConnectionRefusedError:
exit('服务端没开')
while 1:
try:
data=input('强输入')
if not data:
print('不能为空')
elif data.upper() == 'Q':
print('您退出了')
break
else:
phone.send(data.encode('utf-8'))
#获取的包头固定数据字节
from_server_data = phone.recv(4)
#转换获取的字节(变成int类型的长度)
len_totals=struct.unpack('i',from_server_data)[0]
#获得bytes类型字典的总字节数
len_totals_bytes=phone.recv(len_totals)
#获取betes的dic数据(变成json字符串类型)1
len_totals_bytes_json=len_totals_bytes.decode('utf-8')
#解开json字符串获取原类型
len_totals_json_dic=json.loads(len_totals_bytes_json)
#通过自定义包头的里面的长度获取数据
total_ppt=b''
while len(total_ppt)< len_totals_json_dic['total_size']:
total_ppt+=phone.recv(1024)
print(f'来自服务的消息:{total_ppt.decode("gbk")}')
print(f'来自服务的消息:{len(total_ppt)}')
except ConnectionResetError:
print('服务端崩溃了连不上')
break
except ConnectionAbortedError:
pass
phone.close()
socket基于TCP(粘包现象和处理)的更多相关文章
- Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象
TCP的长连接 基于upd的socket服务 TCP黏包现象
- 网络编程-SOCKET开发之----2. TCP粘包现象产生分析
1. 粘包现象及产生原因 1)概念 指TCP协议中,发送方发送的若干个包数据到接收方接收时粘成一包.发送方粘包:发送方把若干个要发送的数据包封装成一个包,一次性发送,减少网络IO延迟:接收方粘包:接收 ...
- socket编程 TCP 粘包和半包 的问题及解决办法
一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次 ...
- (网络编程)基于tcp(粘包问题) udp协议的套接字通信
import socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...
- Socket编程--TCP粘包问题
TCP是个流协议,它存在粘包问题 产生粘包的原因是: TCP所传输的报文段有MSS的限制,如果套接字缓冲区的大小大于MSS,也会导致消息的分割发送. 由于链路层最大发送单元MTU,在IP层会进行数据的 ...
- Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象
一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字 server.bind() #把地址绑定到套接字,网络地址加端口 server.lis ...
- TCP粘包拆包问题
阿π 专注于网络协议,系统底层,服务器软件 C++博客 | 首页 | 发新随笔 | 发新文章 | | | 管理 Socket粘包问题 这两天看csdn有一些关于socket粘包,socket缓冲区设置 ...
- TCP粘包和拆包的定义,产生的原因以及解决方案
TCP粘包:指发送方发送的若干数据包在接收方接收时粘成一团,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾 产生的原因: 1.发送方的原因:TCP默认使用Nagle算法,而Nagle算法主要做两件 ...
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
随机推荐
- PCB SQL SERVER 数据库阻塞进程关系以思维导图方式呈现的实现方法
最近公司服务数据库同步机制常发生阻塞,时不时的导致PCB工程系统卡死现象,只有找到阻塞源头并处理掉,才以消除阻塞,但数据库中查看会话阻塞是通过二维表方式展示的父子会话进程ID的,是很难清楚的展示各会话 ...
- [原创]Greenplum数据库集群实践
GreenPlum实践 ============================================== 目录: 一.安装环境准备 二.GP数据库安装 三.集群添加standby节点 四. ...
- python 之 并发编程(守护进程、互斥锁、IPC通信机制)
9.5 守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就立即终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic process ...
- 洛谷P4994 终于结束的起点 题解
求赞,求回复,求关注~ 题目:https://www.luogu.org/problemnew/show/P4994 这道题和斐波那契数列的本质没有什么区别... 分析: 这道题应该就是一个斐波那契数 ...
- 鸟哥的Linux私房菜笔记第六章(二)
文件内容查询 直接查询文件内容 查阅一个文件的内容可以使用指令cat/tac/nl. # [cat|tac|nl] 文件 区别: 1.cat是直接把文件内容输出到屏幕上,并且从第一行开始输出到末行 2 ...
- Hive的查询
基本查询 全表和特定列查询 1.全表查询 select * from emp; 2.选择特定列查询 select empno,ename from emp; 注意: 1.SQL语言大小写不敏感 2.S ...
- 恢复在iterm2中当滚动光标时候触发滚动历史记录的问题
在Iterm2中,如果你上下滚动光标(上下滑动触摸板.或者滚动鼠标滚轮),通常情况下是触发了屏幕内容上下滚动. 但是在某些异常情况下,却触发了命令行历史记录的上下滚动,效果和你连续按了多次键盘的上下键 ...
- 【TensorFlow 3】mnist数据集:与Keras对比
在TF1.8之后Keras被当作为一个内置API:tf.keras. 并且之前的下载语句会报错. mnist = input_data.read_data_sets('MNIST_data',one_ ...
- django第四次(转自刘江)
我们都知道对于ManyToMany字段,Django采用的是第三张中间表的方式.通过这第三张表,来关联ManyToMany的双方.下面我们根据一个具体的例子,详细解说中间表的使用. 一.默认中间表 首 ...
- vue使用video.js解决m3u8视频播放格式
今天被这个关于m3u8视频播放不了搞了一下午,这个项目所有的视频流都是m3u8格式的,后台给我们返回的都是m3u8格式的视频流,解决了好长时间,看了好多博客,只有这个博客给我点启发,去解决这个问题,请 ...