No.30socketserver 模块

Posted elliottwave

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了No.30socketserver 模块相关的知识,希望对你有一定的参考价值。

No.30

今日概要

  • 非阻塞IO模型
  • 验证客户端合法性
  • socketserver模块
  • 计算器

内容回顾

  • TCP协议的粘包问题

    • 本质:接收的边界不清晰
    • 解决方式:自定义协议
  • 文件发送自定义协议

    • 先发送报头字典的字节长度
    • 再发送字典(字典中包含文件的名字、大小.....)
    • 最后发送文件内容
  • TCP和UDP的特点

    • TCP
      • 面向连接
        • 流式传输、无边界、粘包
      • 可靠
        • 需连接、有回执、慢
      • 全双工通信
      • 应用场景:邮件、文件、http、web......
    • UDP
      • 面向数据
        • 整条传输、不拆包、不粘包
      • 不可靠
        • 不连接、无回执、快
      • 一对一、一对多、多对一、多对多高效通信
      • 应用场景:即时聊天工具、在线观看视频
  • 三次握手

    • 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 模块的主要内容,如果未能解决你的问题,请参考以下文章

Day43:socketserver模块MySQL

数据库:socketserver模块MySQL

python网络编程 socketserver模块

八. 网络编程( socketserver 模块 初级使用)

Python-SocketServer模块

socketserver模块使用与源码分析