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)
不怕大牛比自己牛,就怕大牛比自己更努力
原文地址:https://www.cnblogs.com/zaizai1573/p/10241738.html