粘包问题

"基于tcp协议实现远程执行命令"

"客户端"
"""

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8082))

while True:
cmd=input('请输入命令>>:').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))

解决粘包问题思路:

1、拿到数据的总大小total_size

2、recv_size=0,循环接收,每接收一次,recv_size+=接收的长度

3、直到recv_size=total_size,结束循环

cmd_res=client.recv(1024) # 本次接收,最大接收1024Bytes
print(cmd_res.decode('utf-8')) # 强调:windows系统用gbk

# 粘包问题出现的原因
# 1、tcp是流式协议,数据像水流一样粘在一起,没有任何边界区分
# 2、收数据没收干净,有残留,就会下一次结果混淆在一起
##### # 解决的核心法门就是:每次都收干净,不要任何残留

服务端
"""

# 服务端应该满足两个特点:
# 1、一直对外提供服务
# 2、并发地服务多个客户端

import subprocess
from socket import *

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
server.bind(('127.0.0.1',8082))
server.listen(5)

# 服务端应该做两件事
# 第一件事:循环地从板连接池中取出链接请求与其建立双向链接,拿到链接对象

while True:
conn,client_addr=server.accept()

第二件事:拿到链接对象,与其进行通信循环

while True:
try:
cmd=conn.recv(1024)
if len(cmd) == 0:break
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)

stdout_res=obj.stdout.read()
stderr_res=obj.stderr.read()
print(len(stdout_res)+len(stderr_res))
# conn.send(stdout_res+stderr_res) # ???
conn.send(stdout_res)
conn.send(stderr_res)

with open("1.mp4",mode='rb') as f:

for line in f:

conn.send(line)

except Exception:
break
conn.close()

"udp协议里没有粘包问题"

"解决tcp里的粘包问题"

客户端
"""

import struct
from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8083))

while True:
cmd=input('请输入命令>>:').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))

解决粘包问题思路:

一、先收固定长度的头:解析出数据的描述信息,包括数据的总大小total_size

header=client.recv(4)
total_size=struct.unpack('i',header)[0]

二、根据解析出的描述信息,接收真实的数据

2、recv_size=0,循环接收,每接收一次,recv_size+=接收的长度

3、直到recv_size=total_size,结束循环

recv_size = 0
while recv_size < total_size:
recv_data=client.recv(1024)
recv_size+=len(recv_data)
print(recv_data.decode('utf-8'),end='')
else:
print()

# 粘包问题出现的原因
# 1、tcp是流式协议,数据像水流一样粘在一起,没有任何边界区分
# 2、收数据没收干净,有残留,就会下一次结果混淆在一起
# 解决的核心法门就是:每次都收干净,不要任何残留

服务端
"""

# 服务端应该满足两个特点:
# 1、一直对外提供服务
# 2、并发地服务多个客户端

import subprocess
import struct
from socket import *

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
server.bind(('127.0.0.1',8083))
server.listen(5)

# 服务端应该做两件事
# 第一件事:循环地从板连接池中取出链接请求与其建立双向链接,拿到链接对象

while True:
conn,client_addr=server.accept()

第二件事:拿到链接对象,与其进行通信循环

while True:
try:
cmd=conn.recv(1024)
if len(cmd) == 0:break
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)

stdout_res=obj.stdout.read()
stderr_res=obj.stderr.read()
total_size=len(stdout_res)+len(stderr_res)

1、先发头信息(固定长度的bytes):对数据描述信息

int->固定长度的bytes

header=struct.pack('i',total_size)
conn.send(header)

2、再发真实的数据

conn.send(stdout_res)
conn.send(stderr_res)

except Exception:
break
conn.close()

posted @ 2021-08-31 18:49  #卧龙先生#  阅读(59)  评论(0)    收藏  举报