要解决粘包问题:
TCP:流式协议
特点:
1、数据流没有开头也没有结果,像水流一样
2、TCP协议有一个nagle算法,
nagle算法会将数据量较小,并且时间间隔较短的数据合成一条数据发送,
这么做可以减少网络IO次数,进而提升传输效率

1.struct模块

#1、把整型数字转成bytes类型
#2、转成的bytes是固定长度的 import struct
import json header_dic = {
'total_size': 31222222222121,
'md5': '123svsaef123sdfasdf',
'filename': 'a.txt'
} #序列化:内存中的数据结构----》转成一种中间格式(字符串)----》存到文件中
header_json=json.dumps(header_dic)
print(header_json,type(header_json)) #编码:编码后的结果为bytes类型
header_bytes=header_json.encode('utf-8')
header_size=len(header_bytes)
print(header_size) # #打包
obj=struct.pack('i',header_size)
print(obj,len(obj)) # b'Q\x00\x00\x00' 4 #解包
obj2=struct.unpack('i',obj)
print(obj2) # (81,)

打印结果:

{"total_size": 31222222222121, "md5": "123svsaef123sdfasdf", "filename": "a.txt"} <class 'str'>
81
b'Q\x00\x00\x00' 4
(81,)

result

2.通讯整个流程:

'''
流程:
服务端:制作报头,把报头信息丢到报头字典里,字典序列化得到json字符串,encode得到bytes
---->发报头长度(struct.pack打成4个bytes)--->发送报头---->发真实数据 客户端:先接收4个bytes:struct.unpack解包报头长度--->接收报头-->
解析报头:decode得到字符串,反序列化得到报头--->根据报头内的信息,收取真实的数据 '''

3.具体代码如下:

服务端:

from socket import *
import subprocess
import struct
import json server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5) #半连接池:限制的是请求数,而不是连接数 while True:
conn,client_addr=server.accept() #(连接对象,客户端的ip和端口) conn:tcp三次握手的一个产物,可以用来收发消息
print(client_addr) #此处client_addr的作用是可以知道哪个客户端建的链接,和通讯无关
while True:
try:
cmd=conn.recv(1024) #cmd为bytes类型,
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read() #拿到系统执行的结果(subprocess执行的是window系统命令,解码需要gbk)
stderr=obj.stderr.read() # 1、制作报头
header_dic={
'total_size':len(stdout) + len(stderr),
'md5':'123svsaef123sdfasdf',
'filename':'a.txt'
}
header_json = json.dumps(header_dic) #json序列化报头
header_bytes = header_json.encode('utf-8') #字符串类型---->bytes类型,发送 # 2、先发送报头的长度
header_size=len(header_bytes)
conn.send(struct.pack('i',header_size)) # 3、发送报头
conn.send(header_bytes) # 4、发送真实的数据
conn.send(stdout) #send只能发bytes类型
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()

客户端:

from socket import *
import struct
import json client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
# print(client) while True:
cmd=input('>>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8')) #把命令结果发给服务器
#1、先收报头的长度根据报头内的信息,收取真实的数据
header_size=struct.unpack('i',client.recv(4))[0] #bytes类型,想拿到报头长度要对bytes进行解出 #2、接收报头
header_bytes=client.recv(header_size) #对应服务端的发送报头 #3、解析报头
header_json=header_bytes.decode('utf-8') #bytes类型---->字符串类型
header_dic=json.loads(header_json) #反序列化拿到报头
print(header_dic) total_size=header_dic[ 'total_size']
# print(total_size) #1025
#4、接收数据 recv_size=0
res=b''
while recv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data) print(res.decode('gbk'))
client.close()

最新文章

  1. Swift来的正是时候
  2. JDBC/PreparedStatement
  3. 黑马程序员_ JAVA中的多线程
  4. NHibernate使用ICriteria分页并返回数据库记录总条数
  5. Auguse 2nd, Week 32nd Tuesday, 2016
  6. Chart系列(二):数据绑定
  7. COJ 2024 仙境传奇(五)——一个天才的觉醒 素数筛
  8. Android 汉字转拼音之工具篇
  9. MSP430与ATK-NEO-6M GPS模块
  10. 以太网PHY 芯片之 MII/MDIO接口详解
  11. android JNI--- 搭建环境(1)
  12. CSS实现导航条Tab切换的三种方法
  13. ionic搭建与基础
  14. C#线程等待句柄
  15. spring @Value 设置默认值
  16. Hive 查询元数据库获取某个分区的count数
  17. 494. Target Sum
  18. php ci nginx 伪静态rewrite配置方法
  19. node.js的
  20. Maven pom.xml中的元素modules、parent、properties以及import(转)

热门文章

  1. 【翻译】.NET 5 Preview2发布
  2. 线程 -- ThreadLocal
  3. 《java编程思想》操作符
  4. 《java编程思想》一切都是对象
  5. Ubuntu 18 安装MySQL 5.7
  6. Java并发基础02. 传统线程技术中的定时器技术
  7. SpringBoot 入门:项目属性配置
  8. Zookeeper是如何实现分布式锁的
  9. 2016蓝桥杯报纸页数(C++C组)
  10. Python 1基础语法一(注释、行与缩进、多行语句、空行和代码组)