zoukankan      html  css  js  c++  java
  • 网络编程 socket常用方法 hmac模块 socketserver模块

    socket常用方法:
    服务端套接字函数
    s.bind()    绑定(主机,端口号)到套接字
    s.listen()  开始TCP监听
    s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
    客户端套接字函数
    s.connect()     主动初始化TCP服务器连接
    s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    公共用途的套接字函数
    s.recv()            接收TCP数据   s 相当于 conn
    s.send()            发送TCP数据    #(拆包在发送)   常用
    s.sendall()         发送TCP数据   $(一次性发送)
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址  s = conn
    s.getsockname()     当前套接字的地址         s = conn
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字
    l例 #print(conn.getsockname())
    面向锁的套接字方法
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout(5)      设置阻塞套接字操作的超时时间  #参数5指等待阻塞5秒,超过5秒报错
    s.gettimeout()      得到阻塞套接字操作的超时时间
    面向文件的套接字的函数
    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件
    send 和 sendall 的区别:
    对于程序员来说,用起来是没有什么区别的
    实际上,在socket底层对于两个方法的封装有却别:
     send(num) 此方法会尝试先发送n个数据(n<num),接下来再尝试发送num-n
    sendall(num)  此方法会尝试一次性将num个数据发送出去
    (重点):
    setblocking(True)  阻塞
    setblocking(False) 非阻塞
    settimeout(int)    针对阻塞状态,设置一个延时等待
    gettimeout()       获得延时的时间

    服务器可以开多个端口,可以设置阻塞时间:

    import socket
    sk = socket.socket()
    sk.setblocking(True)  # 设置 阻塞非阻塞
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    # 允许一个端口号重用的解决方法
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    while 1:
        try:
            conn,addr = sk.accept()
            print(123)
            conn.recv(1024)
            print(123)
            # while 1:
            #     try:
            #         msg = conn.recv(1024)
            #         print(msg)
            #         break
            #     except BlockingIOError:
            #         pass
        except BlockingIOError:
            print(789)
    
    # print(conn.getpeername())
    # print(sk.getsockname())
    # print(sk.getsockopt())
    
    
    conn.close()
    sk.close()
    服务器:
    import socket
    
    sk = socket.socket()
    sk.setblocking(True) # 设置 阻塞非阻塞
    sk.settimeout(5)   #阻塞5秒,超过5秒接收不到会报错
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    # 允许一个端口号重用的解决方法
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    print(123)
    print(sk.gettimeout())  # 获取阻塞时间
    conn,addr = sk.accept()
    print(456)
    conn.close()
    sk.close()
    服务器
    客户端:
    import socket
    import time
    sk = socket.socket()
    sk.connect_ex(('127.0.0.1',9090))
    
    time.sleep(2)
    sk.send(b'qweqweqweqweqweqweqw')
    sk.close()
    客户端
    设置服务器客户端不用写encode,decode方法(调用类)
    import socket
    
    class MySocket(socket.socket):
        def __init__(self,encoding = 'utf-8'):
            self.encoding = encoding
            super(MySocket, self).__init__(type=socket.SOCK_DGRAM)
    
        def my_recv(self,num):
            msg,addr = self.recvfrom(num)
            return msg.decode(self.encoding),addr
    
        def my_send(self,msg,addr):
            return self.sendto(msg.encode(self.encoding),addr)
    View Code

    验证客户连接:

    方法1:固态加盐:

    服务器:
    import socket
    import hashlib
    sk = socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    conn,addr = sk.accept()
    key = '天王盖地虎'      # 这个是固定盐
    ch = '这是一个随机字符串'
    conn.send(ch.encode('utf-8'))   # 把随机字符串发给client
    md5_obj = hashlib.md5(key.encode('utf-8'))
    md5_obj.update(ch.encode('utf-8'))
    re = md5_obj.hexdigest()
    # 固定的用盐的加密方式
    client_re = conn.recv(1024).decode('utf-8')   # 接收client端加密后的结果
    
    if re == client_re:
        print('你好机油!')
        '''收发数据的逻辑'''
    else:
        print('你根本不是老司机')
        conn.close()
    sk.close()
    服务器
    客户端:
    import socket
    import hashlib
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    
    key = '天王盖地虎'
    ch = sk.recv(1024)
    md5_obj = hashlib.md5(key.encode('utf-8'))
    md5_obj.update(ch)
    re = md5_obj.hexdigest()
    sk.send(re.encode('utf-8'))
    sk.close()
    客户端

    方法2;用真实的随机字符串

    import socket
    import hashlib
    import os
    sk = socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    conn,addr = sk.accept()
    key = '天王盖地虎'  # 这个是固定盐
    ch = os.urandom(10)
    conn.send(ch)  # 把随机字符串发给client
    md5_obj = hashlib.md5(key.encode('utf-8'))
    md5_obj.update(ch)
    re = md5_obj.hexdigest()
    # 固定的用盐的加密方式
    client_re = conn.recv(1024).decode('utf-8')  # 接收client端加密后的结果
    
    if re == client_re:
        print('你好机油!')
        '''收发数据的逻辑'''
    else:
        print('你根本不是老司机')
        conn.close()
    sk.close()
    服务器
    客户端:
    import socket
    import hashlib
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    
    key = '天王盖地虎'
    ch = sk.recv(1024)
    md5_obj = hashlib.md5(key.encode('utf-8'))
    md5_obj.update(ch)
    re = md5_obj.hexdigest()
    sk.send(re.encode('utf-8'))
    
    sk.close()
    客户端

    方法3:用hmac模块实现机密:

    服务器:
    import socket
    import hashlib
    import os
    import hmac
    sk = socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    conn,addr = sk.accept()
    key = '天王盖地虎'  # 这个是固定盐
    ch = os.urandom(10)
    conn.send(ch)   # 把随机字符串发给client
    obj = hmac.new(key.encode('utf-8'),ch)
    re = obj.digest()
    # 固定的用盐的加密方式
    client_re = conn.recv(1024)  # 接收client端加密后的结果
    
    if re == client_re:
        print('你好机油!')
        '''收发数据的逻辑'''
    else:
        print('你根本不是老司机')
        conn.close()
    sk.close()
    服务器
    客户端:
    import socket
    import hashlib
    import hmac
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    
    key = '天王盖地虎'
    ch = sk.recv(1024)
    obj = hmac.new(key.encode('utf-8'),ch)
    re = obj.digest()
    sk.send(re)
    
    sk.close()
    客户端
    hmac模块(算法模块)
    定义HMAC需要一个加密用散列函数(表示为H,可以是MD5或者SHA-1)和一个密钥K。我们用B来表示数据块的字节数。(以上所提到的散列函数的分割数据块字长B=64),用L来表示散列函数的输出数据字节数(MD5中L=16,SHA-1中L=20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比B大,则首先用使用散列函数H作用于它,然后用H输出的L长度字符串作为在HMAC中实际使用的密钥。一般情况下,推荐的最小密钥K长度是L个字节。
    使用hmac和普通hash算法非常类似。hmac输出的长度和原始哈希算法的长度一致。需要注意传入的key和message都是bytes类型,str类型需要首先编码为bytes。
    HMAC的应用
    hmac主要应用在身份验证中,它的使用方法是这样的:
    (1) 客户端发出登录请求(假设是浏览器的GET请求)
    (2) 服务器返回一个随机值,并在会话中记录这个随机值
    (3) 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
    (4) 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法
    在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的hmac结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使hmac只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了hmac算法,比如php的mhash、python的hmac.py、java的MessageDigest类,在web验证中使用hmac也是可行的,用js进行md5运算的速度也是比较快的。
    socketserver模块     (解决tcp)
     现在没有学习并发编程,现在解决不了tcp协议中一个服务器同时连接多个客户端
    socketserver,看其名字,就知道是一个socket的服务器模块的使用,在这个模块中,主要也就是实现服务器类的相关功能,在其中,也就是将socket模块和select模块进行了封装,从而创建了一些基类供人使用。
    SocketServer内部使用 IO多路复用以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
    原理:
    1、服务器启动socket监听端口
    2、服务器内部利用while循环监视句柄的变化
    3、客户端请求
    4、服务器为这个请求分配线程或进程(底层调用select)。
    SocketServer模块有两个方法ThreadingTCPServer和ForkingTCPServer,分别创建线程或者进程
    服务器:
    import socketserver
    #sk  conn
    import json
    import hashlib
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            while 1:
                dic_str = self.request.recv(1024).decode('utf-8') # 接收到序列化的字典
                dic = json.loads(dic_str) # 反序列化字典,字典中有用户名和密码
                # 用户名当盐   加上密码去做md5
                with open('info',encoding='utf-8') as f:
                    for user_info in f:# 旭哥 | 7d79a61dd0bd94a3df2f765ac12fe492
                        username,pawd = user_info.split('|')
                        if username.strip() == dic['username']:  # 先去对比用户名正确与否
                            '''如果用户名存在的情况下
                             加密方式 : 用户名当盐,对密码加密'''
                            md5_obj = hashlib.md5(dic['username'].encode('utf-8'))
                            md5_obj.update(dic['passwd'].encode('utf-8'))
                            re = md5_obj.hexdigest()
                            if re == pawd.strip(): # 拿加密完的密码密文对比文件中密码的密文
                                self.request.send(b'success!')
                                '''通信的逻辑'''
                            else:
                                '''失败,返回给客户端信息.....'''
                                self.request.send(b'failed!')
                            break
                    else:
                        '''对应for   如果用户名不存在'''
                        print('用户不存在!')
    
    server = socketserver.TCPServer(('127.0.0.1',9090),Myserver)  # 绑定一个服务
    server.serve_forever()   # 永久性开启服务
    服务器
    客户端:
    import socket
    import time
    import json
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    dic = {'username':None,'passwd':None}
    while 1:
        username = input('用户名>>>')
        passwd = input('密码>>>')
        dic['username'] = username
        dic['passwd'] = passwd
        dic_str = json.dumps(dic)
        sk.send(dic_str.encode('utf-8'))
        print(sk.recv(1024))
    sk.close()
    客户端
  • 相关阅读:
    php的四种算法
    laravel框架安装过程中遇到的问题
    json_decode转码无效
    php通过mysqli链接mysql数据库
    jq函数绑定与解绑
    redis的运行机制
    数据库设计的三范式
    MYSQL数据库索引
    PHP超全局变量数组
    vue的settings.json格式化配置
  • 原文地址:https://www.cnblogs.com/ls13691357174/p/9349720.html
Copyright © 2011-2022 走看看