• day3.SocketServer实现并发 及 hashlib模块


    一、基本SocketServer编写

    # 网络协议的最底层就是socket,基于原有socket模块,又封装了一层,就是socketserver
    # socketserver 为了实现tcp协议,server端的并发.
    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print("handle方法被执行了...")
            
    # ThreadingTCPServer(ip端口号,自定义的类)
    server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer )
    # 建立连接,循环调用
    server.serve_forever()
    socketserver-服务端
    import socket
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9000) )
    # 处理收发数据的逻辑
    while True:
        sk.send(b"you can you up no can no bb")
        msg = sk.recv(1024)
        print(msg.decode("utf-8"))
    sk.close()
    socketserver-client
    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
    
            """
            print(self.request)
            print(self.client_address)
            self.request <==> conn
            <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 49116)>
            self.client_address <==> addr ('127.0.0.1', 49116)
            """
    
            conn = self.request
            while True:
                # 接受数据
                msg = conn.recv(1024)
                msg2 = msg.decode("utf-8")
                print(msg2)
                conn.send(msg2.upper().encode())
            
            print("handle方法被执行了...")
            
    # ThreadingTCPServer(ip端口号,自定义的类)
    server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer )
    # 建立连接,循环调用
    server.serve_forever()
    socketserver-服务端1
    import socket
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9000) )
    # 处理收发数据的逻辑
    
    sk.close()
    socketserver-client1

    二、hashlib模块

    1、md5算法

    1.1、基本用法

    # (1)创建一个md5算法的对象
    hm = hashlib.md5()
    # (2)把要加密的字符串通过update更新到hm这个对象中运算
    hm.update("123456".encode("utf-8")) # 里面的数据必须是二进制字节流
    # (3)获取32位16进制字符串
    res = hm.hexdigest() 
    print(res , len(res))

    1.2、加盐

    # 加盐(加key => Xboy_) 加一个关键字配合原字符进行加密,
    # 使密码更复杂,不容易被破解
    hm = hashlib.md5("Xboy_wangwen".encode())
    hm.update("123456".encode())
    res = hm.hexdigest()
    print(res , len(res))

    1.3、动态加盐

    res = str(random.randrange(100000,1000000))
    print(res)
    hm = hashlib.md5(res.encode("utf-8"))
    hm.update("123456".encode())
    res = hm.hexdigest()
    print(res)

    2、sha算法

    """
    sha 算出来的十六进制的串是40位,加密稍慢,安全性稍高
    md5 算出来的十六进制的串是32位,加密很快,安全性稍差
    """
    hs = hashlib.sha1()
    hs.update("我最是牛逼的&#*($&*(#&%*(&%*&%(#%&".encode())
    res = hs.hexdigest()
    print(res, len(res)) # 31673dd65f81fddaae07b4240cbb04af047b7496
    hs = hashlib.sha512()
    hs.update("123456".encode())
    res = hs.hexdigest()
    print(res , len(res))

    3、hmac

    """hmac 加密算法更加复杂,不容易破解"""
    import hmac
    # hmac.new(盐,密码)
    key = b"a"
    msg = b"123456"
    hn = hmac.new(key,msg)
    res = hn.hexdigest()
    print(res, len(res)) # 32位长度 十六进制的字符串

    3.1、动态加盐

    import os
    """
    # os.urandom 返回随机的二进制字节流
    res = os.urandom(32)
    print(res,len(res))
    """
    key = os.urandom(32)
    msg = b"123"
    hn = hmac.new(key,msg)
    res = hn.hexdigest()
    print(res, len(res))

    三、文件校验

    import hashlib
    import os
    """
    mode => r  read(数字->字符个数)
    mode => rb read(数字->字节个数)
    字节的个数 <=> 文件的大小
    """
    
    # (1) 针对于小文件进行内容校验
    def check_md5(filename):
        hs = hashlib.md5()
        with open(filename,mode="rb") as fp:            
            hs.update(fp.read())
        return hs.hexdigest()
        
    res1 = check_md5("ceshi1.txt")
    res2 = check_md5("ceshi2.txt")
    print(res1,res2)
    
    # (2) 针对于大文件进行内容校验
    # 可以通过update 把字符串分段进行加密
    # 常规方法
    strvar = "今天是星期五,好开心了,下周一又要考试了."
    hm = hashlib.md5()
    hm.update(strvar.encode())
    res = hm.hexdigest()
    print(res)
    
    # 分段更新加密
    hm = hashlib.md5()
    hm.update("今天是星期五,好开心了,".encode())
    hm.update("下周一又要考试了.".encode())
    res = hm.hexdigest()
    print(res)
    View Code
    # 方法一
    def check_md5(filename):
        hs = hashlib.md5()
        with open(filename,mode="rb") as fp:
            while True:
                content = fp.read(10) # 一次最多读取10个字节
                if content:
                    # 分批进行字符串密码更新
                    hs.update(content)
                else:
                    break
    
            return hs.hexdigest()
    
        
    res1 = check_md5("ceshi1.txt")
    res2 = check_md5("ceshi2.txt")
    print(res1,res2)
    方法一
    # 方法二
    def check_md5(filename):
        hs = hashlib.md5()
        # 计算文件大小=>返回字节数
        filesize = os.path.getsize(filename)
        with open(filename,mode="rb") as fp:
            while filesize:
                content = fp.read(10) # 一次最多读取10个字节
                hs.update(content)
                # 按照实际的字节个数读取
                filesize -= len(content)
    
            return hs.hexdigest()
    
        
    res1 = check_md5("ceshi1.txt")
    res2 = check_md5("ceshi2.txt")
    print(res1,res2)
    方法二

    四、服务器的合法性校验

    # ### 服务端2(支付宝)
    import socket
    import os
    import hmac
    
    def auth(conn,secret_key):
        # 随机产生32位的二进制字节流
        msg = os.urandom(32)
        conn.send(msg)
        hn = hmac.new(secret_key.encode(),msg)
        res_server = hn.hexdigest()
        print(res_server) # 461ea12ca8ef475caeedd0c742f4295e
        # 服务端接受客户端发送过来的数据进行验证;
        res_client = conn.recv(1024).decode("utf-8")
            
        if res_client == res_server:
            print("你是合法的服务端用户")
            return True
        else:
            print("不是合法的服务端用户")
            return False
    
    
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind( ("127.0.0.1",9000) )
    sk.listen()
    
    # 三次握手
    conn,addr = sk.accept()
    
    # 处理收发数据的逻辑
    secret_key = "芝麻开门"
    res = auth(conn,secret_key)
    
    # 在验证成功之后,给客户端发送状态码
    if res:
        conn.send("状态码是200:付款成功~".encode())
    # 四次挥手
    conn.close()
    # 退还端口
    sk.close()
    server
    # ### 服务端1(公司)
    import socket
    import hmac
    
    def auth(sk,secret_key):
        # 处理收发数据的逻辑
        msg = sk.recv(32)
        hn = hmac.new(secret_key.encode(),msg)
        res = hn.hexdigest()
        # 把客户端加密的字符串发送给服务端进行验证
        sk.send(res.encode("utf-8"))
    
    
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9000) )
    
    # 处理收发数据的逻辑
    secret_key = "芝麻开门123"
    auth(sk,secret_key)
    
    # 在验证成功之后,接受服务端给我的验证码
    res = sk.recv(1025).decode("utf-8")
    print(res)
    
    sk.close()
    client

    五、tcp登录

    import socket
    import hashlib
    import json
    
    def get_md5_code(usr,pwd):
        hs = hashlib.md5(usr.encode())
        hs.update(pwd.encode())
        return hs.hexdigest()
    
    # res = get_md5_code("tianqi","777")
    # print(res)
    
    sk = socket.socket()
    sk.bind( ("127.0.0.1", 9001) )
    sk.listen()
    conn,addr = sk.accept()
    
    
    # 处理收发数据的逻辑
    msg = conn.recv(1024).decode()
    # 把反解之后的字符串恢复原来的数据格式变成字典通过json
    dic = json.loads(msg)
    print(dic)
    
    sign = False
    with open("userinfo.txt",mode="r",encoding="utf-8") as fp:
        for line in fp:
            usr,pwd = line.strip().split(":")
            # print(usr,pwd)
            if usr == dic["username"] and pwd == get_md5_code(dic["username"],dic["password"]):
                # 制定状态码 0=>失败 1=>成功
                res = {"code":1}
                res_msg = json.dumps(res).encode()
                conn.send(res_msg)
                sign = True
                break
    
    if sign == False:
        # 发送错误的状态码
        res = {"code":0}
        res_msg = json.dumps(res).encode()
        conn.send(res_msg)
        
    
    
    conn.close()
    sk.close()
    server
    import socket
    import json
    sk = socket.socket()
    sk.connect( ("127.0.0.1", 9001) )
    
    # 处理收发数据的逻辑
    usr = input("请输入您的用户名:")
    pwd = input("请输入您的密码:")
    dic = {"username":usr,"password":pwd,"operate":"login"}
    # 先通过json变成字符串
    res = json.dumps(dic)
    # json字符串 -> 字节流
    bytes_msg = res.encode()
    # 把字节流发送给服务端
    sk.send(bytes_msg)
    
    
    # 接受服务端发送过来的数据
    res_msg = sk.recv(1024).decode()
    dic_code = json.loads(res_msg)
    
    if dic_code["code"]:
        print("恭喜你~ 登录成功")
    else:
        print("i am so sorry ~ 登录失败")
    
    sk.close()
    client
                                zhangsan:e7a6d0f97db7ea1c4a0c1c137cbf771c
                    lisi:c79439cf9abcbd6c6d46e45766a9e64a
            tianqi:907e690908517e5cededf8db13b28237
    userinfo.txt

    六、SocketServer源码分析

    import socketserver
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print('conn is: ', self.request) # 相当于粘包的conn链接
            print('addr is: ', self.client_address) # 相当于addr地址
    
            while True:  # 通信循环
                try:
                    # 收消息
                    data = self.request.recv(1024)
                    if not data:break
                    print('收到客户端的消息是', data)
    
                    # 发消息
                    self.request.sendall(data.upper())
                except Exception as e:
                    print(e)
                    break
    
    if __name__ == '__main__':
        # 多线程的tcp服务端 实例化得到s
        # 来一个客户端就调用 handle方法创建一个实例通信
        s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)
        # 永远服务 相当于外层循环
        s.serve_forever() # 链接循环
    socketserver实例
    # socketserver 到底是个什么东西
    # socketserver分为两大类
    # server类 和 request类

    1、server类

    """
    一类是帮你处理链接的,可以理解为server类(Ctrl+左键点进socketserver向下翻一点就可以
    看见一个类的继承关系)
    隶属于server类的基本5个
    
            +------------+
            | BaseServer |
            +------------+
                  |
                  v
            +-----------+        +------------------+
            | TCPServer |------->| UnixStreamServer |
            +-----------+        +------------------+
                  |
                  v
            +-----------+        +--------------------+
            | UDPServer |------->| UnixDatagramServer |
            +-----------+        +--------------------+
    
    """

    1.1、关于以上几个类

    """
    BaseServer 祖宗哪个类
    TCPServer 处理tcp链接的
    UDPServer 处理udp链接的
    
        TCPServer继承BaseServer
        UDPServer继承TCPServer 看似没有什么关系 但是写这个模块的人就是这样写的
    
    
        class BaseServer
        class TCPServer(BaseServer)
        class UDPServer(TCPServer):
    
    UnixStreamServer Stream 流 也是处理tcp链接的 用在Unix系统不能跨平台 一般不用
    UnixDatagramServer Datagram 数据包 处理udp的 用在Unix系统不能跨平台 一般不用
        UnixStreamServer 继承 TCPServer
        UnixDatagramServer 继承 UDPServer
    
    """

    2、request类:处理通信

    """
    __all__ = ["BaseServer", "TCPServer", "UDPServer",
               "ThreadingUDPServer", "ThreadingTCPServer",
               "BaseRequestHandler", "StreamRequestHandler",
               "DatagramRequestHandler", "ThreadingMixIn"]
        __all__内置变量 每一个字符串都代表了一个类名
    
    if hasattr(socket, "AF_UNIX"):
        __all__.extend(["UnixStreamServer","UnixDatagramServer",
                        "ThreadingUnixStreamServer",
                        "ThreadingUnixDatagramServer"])
    如果socket下有 AF_UNIX 属性 就把__all__扩充
    
    """

    有一个 BaseRequestHandler

    源码:

    class BaseRequestHandler:
        def handle(self):
        pass
    """
    This class is instantiated for each request to be handled.  The
        constructor sets the instance variables request, client_address
        and server, and then calls the handle() method.  To implement a
        specific service, all you need to do is to derive a class which
        defines a handle() method.
    每一个请求来都会呼叫handle()方法
    你需要做的就是继承一个类,定义一个handle()方法
    
    就是说自己定义的类一定要继承socketserver.BaseRequestHandler并重写handle方法
    """

    类StreamRequestHandler 和 类DatagramRequestHandler

    """
    StreamRequestHandler(BaseRequestHandler):
        pass
    DatagramRequestHandler(BaseRequestHandler):
        pass
    
    """

    继承关系:

    """
                              +--------------------+
                              | BaseRequestHandler |
                              +--------------------+
                                 |            |
                       (继承)  |                |   (继承)
                             |                     |
          +----------------------+             +------------------------+ 
          | StreamRequestHandler |             | DatagramRequestHandler |
          +----------------------+             +------------------------+ 
    """

    3、__all__中的其他类

    __all__ = ["BaseServer", "TCPServer", "UDPServer",
               "ThreadingUDPServer", "ThreadingTCPServer",
               "BaseRequestHandler", "StreamRequestHandler",
               "DatagramRequestHandler", "ThreadingMixIn"]
    if hasattr(os, "fork"):
        __all__.extend(["ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"])
    比如:
    # "ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"
    # "ThreadingUDPServer", "ThreadingTCPServer",

    ForkingUDPServer
    ForkingTCPServer

    if hasattr(os, "fork"):
        class ForkingUDPServer(ForkingMixIn, UDPServer): pass
        class ForkingTCPServer(ForkingMixIn, TCPServer): pass
    
    # ForkingUDPServer 继承 ForkingMixIn UDPServer(即处理udp链接的)
    # ForkingTCPServer 继承 ForkingMixIn TCPServer(即处理tcp链接的)
    
    class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
    class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
    
    # ThreadingUDPServer 继承 ThreadingMixIn UDPServer(即处理udp链接的)
    # ThreadingTCPServer 继承 ThreadingMixIn TCPServer(即处理tcp链接的)

    那么到现在类的关系如图所示

    在我们自己写的代码中

    s = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)

    调用ThreadingTCPServer这个类,加括号,传两个参数实例化

    源码:

    class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
    """
    自己的实例化传两个参数就是传给构造方法,这个类没有构造方法找继承的类的构造方法
    ThreadingMixIn没有构造方法找TCPServer
    """

    源码:

        address_family = socket.AF_INET
    
        socket_type = socket.SOCK_STREAM
    
        request_queue_size = 5
    
        allow_reuse_address = False # 地址默认不重新使用
    
        # ('127.0.0.1',8080),MyServer
        def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
            """Constructor.  May be extended, do not override."""
            BaseServer.__init__(self, server_address, RequestHandlerClass)
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
    # 这里又调用了父类的构造方法,也是把('127.0.0.1',8080),MyServer 传入进去

    源码:

    class BaseServer:
        def __init__(self, server_address, RequestHandlerClass):
            """Constructor.  May be extended, do not override."""
            self.server_address = server_address # self 就是实例出来的s
            self.RequestHandlerClass = RequestHandlerClass
    # 可以在自己的代码中打印一下self.server_address 和 self.RequestHandlerClass
    # 执行完构造方法后,我们再回到TCPServer这个类继续往下走

    源码:

        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        self.socket = socket.socket(self.address_family,self.socket_type)
        # 就是创建一个socket对象
    # 接下来就是绑定监听,就实例化完了
    """
    现在自己的类中找也就是ThreadingTCPServer 结果没找到
    再去左边的父类ThreadingMixIn找 结果也没找到
    再去右边的父类TCPServer找,结果还是没找到
    最后取祖宗类BaseServer找,发现有serve_forever方法
    """

    为什么调用serve_forever就一直循环?

    """
    while循环里有一个 return self._handle_request_noblock() 就是循环接收链接的
    找_handle_request_noblock还是从self也就是s在的类ThreadingTCPServer一层一层找
    最后还是在类BaseServer中找到的
    """
    """
    在self._handle_request_noblock()函数往下走,有一个
    request, client_address = self.get_request()
    找self.get_request()的方法和上面一样就是一层一层找
    最后在TCPServer类中找到了
    这里有一个return self.socket.accept()
    这个就NB了 self.socket就是上面的socket对象 .accept() 返回的就是conn addr
    """
    """
    还是从s的跟找
    最终在类ThreadingMixIn 找到了def process_request(self, request, client_address):
    """

    源码:

    t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
    """
    找process_request_thread 发现在当前类找到了process_request_thread
    process_request_thread这个类又调用了self.finish_request(request, client_address)方法
    
    finish_request方法最终在祖宗类BaseServer类中找到了
    它调用了 self.RequestHandlerClass(request, client_address, self)
    RequestHandlerClass 就是我们传入的第二个参数Myserver
    
    实际上就是把request client_address self 传递到我们自己的类
    这之前都是在做的通信循环
    
    那么传了3个参数我就要找构造方法,我自己没有构造方法,就要去父类找
    
    找 BaseRequestHandler 的 __init__方法
    
    """

    源码:

    def __init__(self, request, client_address, server):
    """
    构造函数接收3个参数
    
    触发__init__方法就会触发self.handle()
    接下来就调用self.handle()
    handle就是控制通信循环的
    
    self.request就是conn
    """

    整个流程结束

  • 相关阅读:
    第四章
    第三章
    第二章
    实验5-2: 编制程序,输入m、n(m≥n≥0)后,计算下列表达式的值并输出。 要求将计算阶乘的运算编写作函数fact(n),函数返回值的类型为float
    作业
    多人电费
    单人电费
    圆柱体积
    圆面积
    第七章
  • 原文地址:https://www.cnblogs.com/kongxiangqun/p/13511304.html
Copyright © 2020-2023  润新知