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) # 买 ...
随机推荐
- Mac上Ultra Edit的激活
2016-11-20 增加16.10.0.22破解 去官网下载原载,先运行一次,再在终端里执行下面代码就可以破解完成!printf '\x31\xC0\xFF\xC0\xC3\x90' | dd se ...
- 【POJ - 2253】Frogger (Floyd算法)
-->Frogger 中文翻译 Descriptions: 湖中有n块石头,编号从1到n,有两只青蛙,Bob在1号石头上,Alice在2号石头上,Bob想去看望Alice,但由于水很脏,他想避免 ...
- 用Python玩数据-笔记整理-第一章
第一个程序:print >>>print("Hallo World!") >>>Hallo World! mystring = "Ha ...
- css基础4
今天是2019年6月21日,周五了.在这里写上一篇随笔,主要内容是css基础中的一些细节部分,话不多说,直接上! 一.背景渐变: background-image 线性渐变:linear-gradie ...
- 比赛:小奔的矩形solution
分析: 交叉相乘,然后除以最大公因数(为了减少爆常数的可能性std做了两次,数据很大),得到的两个数相加减二就是答案 代码: var p,q,n,m,a,b,i:int64; begin readln ...
- 后端 - Lession 01 PHP 基础
目录 Lession 01 php 基础 1. php 基础 2. php 变量 3. php 单引号 和 双引号区别 4. 数据类型 5. 数据类型转换 6. 常量 7. 运算符 8. 为 fals ...
- python虚拟环境管理 Pipenv 使用说明
安装 pip install pipenv 检查是否安装成功 pipenv --version 创建虚拟环境(在工程文件夹下) pipenv install 默认下,Pipenv统一管理所有虚拟环境 ...
- JavaScript数组高性能去重解决方案
在大多数的人眼里,数组去重是一个很简单的课题,很多人甚至熟练掌握了多种数组去重的方法,然而大多时候,我们却忽略了数组去重所消耗的时间资源.譬如我们在做前端性能优化的时候,又有多少人会考虑JavaScr ...
- Flutter初体验--环境搭建
Fluter最近火了起来,它的有点很多,今天我做一篇在Windows下安装Flutter的教程. 一.下载 无论你要安装什么软件,都要先下载下来.我用的是SourceTree,地址: https ...
- 在vue中创建自定义指令
原文:https://dev.to/ratracegrad/creating-custom-directives-in-vue-58hh 翻译:心上有杨 指令是带有 v- 前缀的特殊属性.指令的作用是 ...