zoukankan      html  css  js  c++  java
  • python (大文件下载及进度条展示) 验证客户端链接的合法性,socketserver

    ##########总结###########

     文件校验加进度条显示

    ####server
    import os
    import json
    import socket
    import struct
    import hashlib
    
    sk = socket.socket()
    sk.bind(('127.0.0.1', 9000))
    sk.listen()
    conn, addr = sk.accept()
    filename = 'mysql-5.6.42-winx64.zip'  # 文件名
    absolute_path = os.path.join('D:老男孩老师笔记第二阶段共享', filename)  # 文件绝对路径
    buffer_size = 1024 * 1024  # 缓冲大小,这里表示1MB
    
    md5obj = hashlib.md5()
    with open(absolute_path, 'rb') as f:
        while True:
            content = f.read(buffer_size)  # 每次读取指定字节
            if content:
                md5obj.update(content)
            else:
                break  # 当内容为空时,终止循环
    md5 = md5obj.hexdigest()#打印出16进制的md5值
    print(md5)  # 打印md5值
    
    dic = {'filename': filename,
           'filename_md5': str(md5), 'buffer_size': buffer_size,
           'filesize': os.path.getsize(absolute_path)}
    str_dic = json.dumps(dic).encode('utf-8')
    len_dic = len(str_dic)
    length = struct.pack('i', len_dic)
    conn.send(length)  # dic的长度
    conn.send(str_dic)  # dic
    with open(absolute_path, 'rb') as f:  # 文件
        while dic['filesize']:
            content = f.read(dic['buffer_size'])
            conn.send(content)
            dic['filesize'] -= len(content)
            '''
              这里不能减等4096,因为文件,最后可能只有3字节。
              要根据读取的长度len(content),来计算才是合理的。
              '''
    conn.close()
    ###############
    1cb1926af121c5c1b52a1ec13314805b
    ####client
    import json
    import struct
    import socket
    import sys
    import time
    import hashlib
    import os
    def processBar(num, total):  # 进度条  [接收大小,文件大小]
        rate = num / total
        rate_num = int(rate * 100)
        if rate_num == 100:
            r = '
    %s>%d%%
    ' % ('=' * rate_num, rate_num,)
        else:
            r = '
    %s>%d%%' % ('=' * rate_num, rate_num,)
        sys.stdout.write(r)
        sys.stdout.flush
    
    start_time = time.time()  # 开始时间
    sk = socket.socket()
    sk.connect(('127.0.0.1', 9000))
    
    dic_len = sk.recv(4)
    dic_len = struct.unpack('i', dic_len)[0]#查看长度
    str_dic = sk.recv(dic_len).decode('utf-8')
    dic = json.loads(str_dic)
    
    md5 = hashlib.md5()
    with open(dic['filename'], 'wb') as f:  # 使用wb更严谨一些,虽然可以使用ab
        content_size = 0
        while True:
            content = sk.recv(dic['buffer_size'])  # 接收指定大小
            f.write(content)  # 写入文件
            content_size += len(content)  # 接收大小
            md5.update(content)  # 摘要
    
            processBar(content_size, dic['filesize'])  # 执行进度条函数 [接收大小,文件大小]
            if content_size == dic['filesize']: break  # 当接收的等于文件大小时,终止循环
    
        md5 = md5.hexdigest()
        print(md5)  # 打印md5值
        if dic['filename_md5'] == str(md5):
            print(('md5校验正确--下载成功'))
        else:
            print('文件验证失败')
            os.remove(dic['filename'])  # 删除文件
    sk.close()  # 关闭连接
    end_time = time.time()  # 结束时间
    print('本次下载花费了{}秒'.format(end_time - start_time))
    ############
    ====================================================================================================>100%
    1cb1926af121c5c1b52a1ec13314805b
    md5校验正确--下载成功
    本次下载花费了18.044031858444214秒

    ####验证合法性

    使用hashlib.md5 加密

    为什么要随机字符串,是为了防止客户端的数据被窃取

    生成随机的bytes类型数据,它是解不出来的

    import os
    print(os.urandom(32))

    执行输出:

    b'POxca8xc8xf3xa0xb5,xddxb8K xa8Dx9cN"x82x03x86gx18exa7x97xa77xb9xa5VA'

    ###server
    import os
    import socket
    import hashlib
     
    secret_key = '老衲洗头用飘柔'  # 加密key
     
    sk = socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    while True:
        try:
            conn,addr = sk.accept()
            random_bytes = os.urandom(32)  # 随即产生32个字节的字符串,返回bytes
            conn.send(random_bytes)  # 发送随机加密key
            md5 = hashlib.md5(secret_key.encode('utf-8'))  # 使用secret_key作为加密盐
            md5.update(random_bytes)  #得到MD5消息摘要
            ret = md5.hexdigest()  #以16进制返回消息摘要,它是一个32位长度的字符串
            msg = conn.recv(1024).decode('utf-8')  # 接收的信息解码
            if msg == ret:print('是合法的客户端')  # 如果接收的摘要和本机计算的摘要一致,就说明是合法的
            else:conn.close()  # 关闭连接
        finally:  # 无论如何,都执行下面的代码
            sk.close()  # 关闭连接
            break
    ###client
    import socket
    import hashlib
    secret_key = '老衲洗头用飘柔'  # 加密key
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
     
    urandom = sk.recv(32)  # 接收32字节,也就是os.urandom的返回值
    md5_obj = hashlib.md5(secret_key.encode('utf-8'))  # 使用加密盐加密
    md5_obj.update(urandom)
    sk.send(md5_obj.hexdigest().encode('utf-8'))  # 发送md5摘要
    print('-----')
    sk.close()  # 关闭连接

    先执行server.py,再执行client.py

    client输出:-----

    server输出:是合法的客户端

    socketserver 

    SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进 程” 专门负责处理当前客户端的所有请求。

    它能实现多个客户端,同时连接,它继承了socket

    ####server
    import socketserver
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print(self.request)
            self.request.send(b'hello')  # 跟所有的client打招呼
            print(self.request.recv(1024))  # 接收客户端的信息
     
    server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
    server.serve_forever()
    #######client
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    print(sk.recv(1024))
    inp = input('>>>').encode('utf-8')
    sk.send(inp)
    sk.close()

    client输出:

    b'hello'
    >>>hi

    server输出:

    <socket.socket fd=316, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 49176)>
    b'hi'

    开多个客户端,也可以执行

    ########自己写一个socketserver

    ###server
    import socket
    from threading import Thread
    class MySocket:
        #init方法,将ip和端口封装到了self里面,并且实例化了一个socket对象
        def __init__(self,server_addr):
    
            self.server_addr = server_addr
            self.socket = socket.socket()
    
        #绑定了ip地址和端口,监听,执行了建立连接的方法
        def serve_forever(self):
            self.socket.bind(self.server_addr)
            self.socket.listen(5)
            self.build_connect()
        #建立连接,将建立连接的过程循环起来,因为我们每次建立连接都需要开一个线程带走这个连接,去通过这个连接收发消息.
        def build_connect(self):
    
            while 1:
                #让主线程代码循环起来,要让accept方法和handle方法要异步起来,不然建立连接的这个循环无法循环起来,因为进入handle之后就出不来了,异步你会想到什么,并发,多进程或者多线程,多进程不合适,所以用多线程,每一个conn,一个线程
                conn,addr = self.socket.accept()
                # self.handle(conn)
                #开线程处理conn,一个线程一个conn,
                t = Thread(target=self.handle,args=(conn,))
                t.start()
        #收发消息的内容,每个线程都需要执行一下handle方法,将conn作为参数传给handle方法
        def handle(self,conn):
            while 1:
                from_client_msg = conn.recv(1024).decode('utf-8')
                print('来自客户的消息>>>>',from_client_msg)
    
                to_client_msg = input('宝宝说>>>>')
                conn.send(to_client_msg.encode('utf-8'))
    
    if __name__ == '__main__':
        ip_port = ('127.0.0.1',8001)
        server = MySocket(ip_port)
        server.serve_forever()
    ###client 可实现多个客户端 同时和一个server应对
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1',8001))
    while 1:
        to_server_msg = input('给宝宝的消息>>>')
        client.send(to_server_msg.encode('utf-8'))
    
        from_server_msg = client.recv(1024).decode('utf-8')
        print('来自宝宝的消息:',from_server_msg)
    不怕大牛比自己牛,就怕大牛比自己更努力
  • 相关阅读:
    [ExtJS5学习笔记]第十一节 Extjs5MVVM模式下系统登录实例
    PS 滤镜算法原理——曝光过度
    PS 滤镜算法原理 ——马赛克
    OpenCV——彩色图像转成灰度图像
    [ExtJS5学习笔记]第十节 Extjs5新增特性之ViewModel和DataBinding
    【翻译】EXTJS 编码风格指南与实例
    【翻译】Ext JS最新技巧——2014-8-13
    [ExtJS5学习笔记]第九节 Extjs5的mvc与mvvm框架结构简介
    [ExtJS5学习笔记]第八节 Extjs5的Ext.toolbar.Toolbar工具条组件及其应用
    [ExtJS5学习笔记]第七节 Extjs5的组件components及其模板事件方法学习
  • 原文地址:https://www.cnblogs.com/zaizai1573/p/10241738.html
Copyright © 2011-2022 走看看