zoukankan      html  css  js  c++  java
  • python全栈学习--day36(socket验证客户端的合法性、 并发编程)

     粘包现象

    粘包现象的成因:
    tcp协议的特点,面向流的,为了传输可靠传输,所以有很多优化机制。
    无边界 所有在连接建立的基础上传递的数据之间没有界限
    收发消息很有可能不完全相等
    缓存机制,导致没发过去的消息会在发送端缓存
    没有接收完的消息会在接收端缓存

    解决:
    给应用层定制协议
    先发送一个定长的可以表示待发送数据长度的bytes 先接收一个固定长度
    在发送要发送的数据 在按照长度接收数据


    方案二:
    先发送一个定长表示待发送字典长度的bytes (先接收一个固定长度)
    再发送要发送字典(再按照长度接收字典)
    在发送文件 (再根据字典中的信息接收相应的内容)

    ####大文件传输实例

    实例一:

    import os
    import json
    import struct
    import socket
    
    #
    sk = socket.socket()
    sk.bind(('192.168.11.36',9000))
    sk.listen()
    
    conn,addr = sk.accept()
    print(addr)
    dic = {'filename':' D:战狼.rmvb',
            'filesize':os.path.getsize(r' D:战狼.rmvb)}
    str_dic = json.dumps(dic).encode('utf-8')
    dic_len = struct.pack('i',len(str_dic))
    conn.send(dic_len)
    conn.send(str_dic)
    with open(r' D:战狼.rmvb','rb') as f:
        # content = f.read()
        while True:
            content = f.read(1024)
            if not content:break
            conn.send(content)
            # if not conn.send(content):break
    conn.close()
    sk.close()
    

      

    import json
    import struct
    import socket
    
    sk = socket.socket()
    sk.connect(('192.168.11.36',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)
    
    with open(dic['filename'],'wb') as f:
    
        while True:
            content = sk.recv(dic['filesize'])
            if not content:break
            f.write(content)
    sk.close()
    

    示例二:

    ###加入MD5验证

    #########server 端
    import os
    import json
    import struct
    import socket
    import hashlib
    
    md5 = hashlib.md5()
    with open(r'D:战狼.rmvb', 'rb') as f:
        while True:
            b = f.read(1024)
            if not b:
                break
            md5.update(b)
    md5 = md5.hexdigest()
    print(md5)
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    
    conn,addr = sk.accept()
    print(addr)
    dic = {'filename':'黏包.mp4', 'filename_md5':str(md5),
           'filesize':os.path.getsize(r'D:战狼.rmvb')}
    str_dic = json.dumps(dic).encode('gbk')
    dic_len = struct.pack('i', len(str_dic))
    conn.send(dic_len)
    conn.send(str_dic)
    with open(r'D:战狼.rmvb', 'rb') as f:
        content = f.read()
    conn.send(content)
    conn.close()
    sk.close()
    
    #####客户端
    import json
    import struct
    import socket
    import hashlib
    
    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('gbk')
    dic = json.loads(str_dic)
    
    md5 = hashlib.md5()
    with open(dic['filename'],'wb') as f:
        while True:
            content = sk.recv(1024)
            f.write(content)
            if not content:break
            md5.update(content)
        md5 = md5.hexdigest()
        print(md5)
        if dic['filename_md5'] == str(md5):
            print('md5校验正确--下载成功')
        else:
            print('文件验证失败')
    sk.close()
    

      

    socket 验证客户端的合法性

    #server端 
    #秘钥 # hashlib # md5 # hashlib.md5('秘钥') import os import socket import hmac secret_key = '老衲洗头用飘柔'.encode('utf-8') # 秘钥 sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() while True: try: conn,addr = sk.accept() random_bytes = os.urandom(32) # 加密方法 conn.send(random_bytes) obj = hmac.new(key =secret_key,msg =random_bytes) ret = obj.hexdigest() msg = conn.recv(1024).decode('utf-8') if msg == ret:print('是合法的客户端') else:conn.close() finally: sk.close() break
    ##客户端
    import socket
    import hmac
    secret_key = '老衲洗头用飘柔'.encode('utf-8')
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    
    urandom = sk.recv(1024)
    hmac_obj =  hmac.new(key = secret_key,msg =urandom)
    sk.send(hmac_obj.hexdigest().encode('utf-8'))
    sk.close()
    

    并发编程

    import time
    import socketserver
    #并发编程
    class MyServer(socketserver.BaseRequestHandler):
        clients = {}
        def handle(self):
            qq_id = self.request.recv(1024).decode('utf-8')
            MyServer.clients[qq_id] = self.request
            recv_msg = self.request.recv(1024).decode('utf-8')
            print(recv_msg)
            if recv_msg.startswith('say'):
                recv_msg = recv_msg.replace('say','')
                print(recv_msg)
                friend,msg = recv_msg.split('|')
                time.sleep(5)
                print(MyServer.clients)
                print(friend)
                print('-----',MyServer.clients.get(friend))
                if MyServer.clients.get(friend):
                    MyServer.clients[friend].send(('%s|%s'%(qq_id,msg)).encode('utf-8'))
    
    if __name__ == '__main__':
        socketserver.TCPServer.allow_reuse_address = True
        server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
        server.serve_forever()
    

      

    import socket
    sk  = socket.socket()
    sk.connect(('127.0.0.1',9000))
    while True:
        sk.send('heiheihei'.encode('utf-8'))
        import time
        time.sleep(0.1)
        sk.send('sayhahaha|我是heiheihei'.encode('utf-8'))
    sk.close()
    

    执行:

    说明:python并发中server不做消息回复,只做接收与转发

    ThreadingTCPServer/TCPServer/ForkingTCPServer的区别,原理可同样引申到UDP,

    这三个类其实就是对接收到request请求后的不同处理方法。

    TCPServer是接收到请求后执行handle方法,如果前一个的handle没有结束,那么其他的请求将不会受理,新的客户端也无法加入。

    而ThreadingTCPServer和ForkingTCPServer则允许前一连接的handle未结束也可受理新的请求和连接新的客户端,区别在于前者用建立新线程的方法运行handle,后者用新进程的方法运行handle。

    二、socket的更多方法介绍              

    服务端套接字函数
    s.bind()                     绑定(主机,端口号)到套接字
    s.listen()                   开始TCP监听
    s.accept()                 被动接受TCP客户的连接,(阻塞式)等待连接的到来
    
    客户端套接字函数
    s.connect()                主动初始化TCP服务器连接
    s.connect_ex()           connect()函数的扩展版本,出错时返回错误码,而不是抛出异常
    
    公共用途的套接字函数
    s.recv()                         接收TCP数据
    s.send()                        发送TCP数据
    s.sendall()                     发送TCP数据
    s.recvfrom()                   接收UDP数据
    s.sendto()                      发送UDP数据
    s.getpeername()            连接到当前套接字的远端地址
    s.getsockname()            当前套接字的地址
    s.getsockopt()                返回指定的套接字的参数
    s.setsockopt()                设置指定的套接字的参数
    s.close()                         关闭套接字
    
    面向锁的套接字方法
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间
    
    面向文件的套接字的函数
    s.fileno()               套接字的文件描述符
    s.makefile()           创建一个与该套接字相关的文件
    

    使用hmac加密                                                                                                              

    hmac是专门来做客户端合法性的

    假如黑客渗透到内网,得知到服务器IP地址。就可以做端口扫描,一台计算机的端口范围是0~65535

    扫描6万多次,就能知道了。

    import hmac
    obj = hmac.new(key=b'secret_key',msg=b'23f232r2rq23')
    print(obj.hexdigest())
    

    执行:

     

    三、socketserver   

    SocketServer 内部使用IO多路复用以及"多线程"和"多进程",从而实现并处理多个客户端请求的Socket服务器。

    即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个"线程"或者"进程"专门负责处理当前客户端的所有请求

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

    ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

    使用ThreadingTCPServer:

    创建一个继承自SocketServer.BaseRequestHandler 的类,必须继承

    类中必须定义一个名称为handle的方法,必须重写

    看BaseRequestHandler 的源码,它的hendle方法,是空的

    1
    2
    def handle(self):
        pass

    需要自己去实现

    server.py

    1
    2
    3
    4
    5
    6
    7
    import socketserver
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print(self.request)
     
    server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
    server.serve_forever()

     

    client.py

    1
    2
    3
    4
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    sk.close()

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

    1
    2
    3
    4
    5
    6
    7
    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连续发送:

    1
    2
    3
    4
    5
    6
    7
    8
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    while True:
        print(sk.recv(1024))
        #inp = input('>>>').encode('utf-8')
        sk.send(b'hahaha')
    sk.close()

    server连续接收:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import socketserver
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            while True:
                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()

    执行效果如下:

    如果server端口重复,使用以下代码:


     
  • 相关阅读:
    JS
    Python之缩进块
    Python快捷键
    Python介绍
    SOAP UI-----测webservice接口
    jmeter分布式压测(多台电脑一起压测)
    jmeter操作数据库
    jmeter压测
    jmeter关联
    jmeter参数化
  • 原文地址:https://www.cnblogs.com/haowen980/p/9010539.html
Copyright © 2011-2022 走看看