• python网络编程-异常处理-异常捕获-抛出异常-断言-自定义异常-UDP通信-socketserver模块应用-03


    异常捕获

    异常:程序在运行过程中出现了不可预知的错误,并且该错误没有对应的处理机制,那么就会以异常的形式表现出来

    影响:整个程序无法再正常运行

    异常的结构

    异常的类型

    NameError

    异常的信息

    name 'safaf' is not defined

    异常的位置

    '''
    Traceback (most recent call last):
      File "E:/PyCharm 2019.1.3/ProjectFile/day010/day029/01 异常处理.py", line 1, in <module>
        safaf
    '''
    

    异常的种类

    语法错误

    程序错误编译成一般都会直接有提示,提示就应该直接解决的,出现语法错误一般是基础太差

    逻辑错误

    这种错误是可以被容忍的,逻辑错误无法一眼看出来

    针对逻辑上的错误可以采用异常处理机制来进行捕获

    常见的错误类型

    常见的出错类型 原因
    NameError 名字错误
    SyntaxError 语法错误
    keyError 键不存在
    ValueError 值错误
    IndexError 索引错误

    如何避免

    异常处理

    使用:在你认为可能会出现问题的代码上方用 try 包起来

    注意:try 内部的代码块越少越好

    异常处理能少用就少用,不然该报错的不报错哪里错了你都不知道

    像服务端、客户端断开链接就需要用到异常处理,你不知道他什么时候会断开

    异常处理使用模板(try.. except.. else.. finally..)

    try:
        可能出错的代码
    except 出错类型 as e:  # 将报错信息赋值给e
        错误的处理代码
    except 出错类型2 as e:  # 将报错信息赋值给e
        错误的处理代码
    except 出错类型3 as e:  # 将报错信息赋值给e
        错误的处理代码
        
    # 或者 Exception直接捕获所有的
    except Exception as e:  # 可以匹配所有的错误类型
    	BaseException 是 Exception 的父类,也可以捕获所有类型
        错误的处理代码
    else:
        当被检测的代码没有任何异常发生,才会执行这里面的语句
    finally:
        无论被检测的代码有没有异常发生,都会在代码执行完后执行这里面的代码
    

    案例

    try:
        safaf
    except NameError as e:
        print(e)
    else:
        print("没有报错才会执行我哦~")
    finally:
        print("管你报不报错,我都会执行的!")
    
    # name 'safaf' is not defined
    # 管你报不报错,我都会执行的!
    
    try:
        print("我没错哦~")
    except Exception as e:  # 捕捉所有异常
        print("管你啥错,我都抓了!")
    else:
        print("没有报错才会执行我哦~")
    finally:
        print("管你报不报错,我都会执行的!")
    # 我没错哦~
    # 没有报错才会执行我哦~
    # 管你报不报错,我都会执行的!
    

    补充

    出错后会立即停止代码运行,去与except中的错误类型一个个比较,匹配上了就执行里面的代码,没匹配上就直接报错

    主动抛出异常raise

    if 'egon' == 'DSB':
        pass
    else:
        raise TypeError('尽说大实话')  # 这一行就是报错的位置
        # raise 关键字就是用来主动抛出异常的
    

    断言assert

    断言不成立直接报错

    l = [1, 2, 3]
    assert len(1) < 0  # assert 断言,预言,猜某个数据的状态,猜对了不影响代码执行,猜错了直接报错
    

    自定义异常(继承异常类)

    报错类型,其实对应的就是一个个类(可以自定义拼接异常的格式)

    class MyError(BaseException):
        def __init__(self, msg):
            super().__init__()
            self.msg = msg
        
        def __str__(self):
            return f'----<{self.msg}>----'
    
    raise MyError('自定义的异常')
    # Traceback (most recent call last):
    #   File "E:/PyCharm 2019.1.3/ProjectFile/day010/day029/test.py", line 15, in <module>
    #     raise MyError('自定义的异常')
    # __main__.MyError: ----<自定义的异常>----
    

    UDP通信

    UDP协议又叫用户数据报协议

    它没有双向通道,类似于发短信(只管发,不管对方有没有收到,不需要对方立即回应)

    UDP的程序可以先启动客户端再启动服务端(客户端发数据给服务端之前都没问题)

    UDP类似于发短信

    TCP类似于打电话,你一句我一句的

    普通使用

    服务端

    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)  # type=socket.SOCK_DGRAM 指定成 UDP 协议  type=socket.SOCK_STREAM TCP协议(默认就是,不用指定)
    server.bind(('127.0.0.1', 8080))
    
    # UDP 不需要设置半连接池(server.listen(5)),也没有半连接池的概念
    # UDP 没有双向通道,所以也不需要建立连接(conn, addr = server.accept())
    
    # 直接就是通信循环
    
    while True:  # 这里只需要直接通信(交互)即可
        data, addr = server.recvfrom(1024)
        print("数据:", data.decode('utf-8'))  # 客户端发来的消息
        print("地址:", addr)  # 客户端的地址
    
        re_msg = input("Please input your response msg:").strip()  # 会阻塞在这里,这里过了,才能发出信息,看到下一条信息
        server.sendto(re_msg.encode('utf-8'), addr)  # 向客户端发送消息
    
    
    # 数据: hi
    # 地址: ('127.0.0.1', 64821)
    # Please input your response msg:o  hi
    # 数据: hihihi
    # 地址: ('127.0.0.1', 64823)
    # Please input your response msg:xxixixi
    # 数据: aha e
    # 地址: ('127.0.0.1', 64828)
    # Please input your response msg:emmm?
    # 数据:
    # 地址: ('127.0.0.1', 64828)
    # Please input your response msg:adsa
    

    客户端

    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    # UDP 不需要建立连接(client.connect(('127.0.0.1', 8080)))
    
    server_addr = ('127.0.0.1', 8080)  # UDP sendto发消息时需要一个服务器的地址及端口号
    
    while True:
        msg = input("Please input your msg:").strip()
        client.sendto(msg.encode('utf-8'), server_addr)  # 向服务器发送数据,要附带服务器端地址及端口(基于网络传输的数据都必须是二进制的)
        data, msg_from_server_addr = client.recvfrom(1024)  # 收到消息,且得到地址
    
        print("服务端发来的数据:", data.decode('utf-8'))
        print("服务器端的ip及端口", msg_from_server_addr)
    
    # 窗口1
    # Please input your msg:hi
    # 服务端发来的数据: o  hi
    # 服务器端的ip及端口 ('127.0.0.1', 8080)
    # Please input your msg:
    
    # 窗口2
    # Please input your msg:hihihi
    # 服务端发来的数据: xxixixi
    # 服务器端的ip及端口 ('127.0.0.1', 8080)
    # Please input your msg:
    
    # 窗口3
    # Please input your msg:aha e
    #
    # 服务端发来的数据: emmm?
    # 服务器端的ip及端口 ('127.0.0.1', 8080)
    # Please input your msg:服务端发来的数据: adsa
    # 服务器端的ip及端口 ('127.0.0.1', 8080)
    # Please input your msg:
    

    TCP与UDP之间的区别

    1. UDP 允许发空数据,不会有影响
    2. UDP 直接启动客户端未启动服务端不会报错
    3. UDP 不会有粘包问题(自带报头)
    4. UDP 支持并发

    服务端

    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(('127.0.0.1', 8080))
    
    while True:
        data, addr = server.recvfrom(1024)
        print(data.decode('utf-8'))
    
        server.sendto(data.upper(), addr)
    
    

    客户端

    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    while True:
    
        msg = input(">>>:")
        # -------------------------------------------
        # 1.UDP 允许发空数据,不会有影响
        # -------------------------------------------
        # UDP自带报头,就算数据为空,另一端也能处理
        client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))  # 第二参数,目标服务器地址
    
        # -------------------------------------------
        # 2.UDP 直接启动客户端未启动服务端不会报错
        #   发数据找不到服务端也还是会报错
        # -------------------------------------------
        # 下面两行代码直接注释掉,服务端没启动,都不会报错,只管给服务器发(收没收到不管)
        # data, server_addr = client.recvfrom(1024)
        # print(data)
    
        # -------------------------------------------
        # 3.UDP 不会有粘包问题(自带报头)
        # -------------------------------------------
        # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
        # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
        # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
        #
        # server.sendto(data.upper(), addr)
        # server.sendto(data.upper(), addr)
        # server.sendto(data.upper(), addr)
    
        # -------------------------------------------
        # 4.UDP 支持并发
        #   TCP是保持连接,而UDP不需要保持连接
        #       与一个客户端断开连接才会和下一个客户端建立连接
        # -------------------------------------------
    

    简易版的QQ

    服务器端

    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)
    server.bind(('127.0.0.1', 8080))
    
    while True:
        data, client_addr = server.recvfrom(1024)
        print(data.decode('utf-8'))
    
        msg = input(">>>:").strip()
        server.sendto(msg.encode('utf-8'), client_addr)
    
    # 来自star3的消息:helo
    # >>>:hi
    # 来自star2的消息:aha
    # >>>:haa
    # 来自star的消息:hello world
    # >>>:ha
    # 来自star2的消息:jason nnn
    # >>>:jj
    

    客户端1、2、3共用同一份代码

    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    username = 'star'  # 客户端对应改成 star2 star3
    server_address = ('127.0.0.1', 8080)  # 指定一个发消息的目标服务器
    while True:
        msg = input(">>>:").strip()
    
        msg = f'来自{username}的消息:{msg}'  # 是哪个用户名发出的数据不应该由这里传过去,用户可以随便改,实际意义不大
        '''
        user_dict = {
            "username1": (ip1 + port1),
            "username2": (ip2 + port2),
            "username3": (ip3 + port3),
        }
        # 可以在每个端都存这样一个对照表,根据ip与port就可以知道用户名了
        '''
    
        client.sendto(msg.encode('utf-8'), server_address)
    
        data, server_addr = client.recvfrom(1024)  # server_addr 收到消息的服务端地址
    
        print(data.decode('utf-8'))
    
    # 各个窗口的控制台输入与输出
    # >>>:helo
    # hi
    # >>>:
    
    # >>>:aha
    # haa
    # >>>:jason nnn
    # jj
    
    # >>>:
    # >>>:hello world
    # ha
    # >>>:
    

    概念科普

    • 并发

    看起来像同时运行(直接启动多个UDP客户端)(默认的UDP程序多开就是这个效果)

    • 并行

    真正意义上的同时运行

    socketserver模块科普

    是给服务端用的(客户端还是用socket模块),能够支持服务端的并发

    TCP模拟UDP实现并发

    服务器端

    import socketserver  # 文件名不要和模块冲突了,不然都不知道导哪个了
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # 与客户端进行通信
            # print("来啦 老弟")
            while True:  # 需要保持通信(后续 client.send() 可没有附带服务器地址, connect 被关闭了)
                data = self.request.recv(1024)
                print(self.client_address)  # 客户端地址
                print(data.decode('utf-8'))
                self.request.send(data.upper())
    
    
    if __name__ == '__main__':
        '''只要有客户端连接,会自动交给自定义类中的handle方法去处理'''
        server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 创建一个基于TCP的对象
        server.serve_forever()  # 启动该服务对象
    
    # ('127.0.0.1', 14327)
    # dAddA
    # ('127.0.0.1', 14326)
    # ADD
    # ('127.0.0.1', 14325)
    # ADsafga
    

    客户端

    # TCP 实现UDP
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        res = input(">>>:")
        client.send(res.encode('utf-8'))
    
        data = client.recv(1024)
        print(data.decode('utf-8'))
    
    
    # 窗口1 控制台数据(输入与输出)
    # >>>:dAddA
    # DADDA
    # >>>:
    
    # 窗口2 控制台数据(输入与输出)
    # >>>:ADD
    # ADD
    # >>>:
    
    # 窗口1 控制台数据(输入与输出)
    # >>>:ADsafga
    # ADSAFGA
    # >>>:
    

    socketserver之UDP

    服务器端

    import socketserver  # 文件名不要和模块冲突了,不然都不知道导哪个了
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # 与客户端进行通信
            # while True:  # UDP 不需要通信循环,每次 sendto 都有服务器的地址
            data, sock = self.request
            print(self.client_address)  # 客户端地址
            print(data.decode('utf-8'))
            sock.sendto(data.lower(), self.client_address)
    
    
    if __name__ == '__main__':
        '''只要有客户端连接,会自动交给自定义类中的handle方法去处理'''
        server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyServer)  # 创建一个基于UDP的对象
        server.serve_forever()  # 启动该服务对象
    
    # 控制台打印的数据
    # ('127.0.0.1', 52524)
    # CLient2
    # ('127.0.0.1', 52529)
    # clet1
    # ('127.0.0.1', 52529)
    # CLienT1
    # ('127.0.0.1', 54485)
    # CLiEnt3
    

    客户端

    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_addr = ('127.0.0.1', 8080)
    
    while True:
        res = input(">>>:").strip()
        client.sendto(res.encode('utf-8'), server_addr)
    
        data, response_server_addr = client.recvfrom(1024)
        print(data.decode('utf-8'), response_server_addr)
    
    # 窗口1 控制台数据(输入与输出)
    # >>>:clIeNt1
    # clet1 ('127.0.0.1', 8080)
    # >>>:CLienT1
    # client1 ('127.0.0.1', 8080)
    # >>>:
    
    # 窗口2 控制台数据(输入与输出)
    # >>>:CLient2
    # client2 ('127.0.0.1', 8080)
    # >>>:
    
    # 窗口1 控制台数据(输入与输出)
    # >>>:CLiEnt3
    # client3 ('127.0.0.1', 8080)
    # >>>:
    

    为什么UDP时重写的handle方法里不用写通信循环

    handle 是处理一次连接请求的,handle结束连接就断开了
    
    UDP是不需要保持(双向)连接的,所以每次sendto 都是单个请求(都附带服务器端地址及端口),不能写通信循环(不然就拿着一个sendto 过来的数据循环打印了)
    
    而TCP是基于双向通道通信的,handle结束后连接就断开了(再client.send() 这个连接状态就已经不存在了),所以TCP这边的服务端要写通信循环保持连接来多次通信
    
  • 相关阅读:
    ORACLE中的TOP-N查询(TOP-N分析)、分页查询
    ORACLE复杂查询之子查询
    ORACLE复杂查询之连接查询
    利用rand7() 产生rand10()(腾讯)
    汉罗塔1(递归和分治)
    数位dp(不要62)
    数位dp(二进制01问题)
    多重背包
    模拟(所有边权)
    模拟(进制)
  • 原文地址:https://www.cnblogs.com/suwanbin/p/11323276.html
Copyright © 2020-2023  润新知