远程执行模块和黏包 socketserve hamc模块验证合法性

一.远程执行模块:subprocess

能执行操作系统的命令的功能
ret=subprocess.Popen("dir",   #要执行的命令
shell=True, # 表示要执行的是一条系统命令
stdout=subprocess.PIPE, # 存储执行结果的正常信息
stderr=subprocess.PIPE) # 存储执行结果的错误信息
#读出的内容是bytes类型
print("stdout: ",ret.stdout.read().decode("gbk")) #在windows环境下转换成gbk格式。在linux环境下转换成utf-8
print("stderr: ",ret.stderr.read().decode("gbk"))
server端
import socket
import subprocess
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr=sk.accept()
while True:
    cmd=input("请输入操作命令:")
    if cmd == "q":
        conn.send(cmd.encode("utf-8"))
        break
    conn.send(cmd.encode("utf-8"))
    print('stdout : ', conn.recv(1024).decode('gbk'))  #只有这个接收的话会丢包。接收不完整。剩余的会被另一条命令接收到
    print('stderr : ', conn.recv(1024).decode('gbk'))
conn.close()
sk.close()
View Code

client端

import socket
import subprocess
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
while True:
    cmd=sk.recv(1024).decode("utf-8")
    if cmd == "q":break
    ret=subprocess.Popen(cmd,
                         shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    sk.send(ret.stdout.read())
    sk.send(ret.stderr.read())
sk.close()
View Code

二.黏包

只有TCP有粘包现象,UDP永远不会粘包

1.黏包的概念:

同时执行多条命令之后,得到的结果很可能只有一部分
在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是黏包。

2.产生黏包的原因:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

3.发生黏包的两种情况:

1.发送方的缓存机制:
发送端需要等缓冲区满才发送出去
发送端多次send间隔较短,并且数据量较小,tcp会通过Nagle算法,封装成一个包,发送到接收端,
接收端不知道这个包由几部分组成,所以就会产生粘包。
2.接收方的缓存机制
数据量发送的大,接收端接收的小,再接一次,会出现上次没有接收完成的数据。就会出现粘包

4.tcp协议的拆包机制,面向流的通信特点和Nagle算法

当发送端缓冲区的长度大于网卡的MTU时(网络上传送的最大数据包)tcp会将这次发送的数据拆成几个数据包发送出去
发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法)
将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包

tcp和udp的总结

TCP是面向连接的,面向流的,提供高可靠性服务
对于空消息:tcp是基于数据流的,于是收发的消息不能为空
tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收.数据是可靠的,但是会粘包
UDP是无连接的,面向消息的,提供高效率服务
UDP支持的是一对多的模式,不会使用块的合并优化算法
udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送
udp的recvfrom是阻塞的,一个recvfrom必须对唯一一个sendintoudp根本不会粘包,但是会丢数据,不可靠。

黏包的解决方案

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,
对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes
借助struct模块,长度数字可以被转换成一个标准大小的4字节数字。可以利用这个特点来预先发送数据长度。
我们还可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,
然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)
struct.pack("i", len(str_dic)) 将长度转换成个定长(4)的bytes
#unpack结果是元祖 (20,)

客户端:
str_dic = json.dumps(dic).encode("utf-8")
ret = struct.pack("i", len(str_dic)) # 将字典的大小转换成一个定长(4)的bytes
sk.send(ret + str_dic)
服务端:
dic_len = conn.recv(4) # 4个字节 数字的大小
dic_len=struct.unpack("i",dic_len)[0]
connet_str=conn.recv(dic_len).decode("utf-8")
connet=json.loads(connet_str)

socketserver可以使tcp实现并发编程server端

import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print("——————")
        while True:
        #self.request  #相当于conn
            msg=input("<<")
            self.request.send(msg.encode("utf-8"))
            ret=self.request.recv(1024).decode("utf-8")
            print(ret)
server=socketserver.ThreadingTCPServer(
    ("127.0.0.1",9000),MyServer)
server.serve_forever()
View Code

client端

import socket
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
while True:
    ret=sk.recv(1024).decode("utf-8")
    if ret:
        if ret == "q":break
        print(ret)
        msg=input("<<")
        sk.send(msg.encode("utf-8"))
sk.close()
View Code

验证客户端的合法性 hmac模块

hashlib加密后是str hamc之后为bytes
os.urandom(32) #随即产生n个字节的字符串,bytes类型可以作为随机加密key使用
server端
import socket
import hmac
import os
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr=sk.accept()
def check_client(conn):
    secret_key=b"456"
    message=os.urandom(32) #随即产生n个字节的字符串,bytes类型可以作为随机加密key使用
    conn.send(message)
    obj=hmac.new(secret_key,message)
    ret=obj.digest()   #加密后bytes类型
    msg=conn.recv(1024)
    if ret==msg:
        print('合法的客户端')
        return True
    else:
        print('非法的客户端')
        return False
ret=check_client(conn)
while ret:
    msg=input("<<")
    conn.send(msg.encode("utf-8"))
    rec=conn.recv(1024).decode("utf-8")
    print(rec)
conn.close()
sk.close()
View Code

client端

import socket
import hmac
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
secret_key=b"456"
msg=sk.recv(1024)
obj=hmac.new(secret_key,msg)
ret=obj.digest()
sk.send(ret)
rec=sk.recv(1024)
if rec:
    print(rec.decode("utf-8"))
    while True:
        msg = input("<<")
        sk.send(msg.encode("utf-8"))
        msg = sk.recv(1024)
        print(msg.decode('utf-8'))
sk.close()
View Code
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/zgf-666/p/8658483.html