欢迎来到 Kong Xiangqun 的博客

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
"""

整个流程结束

 

posted @ 2020-08-22 22:30  kongxiangqun20220317  阅读(163)  评论(0)    收藏  举报