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) # 买 ...
随机推荐
- Bzoj 3166 [Heoi2013] Alo 题解
3166: [Heoi2013]Alo Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1118 Solved: 518[Submit][Status ...
- STM32F072从零配置工程-实现delay功能
因为是使用SysTick来作为延时时钟,因此在这里给出SysTick时钟的寄存器: CTRL:SysTick控制及状态寄存器 位段 名称 类型 复位值 描述 16 COUNTFLAG R/W 0 如果 ...
- 深入理解Java虚拟机二 阅读笔记
xl_echo编辑整理.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! --- > 以下内容摘抄自 ...
- 从微信小程序开发者工具源码看实现原理(二)- - 小程序技术实现
wxml与wxss的转换 1.wxml使用wcc转换 2.wxss使用wcsc转换 开发者工具主入口 视图层页面的实现 视图层页面实现技术细节 视图层快速打开原理 视图层新打开页面流程 业务逻辑层页面 ...
- Linux/Ubuntu正确卸载LXDE
第一步: sudo apt-get remove lxde 第二步 sudo apt autoremove lxde
- 面向对象和pickle模块结合
面向对象和pickle模块相关 1.面向对象: class 类名: def init(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法 ...
- HTML介绍和标签
1.HTML介绍 1.概述 html不是一种编程语言,是一种描述性的标记语言,用于描述超文本内容的显示方式.比如字体,颜色,大小等. 超文本:音频,视频,图片称为超文本. 标记:<英文单词或者字 ...
- maven install时跳过测试
xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! - ...
- Python_我的学习笔记 (博客停更------)
贡献一张PyCharm快捷键图(图片是借用他人的)----------建议最大化查看,因为这样不会破坏布局 注:部分内容引用小甲鱼,其他等网页,网站内容.如有冒犯,请联系我. 2019.07.21 ...
- Pytest进阶之参数化
前言 unittest单元测试框架使用DDT进行数据驱动测试,那么身为功能更加强大且更加灵活的Pytest框架怎么可能没有数据驱动的概念呢?其实Pytest是使用@pytest.mark.parame ...