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个类的继承关系:
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和普通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、实现效果:
链接是合法的,暗号对上了:
知道有暗号,但是暗号没对上:
压根就不知道有暗号:
这样就成功完成了对链接的认证,合法就接入,不合法就断开。
需要测试代码的Python学习伙伴们请私聊或评论