Python之网络编程(socketserver模块详解、链接认证)

socketserver本质是基于socket进行的一个封装,将多线程并发功能集成到一个新的模块里,就叫socketserver;
它用来解决TCP套接字无法并发的问题,也就是无法一个服务端不能同时服务多个客户端的问题(UDP没有此问题,因为它不需要链接)

多线程对多客户端

多线程对多客户端

socketserver

分类(2种)

1、server类:处理链接
BaseServer 处理链接必须
TCPServer 处理流式(TCP)链接
UDPServer 处理数据报式(UDP)链接

2、request类:处理通信必须
BaseRequestHandler 处理通信
StreamRequestHandler 处理流式通信,即TCP
DatagramRequestHandler 处理数据报式通信,即UDP

12个类的继承关系(原理)

socketserver模块的源码里面,主要只有12个类,这12个类实现了socketserver的所有功能,下面是这12个类的继承关系:
12个类的继承关系

socketserver的实现效果

1、服务端测试代码:

#导入socketserver模块
import socketserver

#定义一个类,该类必须继承socketserver下的BaseRequestHandler类
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):  #必须定义一个handle方法
        print('conn is ',self.request)  #self.request是获取的链接,相当于conn
        print('addr is ',self.client_address)  #self.client_address则是获取的地址,相当于addr

        #循环收发消息
        while True:
            try:  #异常处理
                #收消息
                data = self.request.recv(1024)  #1024字节
                if not data:continue  #如果是空就重新输入
                if data == 'quit':break  #退出
                print('客户端发来的消息是:',data.decode('utf-8'),'\n')

                #发消息
                self.request.sendall(data.upper())  #将受到的数据转化为大写后发回客户端
            except Exception as e:
                print(e)  #打印错误
                break

if __name__ == '__main__':
    #多线程的TCP服务端,即可以同时开启多个链接运行,也就是多客户端并发效果;
    #再将这样的功能实例化成一个对象s
    s = socketserver.ThreadingTCPServer(('192.168.43.247',8080),MyServer)
    #第一个参数为服务端的ip地址,用元组传进;第二个参数是刚刚定义的那个类
    
    s.serve_forever()  #永远,就是通信循环(链接循环)

2、客户端测试代码:

#客户端就不需要socketserver模块了,用socket模块收发消息就可以了
from socket import *

ip_port = ('192.168.43.247',8080)  #记录ip地址
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

#实现循环发收消息
while True:
    msg = input('请输入>>>')
    tcp_client.send(msg.encode('utf-8'))  #发消息
    print('客户端已发送出消息!')  #接收到的消息要解码
    #接收消息
    data = tcp_client.recv(buffer_size)  #buffer_size=1024默认参数,表示字节格式
    print('服务端发回的消息是:',data.decode('utf-8'))

tcp_client.close()  #关闭socket对象

3、多个相同的客户端
当然我们需要准备多个相同的客户端才能看到效果
多个客户端
4、测试结果:
测试结果
断开并发
可以看到,成功实现了tcp套接字的并发效果,只有一个客户端,可以同时服务多个客户端,并且,四个客户端可以分别断开,不影响其他客户端的使用

认证链接的合法性

所谓认证客户端合法性,就是通过一条或多条规则,让服务端和客户端做一个接口验证;

如果验证成功就表明链接的合法的,就让对方的链接接入进来;错了当然就不允许接入

说白了就像对暗号一样,如果对了,就说明是自己人,就允许你进入;错了,就不准进

加盐(hmac)

我们通过哈希算法,可以验证一段数据是否有效,方法就是对比该数据的哈希值,来判断用户的暗号是否正确;

为了防止黑客通过彩虹表根据哈希值反推我们的暗号,在计算哈希的时候,不能只通过对原始数据进行比较,还需要增加一些salt来使得相同的输入也能得到不同的哈希,这样,就可以让我们的数据更安全;

一般都是我们随机生成一个salt(一段随机码),然后与我们自定义的一个暗号进行混合运算,这个就是hmac算法,它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中;

hmac算法练习代码:

import hmac

message = b'ViewIn is strongest'  #自定义一段文字
secret_key = b'Very'  #自定义暗号的关键文字

h = hmac.new(secret_key, message, digestmod='MD5')  #将前两段文字用hmac算法混个,模式为MD5
#如果文字很长,可以多次使用h.update(msg)操作

print(h.hexdigest())  #以十六进制形式输出h

练习结果:
hmac算法
可见使用hmac和普通hash算法都非常类似,hmac输出的长度和原始哈希算法的长度一样长。不过传入的key和message都必须是bytes类型,str类型需要首先编码为bytes;
所以上文练习代码里在字符串前加了一个b,表示是二进制形式的变量

对暗号

1、我们要完成验证链接合法性,就必须先用hmac对生成我们的暗号

#验证链接的合法性
def conn_auth(conn):
    print('开始验证新链接的合法性')
    msg=os.urandom(32)  #生成一个32位随机码
    conn.sendall(msg)  #发送随机码给想接入的链接
    h=hmac.new(secret_key,msg)  #将随机码与我们自定义的暗号混合(加盐操作)
    digest=h.digest()  #再将混合后的内容转化成二进制
    respone=conn.recv(len(digest))  #接收对方发来的加盐结果
    return hmac.compare_digest(respone,digest)
    #再返回digest与想接入链接发来的消息做比较的结果,是一个布尔值
    #该布尔值作为对暗号的结果

2、暗号生成完了,自然就是要开始对暗号了

#开始对暗号
def data_handler(conn,bufsize=1024):  #传入链接、可接收内容的大小
    #验证链接合法性
    if not conn_auth(conn):  #传入需要验证的链接到conn_auth
        print('该链接不合法,关闭!!!')
        conn.close()
        return
    
    #暗号对了,通信
    print('链接合法,开始通信!')
    while True:  #通信就是循环收发消息
        data=conn.recv(bufsize)  #收消息
        if not data:continue
        if data == 'quit':break
        conn.sendall(data.upper())  #发消息

经过这两步操作我们就可以对任何传来的链接进行验证了,客户端发来正确的暗号就让其连接上服务端;如果暗号错误或者压根就不知道有暗号,那肯定就连接不上服务端

3、实现效果:
链接是合法的,暗号对上了:
链接合法
知道有暗号,但是暗号没对上:
非法1
压根就不知道有暗号:
不知道有暗号
这样就成功完成了对链接的认证,合法就接入,不合法就断开。
需要测试代码的Python学习伙伴们请私聊或评论

猜你喜欢

转载自blog.csdn.net/Viewinfinitely/article/details/105078301