No.30socketserver 模块
Posted elliottwave
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了No.30socketserver 模块相关的知识,希望对你有一定的参考价值。
No.30
今日概要
- 非阻塞IO模型
- 验证客户端合法性
- socketserver模块
- 计算器
内容回顾
-
TCP协议的粘包问题
- 本质:接收的边界不清晰
- 解决方式:自定义协议
-
文件发送自定义协议
- 先发送报头字典的字节长度
- 再发送字典(字典中包含文件的名字、大小.....)
- 最后发送文件内容
-
TCP和UDP的特点
- TCP
- 面向连接
- 流式传输、无边界、粘包
- 可靠
- 需连接、有回执、慢
- 全双工通信
- 应用场景:邮件、文件、http、web......
- 面向连接
- UDP
- 面向数据
- 整条传输、不拆包、不粘包
- 不可靠
- 不连接、无回执、快
- 一对一、一对多、多对一、多对多高效通信
- 应用场景:即时聊天工具、在线观看视频
- 面向数据
- TCP
-
三次握手
-
accept 接受过程中等待客户端的连接
-
connect 客户端会发起一个syn连接请求
- 收到 server端 ack响应的同时还会收到 server端 syn的连接请求
- client 端进行回复ack之后,就建立了一个tcp协议的连接
-
三次握手的过程在代码中是由accept和connet共同完成的,具体的细节在socket中没有体现出来。
-
-
四次挥手
- server和client端在代码中都有close方法
- 每一端发起的close操作都是一次fin断开请求,得到‘断开确认’ack之后,就可以结束一端的数据发送
- 如果两端都发起close,那么就是两次请求和两次回复,一共是四次操作。可以结束两端的数据发送,表示连接断开。
内容详细
1.非阻塞IO模型
# Server.py 阻塞IO模型
import socket
sk = socket.socket()
sk.bind((‘127.0.0.1‘, 9000))
sk.listen()
conn, addr = sk.accept() # 阻塞:数据不来一直等
print(conn)
# Server.py 非阻塞IO模型
import socket
sk = socket.socket()
sk.bind((‘127.0.0.1‘, 9000))
sk.setblocking(False) # 设置一个阻塞为非
sk.listen()
conn_lst = []
del_lst = []
while True:
try:
conn, addr = sk.accept()
print(conn)
conn_lst.append(conn)
except BlockingIOError:
for c in conn_lst:
try:
msg = c.recv(1024).decode(‘utf-8‘)
if not msg:
del_lst.append(c)
continue
print(msg)
c.send(msg.upper().encode(‘uft-8‘))
except BlockingIOError:
pass
for c in del_lst:
conn_lst.remove(c)
del_lst.clear()
# Client.py
import time
import socket
sk = socket.socket()
sk.connect((‘127.0.0.1‘, 9000))
while True:
sk.send(b‘alex‘)
msg = sk.recv(1024)
print(msg)
time.sleep(0.2)
虽然非阻塞提高了CPU的利用率,但是耗费CPU做了很多无用功。
2.验证客户端合法性
# Server.py
import os
import socket
import hashlib
def get_md5(secret_key, rad):
md5 = hashlib.md5(secret_key)
md5.update(rad)
res = md5.hexdigest()
return res
def chat(conn):
while True:
msg = conn.recv(1024).decode(‘utf-8‘)
print(msg)
conn.send(msg.upper().encode(‘utf-8‘))
sk = socket.socket()
sk.bind((‘127.0.0.1‘, 9000))
sk.listen()
secret_key = b‘alex‘
while True:
conn, _ = sk.accept()
rad = os.urandom(32)
conn.send(rad)
md5_code = get_md5(secret_key, rad)
ret = conn.recv(1024).decode(‘utf-8‘)
print(ret)
if md5_code == ret:
print(‘合法客户端‘)
chat(conn)
else:
print(‘不合法客户端‘)
conn.close()
sk.close()
# Client.py
import time
import socket
import hashlib
def get_md5(secret_key, rad):
md5 = hashlib.md5(secret_key)
md5.update(rad)
res = md5.hexdigest()
return res
def chat(sk):
while True:
sk.send(b‘hellow‘)
msg = sk.recv(1024).decode(‘utf-8‘)
print(msg)
time.sleep(0.5)
sk = socket.socket()
sk.connect((‘127.0.0.1‘, 9000))
secret_key = b‘alex‘
rad = sk.recv(32)
md5_code = get_md5(secret_key, rad)
sk.send(md5_code.encode(‘utf-8‘))
chat(sk)
sk.close()
hmac模块
import os
import hmac
secret_key = b‘alex‘
random_seq = os.urandom(32)
hmac = hmac.new(secret_key, random_seq)
ret = hmac.digest() # 直接获得字节流对象
print(ret)
print(len(ret))
3.socketserver 模块
直接实现TCP协议可并发的Server端
# Server.py
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self): # 自动触发 handle 方法,并且self.request == conn
print(self.request)
while True:
msg = self.request.recv(1024).decode(‘utf-8‘)
print(msg)
self.request.send(msg.upper().encode(‘utf-8‘))
server = socketserver.ThreadingTCPServer((‘127.0.0.1‘, 9000), Myserver)
server.serve_forever()
4.计算器
import re
from functools import reduce
def exp_format(exp):
exp = exp.replace(‘--‘, ‘+‘)
exp = exp.replace(‘+-‘, ‘-‘)
exp = exp.replace(‘++‘, ‘+‘)
exp = exp.replace(‘-+‘, ‘-‘)
return exp
def mul_div(atom_exp): # 最基础两个数的乘除法
if ‘*‘ in atom_exp:
a, b = atom_exp.split(‘*‘)
res = float(a) * float (b)
else:
a, b = atom_exp.split(‘/‘)
res = float(a) / float(b)
return res
def cal_muldiv(exp):
com = re.compile(‘d+(.d+)?[*/]-?d+(.d+)?‘)
while True:
obj = com.search(exp)
if obj:
atom_exp = obj.group()
res = mul_div(atom_exp)
exp = exp.replace(atom_exp, str(res))
exp = exp_format(exp)
else:
break
return exp # 只有加减法的表达式
def cal_addsub(exp): # 计算加减法
ret = re.findall(‘[-+]?d+(?:.d+)?‘, exp)
count = reduce(lambda x,y:float(x)+float(y),ret)
return count
def cal(no_bracket_exp): # 完成最基本的加减乘除四则运算
sub_exp = cal_muldiv(no_bracket_exp) # 做乘除法得到加减表达式
sub_exp = exp_format(sub_exp) # 符号整理
ret = cal_addsub(sub_exp) # 计算加减法
return ret
def remove_backet(exp): # 去括号
while True:
ret = re.search(‘([^()]+)‘, exp)
if ret:
no_bracket_exp = ret.group()
ret = cal(no_bracket_exp)
exp = exp.replace(no_bracket_exp, str(ret))
else:
return exp
def main(exp):
exp = exp.replace(‘ ‘, ‘‘)
exp = exp_format(exp)
exp = remove_backet(exp)
return cal(exp)
以上是关于No.30socketserver 模块的主要内容,如果未能解决你的问题,请参考以下文章