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)
    不怕大牛比自己牛,就怕大牛比自己更努力
  • 相关阅读:
    HDU 1261 字串数(排列组合)
    Codeforces 488C Fight the Monster
    HDU 1237 简单计算器
    POJ 2240 Arbitrage
    POJ 3660 Cow Contest
    POJ 1052 MPI Maelstrom
    POJ 3259 Wormholes
    POJ 3268 Silver Cow Party
    Codesforces 485D Maximum Value
    POJ 2253 Frogger(最短路)
  • 原文地址:https://www.cnblogs.com/zaizai1573/p/10241738.html
Copyright © 2011-2022 走看看