zoukankan      html  css  js  c++  java
  • 远程执行模块和黏包 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
     
     
     
     
     
     
  • 相关阅读:
    jquery操作select(取值,设置选中)
    js一点代码备用
    正则实现文本框只能输入16个数字,每4位数字后添加一个空格
    js限制文本框只能输入数字方法
    可编程数据平面将OpenFlow扩展至电信级应用(二)
    Android Camera解析(上) 调用系统相机拍摄照片
    结束QQ即时通信垄断,开辟即时通信互联互通instantnet时代
    KMP模板
    网络抓包工具 Fiddler
    STM32F030, 使用嘀嗒定时器Systick实现LED闪烁
  • 原文地址:https://www.cnblogs.com/zgf-666/p/8658483.html
Copyright © 2011-2022 走看看