一,昨日作业,讲解(客户端通过命令,操作服务端系统)
服务端:
# 服务端应该满足两个特点:
# 1、一直对外提供服务
# 2、并发地服务多个客户端
import subprocess
from socket import *
server = socket(AF_INET, SOCK_STREAM) # 创建对象
server.bind(('127.0.0.1', 8080)) # 监听接口
server.listen(5) # 半连接池
while True:
conn, client_addr = server.accept() # 与客户端建立双向通道
while True:
try:
cmd = conn.recv(1024) # 读去客户端发来的命令 最大是1024
if len(cmd) == 0: break # 如果 命令长度是0 则终止循环,关闭通道
obj = subprocess.Popen(cmd.decode('utf-8'), # subprocess 这个模块需要仔细研究 目前先用
shell=True,
stdout=subprocess.PIPE, # stdout是一个管道
stderr=subprocess.PIPE # stderr是一个管道
)
stdout_res = obj.stdout.read() # 得到stdout管道内容 正确内容
stderr_res = obj.stderr.read() # 得到stderr管道内容 错误内容
# print(len(stdout_res) + len(stderr_res))
conn.send(stdout_res) # 给客户端发送 命令执行正确结果 倘若是没有数据命令结果,我也不知道会怎样
conn.send(stderr_res) # 给客户端发送 命令执行错误结果
except Exception:
break
conn.close()
客户端:
from socket import *
client = socket(AF_INET, SOCK_STREAM) # 创建客户对象
client.connect(('127.0.0.1', 8080)) # 选择连接服务器地址
while True:
cmd = input('请输入命令>>:').strip() # 输入命令 此命令会被操作系统系统执行
if len(cmd) == 0: continue # 输入命令为空则返回
client.send(cmd.encode('utf-8')) # 发送命令以utf-8 事先知道服务端的解码方式
cmd_res = client.recv(1024) # 接收命令执行的结果 大小为1024
print(cmd_res.decode('GBK')) # window系统 是以GBK解码的
client.close() # 关闭管道
二,解决粘包问题:
服务端:
# 服务端应该满足两个特点:
# 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()
客户端:
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
import json
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、制作头
header_dic = {
"filename": "a.txt",
"total_size": total_size,
"md5": "123123xi12ix12"
}
json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode('utf-8')
# 2、先把头的长度发过去
x = struct.pack('i', len(json_str_bytes)) # i 模式把int类型的模式转换成 固定的4个字节的Bytes
conn.send(x)
# 3、发头信息
conn.send(json_str_bytes) # 这个Bytes会和一下的Bytes粘到一起
# 4、再发真实的数据
conn.send(stdout_res)
conn.send(stderr_res)
except Exception:
break
conn.close()
客户端:
import struct
import json
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'))
# 接收端
# 1、先手4个字节,从中提取接下来要收的头的长度
x=client.recv(4)
header_len=struct.unpack('i',x)[0] # 解包操作,把你收到的东西解成一个小元组
# 2、接收头,并解析
json_str_bytes=client.recv(header_len)
json_str=json_str_bytes.decode('utf-8')
header_dic=json.loads(json_str)
print(header_dic)
total_size=header_dic["total_size"]
# 3、接收真实的数据
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()