zoukankan      html  css  js  c++  java
  • 网络编程(2)

    文件传输

    基础文件传输(小文件)

        # server端负责接收
        import socket
        import json
        
        sk = socket.socket()
        sk.bind(('127.0.0.1', 5002))
        sk.listen()
        conn, addr = sk.accept()
        msg_f = conn.recv(1024).decode('UTF-8')# 接收client端的文件信息
        dic_f = json.loads(msg_f)# json.loads()用于将字符串形式的数据转化为字典
        
        with open(dic_f['filename'], mode='wb') as f:
            file = conn.recv(dic_f['filesize'])# 以发送过来的文件大小接收文件
            f.write(file)
        
        conn.close()
        sk.close()
        ##############################################
        # client端负责发送
        import socket
        import os
        import json
        
        sk = socket.socket()
        sk.connect(('127.0.0.1', 5002))
        
        abs_path = r'D:1.jpg'
        filename = os.path.basename(abs_path)
        filesize = os.path.getsize(abs_path)
        dic = {'filename': filename, 'filesize': filesize}
        str_dic = json.dumps(dic)# json.dumps()用于将字典形式的数据转化为字符串
        sk.send(str_dic.encode('UTF-8'))# 将文件信息传递给server端
        
        with open(abs_path, mode='rb') as f:
            file = f.read()
            sk.send(file)
        
        sk.close()
    
    • 以上server端负责接收,client端负责发送,运行后在当前目录下出现1.jpg文件。
    • 以上代码只能传输较小的文件,且隐患较多,例如当传输过程中的str_dic大于1024字节时,server端接收到的文件信息会有误;且当传输大文件时会出现更多问题。

    大文件传输

        # 负责接收
        import socket
        import json
        import struct
        
        sk = socket.socket()
        sk.bind(('127.0.0.1', 5002))
        sk.listen()
        conn, addr = sk.accept()
        lens = conn.recv(4)
        lens = struct.unpack('i', lens)[0]
        msg_f = conn.recv(lens).decode('UTF-8')# 接收client端的文件信息
        dic_f = json.loads(msg_f)# json.loads()用于将字符串形式的数据转化为字典
        
        with open(dic_f['filename'], mode='wb') as f:
            while dic_f['filesize'] > 0:
                file = conn.recv(1024)
                dic_f['filesize'] -= len(file)
                f.write(file)
        
        conn.close()
        sk.close()
        ############################################################
        # 负责发送
        import socket
        import os
        import json
        import struct
        
        sk = socket.socket()
        sk.connect(('127.0.0.1', 5002))
        
        abs_path = r'C:UsersLENOVOPicturesCamera RollWIN_20200301_14_36_34_Pro.mp4'
        filename = os.path.basename(abs_path)
        filesize = os.path.getsize(abs_path)
        dic = {'filename': filename, 'filesize': filesize}
        str_dic = json.dumps(dic)# json.dumps()用于将字典形式的数据转化为字符串
        # 为了避免粘包现象
        b_dic = str_dic.encode('UTF-8')
        lens = struct.pack('i', len(b_dic))# 将要传输的文件信息的长度转化为4字节的形式
        sk.send(lens)
        sk.send(b_dic)# 将文件信息传递给server端
        
        with open(abs_path, mode='rb') as f:
            # 传输大文件用循环读取
            while filesize > 0:
                file = f.read(1024)# 每次读取1024字节
                filesize -= 1024
                sk.send(file)
        
        sk.close()
    
    • 运行后在当前目录下出现相应mp4文件,且为144M较大的文件。
    • 文件发送比接收快,且发送过程中会将发送的文件进行拆解,所以当发送1024字节时,接收端不一定会接收到1024字节,会出现数据丢失现象,为了避免这种现象,server端接收数据进行循环时每次减去的数据长度应为每次接收到的数据长度。
    • 在传输文件信息时,可能会出现粘包现象,为了避免出现这种现象,需要先将文件信息的长度以4字节的形式发送给server端,再以相应的长度接收文件。

    客户端合法性

    • 若客户端要连接服务端要经过合法认证,通过约定的密钥验证客户端合法性。
    • 服务端随机发送信息给请求连接的客户端,客户端将接收到的信息加上约定的密钥经过哈希算法得到一个结果,将此结果发送给服务端。
    • 而服务端同时将发送的随机信息和密钥经过哈希算法得到一个结果,将此结果和接收到客户端的结果进行比较,若结果一致则通过合法验证。
    • 注意:不能将密钥通过网络直接发送,这样可能被拦截。

    哈希算法:通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。即通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

        # server端
        import socket
        import os
        import hashlib
        import hmac
        
        key = b'password'
        msg_rand = os.urandom(16)# 生成16位类型bytes的随机信息
        sk = socket.socket()
        sk.bind(('127.0.0.1', 5002))
        sk.listen()
        conn, addr = sk.accept()
        conn.send(msg_rand)# 发送16位随机信息
        # 根据随机信息和密钥进行摘要
        '''
        # 采用hashlib模块中的md5,也可以用sha1
        md5 = hashlib.md5(key)
        md5.update(msg_rand)
        res = md5.hexdigest().encode('UTF-8')# 摘要结果为str类型
        '''
        # 采用hmac模块
        hm = hmac.new(key, msg_rand)
        res = hm.digest()# 此时摘要结果为bytes类型
        msg_res = conn.recv(32)
        if res == msg_res:
            print('合法客户端')
            conn.send(b'hello')
        else:
            conn.close()
        sk.close()
        ########################################################  
        # client端
        import socket
        import hashlib
        import hmac
        
        key = b'password'
        sk = socket.socket()
        sk.connect(('127.0.0.1', 5002))
        msg_rand = sk.recv(16)# 接收随机信息
        '''
        md5 = hashlib.md5(key)
        md5.update(msg_rand)
        res = md5.hexdigest().encode('UTF-8')
        '''
        hm = hmac.new(key, msg_rand)
        res = hm.digest()
        sk.send(res)
        msg = sk.recv(1024)
        print(msg)
        sk.close()
    

    sockesever模块

    • socketsever模块基于socket模块。

    • 常用于tcp协议的sever端处理并发的客户请求。

    • 此时多个client可与sever端通信。

        # sever端
        import time
        import socketserver
        class Mysever(socketserver.BaseRequestHandler):
            def handle(self):
                conn = self.request
                while True:
                    # 小循环可发多次消息
                    try:
                        msg_r = conn.recv(1024).decode('UTF-8')
                        conn.send(msg_r.upper().encode('UTF-8'))
                        time.sleep(0.5)
                    except ConnectionResetError:
                        break
        sever = socketserver.ThreadingTCPServer(('127.0.0.1',5002), Mysever)
        sever.serve_forever()# sever端不主动退出
        ######################################################################
        # client-n端
        import time
        import socket
        sk = socket.socket()
        sk.connect(('127.0.0.1', 5002))      
        while True:
            sk.send(b'hello')
            msg = sk.recv(1024).decode('UTF-8')
            print(msg)
  • 相关阅读:
    [译]JavaScript源码转换:非破坏式与再生式
    [译]ES6中的代理对象
    tensorflow 如何获取graph中的所有tensor name
    python3中的str和bytes
    git submodule 添加 更新 删除 教程
    《重构:改善既有代码的设计》摘抄
    thrift入门教程/thrift资料集合
    将python2代码升级为python3代码最佳实践
    python标准库:subprocess——子进程管理
    安装“python-snappy”遇到“error: command 'x86_64-linux-gnu-gcc' failed with exit status 1”
  • 原文地址:https://www.cnblogs.com/geqianLee/p/12547224.html
Copyright © 2011-2022 走看看