一、基本SocketServer编写
# 网络协议的最底层就是socket,基于原有socket模块,又封装了一层,就是socketserver # socketserver 为了实现tcp协议,server端的并发.
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print("handle方法被执行了...") # ThreadingTCPServer(ip端口号,自定义的类) 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(b"you can you up no can no bb") msg = sk.recv(1024) print(msg.decode("utf-8")) sk.close()
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): """ print(self.request) print(self.client_address) self.request <==> conn <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 49116)> self.client_address <==> addr ('127.0.0.1', 49116) """ conn = self.request while True: # 接受数据 msg = conn.recv(1024) msg2 = msg.decode("utf-8") print(msg2) conn.send(msg2.upper().encode()) print("handle方法被执行了...") # ThreadingTCPServer(ip端口号,自定义的类) server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer ) # 建立连接,循环调用 server.serve_forever()
import socket sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) # 处理收发数据的逻辑 sk.close()
二、hashlib模块
1、md5算法
1.1、基本用法
# (1)创建一个md5算法的对象 hm = hashlib.md5() # (2)把要加密的字符串通过update更新到hm这个对象中运算 hm.update("123456".encode("utf-8")) # 里面的数据必须是二进制字节流 # (3)获取32位16进制字符串 res = hm.hexdigest() print(res , len(res))
1.2、加盐
# 加盐(加key => Xboy_) 加一个关键字配合原字符进行加密, # 使密码更复杂,不容易被破解
hm = hashlib.md5("Xboy_wangwen".encode()) hm.update("123456".encode()) res = hm.hexdigest() print(res , len(res))
1.3、动态加盐
res = str(random.randrange(100000,1000000)) print(res) hm = hashlib.md5(res.encode("utf-8")) hm.update("123456".encode()) res = hm.hexdigest() print(res)
2、sha算法
""" sha 算出来的十六进制的串是40位,加密稍慢,安全性稍高 md5 算出来的十六进制的串是32位,加密很快,安全性稍差 """
hs = hashlib.sha1() hs.update("我最是牛逼的&#*($&*(#&%*(&%*&%(#%&".encode()) res = hs.hexdigest() print(res, len(res)) # 31673dd65f81fddaae07b4240cbb04af047b7496
hs = hashlib.sha512() hs.update("123456".encode()) res = hs.hexdigest() print(res , len(res))
3、hmac
"""hmac 加密算法更加复杂,不容易破解"""
import hmac # hmac.new(盐,密码) key = b"a" msg = b"123456" hn = hmac.new(key,msg) res = hn.hexdigest() print(res, len(res)) # 32位长度 十六进制的字符串
3.1、动态加盐
import os """ # os.urandom 返回随机的二进制字节流 res = os.urandom(32) print(res,len(res)) """ key = os.urandom(32) msg = b"123" hn = hmac.new(key,msg) res = hn.hexdigest() print(res, len(res))
三、文件校验
import hashlib import os """ mode => r read(数字->字符个数) mode => rb read(数字->字节个数) 字节的个数 <=> 文件的大小 """ # (1) 针对于小文件进行内容校验 def check_md5(filename): hs = hashlib.md5() with open(filename,mode="rb") as fp: hs.update(fp.read()) return hs.hexdigest() res1 = check_md5("ceshi1.txt") res2 = check_md5("ceshi2.txt") print(res1,res2) # (2) 针对于大文件进行内容校验 # 可以通过update 把字符串分段进行加密 # 常规方法 strvar = "今天是星期五,好开心了,下周一又要考试了." hm = hashlib.md5() hm.update(strvar.encode()) res = hm.hexdigest() print(res) # 分段更新加密 hm = hashlib.md5() hm.update("今天是星期五,好开心了,".encode()) hm.update("下周一又要考试了.".encode()) res = hm.hexdigest() print(res)
# 方法一 def check_md5(filename): hs = hashlib.md5() with open(filename,mode="rb") as fp: while True: content = fp.read(10) # 一次最多读取10个字节 if content: # 分批进行字符串密码更新 hs.update(content) else: break return hs.hexdigest() res1 = check_md5("ceshi1.txt") res2 = check_md5("ceshi2.txt") print(res1,res2)
# 方法二 def check_md5(filename): hs = hashlib.md5() # 计算文件大小=>返回字节数 filesize = os.path.getsize(filename) with open(filename,mode="rb") as fp: while filesize: content = fp.read(10) # 一次最多读取10个字节 hs.update(content) # 按照实际的字节个数读取 filesize -= len(content) return hs.hexdigest() res1 = check_md5("ceshi1.txt") res2 = check_md5("ceshi2.txt") print(res1,res2)
四、服务器的合法性校验
# ### 服务端2(支付宝) import socket import os import hmac def auth(conn,secret_key): # 随机产生32位的二进制字节流 msg = os.urandom(32) conn.send(msg) hn = hmac.new(secret_key.encode(),msg) res_server = hn.hexdigest() print(res_server) # 461ea12ca8ef475caeedd0c742f4295e # 服务端接受客户端发送过来的数据进行验证; res_client = conn.recv(1024).decode("utf-8") if res_client == res_server: print("你是合法的服务端用户") return True else: print("不是合法的服务端用户") return False sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind( ("127.0.0.1",9000) ) sk.listen() # 三次握手 conn,addr = sk.accept() # 处理收发数据的逻辑 secret_key = "芝麻开门" res = auth(conn,secret_key) # 在验证成功之后,给客户端发送状态码 if res: conn.send("状态码是200:付款成功~".encode()) # 四次挥手 conn.close() # 退还端口 sk.close()
# ### 服务端1(公司) import socket import hmac def auth(sk,secret_key): # 处理收发数据的逻辑 msg = sk.recv(32) hn = hmac.new(secret_key.encode(),msg) res = hn.hexdigest() # 把客户端加密的字符串发送给服务端进行验证 sk.send(res.encode("utf-8")) sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) # 处理收发数据的逻辑 secret_key = "芝麻开门123" auth(sk,secret_key) # 在验证成功之后,接受服务端给我的验证码 res = sk.recv(1025).decode("utf-8") print(res) sk.close()
五、tcp登录
import socket import hashlib import json def get_md5_code(usr,pwd): hs = hashlib.md5(usr.encode()) hs.update(pwd.encode()) return hs.hexdigest() # res = get_md5_code("tianqi","777") # print(res) sk = socket.socket() sk.bind( ("127.0.0.1", 9001) ) sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑 msg = conn.recv(1024).decode() # 把反解之后的字符串恢复原来的数据格式变成字典通过json dic = json.loads(msg) print(dic) sign = False with open("userinfo.txt",mode="r",encoding="utf-8") as fp: for line in fp: usr,pwd = line.strip().split(":") # print(usr,pwd) if usr == dic["username"] and pwd == get_md5_code(dic["username"],dic["password"]): # 制定状态码 0=>失败 1=>成功 res = {"code":1} res_msg = json.dumps(res).encode() conn.send(res_msg) sign = True break if sign == False: # 发送错误的状态码 res = {"code":0} res_msg = json.dumps(res).encode() conn.send(res_msg) conn.close() sk.close()
import socket import json sk = socket.socket() sk.connect( ("127.0.0.1", 9001) ) # 处理收发数据的逻辑 usr = input("请输入您的用户名:") pwd = input("请输入您的密码:") dic = {"username":usr,"password":pwd,"operate":"login"} # 先通过json变成字符串 res = json.dumps(dic) # json字符串 -> 字节流 bytes_msg = res.encode() # 把字节流发送给服务端 sk.send(bytes_msg) # 接受服务端发送过来的数据 res_msg = sk.recv(1024).decode() dic_code = json.loads(res_msg) if dic_code["code"]: print("恭喜你~ 登录成功") else: print("i am so sorry ~ 登录失败") sk.close()
zhangsan:e7a6d0f97db7ea1c4a0c1c137cbf771c
lisi:c79439cf9abcbd6c6d46e45766a9e64a
tianqi:907e690908517e5cededf8db13b28237
六、SocketServer源码分析
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print('conn is: ', self.request) # 相当于粘包的conn链接 print('addr is: ', self.client_address) # 相当于addr地址 while True: # 通信循环 try: # 收消息 data = self.request.recv(1024) if not data:break print('收到客户端的消息是', data) # 发消息 self.request.sendall(data.upper()) except Exception as e: print(e) break if __name__ == '__main__': # 多线程的tcp服务端 实例化得到s # 来一个客户端就调用 handle方法创建一个实例通信 s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer) # 永远服务 相当于外层循环 s.serve_forever() # 链接循环
# socketserver 到底是个什么东西 # socketserver分为两大类 # server类 和 request类
1、server类
""" 一类是帮你处理链接的,可以理解为server类(Ctrl+左键点进socketserver向下翻一点就可以 看见一个类的继承关系) 隶属于server类的基本5个 +------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+ """
1.1、关于以上几个类
""" BaseServer 祖宗哪个类 TCPServer 处理tcp链接的 UDPServer 处理udp链接的 TCPServer继承BaseServer UDPServer继承TCPServer 看似没有什么关系 但是写这个模块的人就是这样写的 class BaseServer class TCPServer(BaseServer) class UDPServer(TCPServer): UnixStreamServer Stream 流 也是处理tcp链接的 用在Unix系统不能跨平台 一般不用 UnixDatagramServer Datagram 数据包 处理udp的 用在Unix系统不能跨平台 一般不用 UnixStreamServer 继承 TCPServer UnixDatagramServer 继承 UDPServer """
2、request类:处理通信
""" __all__ = ["BaseServer", "TCPServer", "UDPServer", "ThreadingUDPServer", "ThreadingTCPServer", "BaseRequestHandler", "StreamRequestHandler", "DatagramRequestHandler", "ThreadingMixIn"] __all__内置变量 每一个字符串都代表了一个类名 if hasattr(socket, "AF_UNIX"): __all__.extend(["UnixStreamServer","UnixDatagramServer", "ThreadingUnixStreamServer", "ThreadingUnixDatagramServer"]) 如果socket下有 AF_UNIX 属性 就把__all__扩充 """
有一个 BaseRequestHandler
源码:
class BaseRequestHandler: def handle(self): pass """ This class is instantiated for each request to be handled. The constructor sets the instance variables request, client_address and server, and then calls the handle() method. To implement a specific service, all you need to do is to derive a class which defines a handle() method. 每一个请求来都会呼叫handle()方法 你需要做的就是继承一个类,定义一个handle()方法 就是说自己定义的类一定要继承socketserver.BaseRequestHandler并重写handle方法 """
类StreamRequestHandler 和 类DatagramRequestHandler
""" StreamRequestHandler(BaseRequestHandler): pass DatagramRequestHandler(BaseRequestHandler): pass """
继承关系:
""" +--------------------+ | BaseRequestHandler | +--------------------+ | | (继承) | | (继承) | | +----------------------+ +------------------------+ | StreamRequestHandler | | DatagramRequestHandler | +----------------------+ +------------------------+ """
3、__all__中的其他类
__all__ = ["BaseServer", "TCPServer", "UDPServer", "ThreadingUDPServer", "ThreadingTCPServer", "BaseRequestHandler", "StreamRequestHandler", "DatagramRequestHandler", "ThreadingMixIn"] if hasattr(os, "fork"): __all__.extend(["ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"]) 比如: # "ForkingUDPServer","ForkingTCPServer", "ForkingMixIn" # "ThreadingUDPServer", "ThreadingTCPServer",
ForkingUDPServer
ForkingTCPServer
if hasattr(os, "fork"): class ForkingUDPServer(ForkingMixIn, UDPServer): pass class ForkingTCPServer(ForkingMixIn, TCPServer): pass # ForkingUDPServer 继承 ForkingMixIn UDPServer(即处理udp链接的) # ForkingTCPServer 继承 ForkingMixIn TCPServer(即处理tcp链接的) class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass # ThreadingUDPServer 继承 ThreadingMixIn UDPServer(即处理udp链接的) # ThreadingTCPServer 继承 ThreadingMixIn TCPServer(即处理tcp链接的)
那么到现在类的关系如图所示
在我们自己写的代码中
s = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)
调用ThreadingTCPServer这个类,加括号,传两个参数实例化
源码:
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
""" 自己的实例化传两个参数就是传给构造方法,这个类没有构造方法找继承的类的构造方法 ThreadingMixIn没有构造方法找TCPServer """
源码:
address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = False # 地址默认不重新使用 # ('127.0.0.1',8080),MyServer def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): """Constructor. May be extended, do not override.""" BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type)
# 这里又调用了父类的构造方法,也是把('127.0.0.1',8080),MyServer 传入进去
源码:
class BaseServer: def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address # self 就是实例出来的s self.RequestHandlerClass = RequestHandlerClass
# 可以在自己的代码中打印一下self.server_address 和 self.RequestHandlerClass
# 执行完构造方法后,我们再回到TCPServer这个类继续往下走
源码:
address_family = socket.AF_INET socket_type = socket.SOCK_STREAM self.socket = socket.socket(self.address_family,self.socket_type) # 就是创建一个socket对象
# 接下来就是绑定监听,就实例化完了
""" 现在自己的类中找也就是ThreadingTCPServer 结果没找到 再去左边的父类ThreadingMixIn找 结果也没找到 再去右边的父类TCPServer找,结果还是没找到 最后取祖宗类BaseServer找,发现有serve_forever方法 """
为什么调用serve_forever就一直循环?
""" while循环里有一个 return self._handle_request_noblock() 就是循环接收链接的 找_handle_request_noblock还是从self也就是s在的类ThreadingTCPServer一层一层找 最后还是在类BaseServer中找到的 """
""" 在self._handle_request_noblock()函数往下走,有一个 request, client_address = self.get_request() 找self.get_request()的方法和上面一样就是一层一层找 最后在TCPServer类中找到了 这里有一个return self.socket.accept() 这个就NB了 self.socket就是上面的socket对象 .accept() 返回的就是conn addr """
""" 还是从s的跟找 最终在类ThreadingMixIn 找到了def process_request(self, request, client_address): """
源码:
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
""" 找process_request_thread 发现在当前类找到了process_request_thread process_request_thread这个类又调用了self.finish_request(request, client_address)方法 finish_request方法最终在祖宗类BaseServer类中找到了 它调用了 self.RequestHandlerClass(request, client_address, self) RequestHandlerClass 就是我们传入的第二个参数Myserver 实际上就是把request client_address self 传递到我们自己的类 这之前都是在做的通信循环 那么传了3个参数我就要找构造方法,我自己没有构造方法,就要去父类找 找 BaseRequestHandler 的 __init__方法 """
源码:
def __init__(self, request, client_address, server):
""" 构造函数接收3个参数 触发__init__方法就会触发self.handle() 接下来就调用self.handle() handle就是控制通信循环的 self.request就是conn """
整个流程结束