zoukankan      html  css  js  c++  java
  • d29天 上传电影练习 UDP使用 ScketServer模块

    TCP小结

    socket套接字
        TCP
            1.最简易的版本的客户端与服务端之间通信
                
        2.通信循环
            recv()  阻塞 
                
        3.连接循环
            accept()  阻塞
                    
        4.TCP粘包问题
        5.struct模块  对数据进行打包处理 固定长度
            pack
            unpack
                                
    小结

    解决粘包问题的流程

    服务端
        1.生成一个字典
        2.制作该字典的报头
            json序列化
            编码 统计长度
        3.发送字典的报头
        4.发送字典
        5.最后发真实数据
    服务端
    客户端
        1.先接受固定长度的4个字节字典报头
        2.解析获取字典数据的长度
            unpack(...)[0]
        3.接受字典数据
            解码 反序列化
        4.接受真实数据
    客户端
    为什么要多加一个字典
    1.打包的数据大小有限
    2.可以携带更多的信息

    上传电影练习

    import socket
    import json
    import struct
    
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    while True:
        conn,addr = server.accept()
        while True:
            try:
                header_len = conn.recv(4)
                # 解析字典报头
                header_len = struct.unpack('i',header_len)[0]
                # 再接收字典数据
                header_dic = conn.recv(header_len)
                # 反序列化 得到真实数据
                real_dic = json.loads(header_dic.decode('utf-8'))
                # 获取数据长度
                total_size = real_dic.get('file_size')
                # 循环接收并写入文件
                recv_size = 0
                with open(real_dic.get('file_name'),'wb') as f:
                    while recv_size < total_size:
                        data = conn.recv(1024)
                        f.write(data)
                        recv_size += len(data)
                    print('上传成功')
            except ConnectionResetError as e:
                print(e)
                break
        conn.close()
    服务端
    import socket
    import json
    import os
    import struct
    
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    while True:
        # 获取电影列表 循环展示
        MOVIE_DIR = r'D:python脱产10期视频day25视频'
        movie_list = os.listdir(MOVIE_DIR)
        # print(movie_list)
        for i,movie in enumerate(movie_list,1):
            print(i,movie)
        # 用户选择
        choice = input('please choice movie to upload>>>:')
        # 判断是否是数字
        if choice.isdigit():
            # 将字符串数字转为int
            choice = int(choice) - 1
            # 判断用户选择在不在列表范围内
            if choice in range(0,len(movie_list)):
                # 获取到用户想上传的文件路径
                path = movie_list[choice]
                # 拼接文件的绝对路径
                file_path = os.path.join(MOVIE_DIR,path)
                # 获取文件大小
                file_size = os.path.getsize(file_path)
                # 定义一个字典
                res_d = {
                    'file_name':'性感荷官在线发牌.mp4',
                    'file_size':file_size,
                    'msg':'注意身体,多喝营养快线'
                }
                # 序列化字典
                json_d = json.dumps(res_d)
                json_bytes = json_d.encode('utf-8')
    
                # 1.先制作字典格式的报头
                header = struct.pack('i',len(json_bytes))
                # 2.发送字典的报头
                client.send(header)
                # 3.再发字典
                client.send(json_bytes)
                # 4.再发文件数据(打开文件循环发送)
                with open(file_path,'rb') as f:
                    for line in f:
                        client.send(line)
            else:
                print('not in range')
        else:
            print('must be a number')
    客户端

    异常

    程序在运行过程中出现了不可预知的错误
    并且该错误没有对应的处理机制,那么就会以异常的形式表现出来
    造成的影响就是整个程序无法再正常运

    异常的结构

    1.异常的类型:NAMEERROR
    2.异常的信息:name 'fdsdfsdf' is not defined
    3.异常的位置:Traceback (most recent call last):
        File "D:/python/day29/01 异常处理.py", line 1, in <module>

    异常的种类

    分为两大类
        1.语法错误
            是你程序立刻就能解决的,这种错误是不能被容忍的
            语法上的错误 发现之后应该立刻解决
                
        2.逻辑错误
            这种错是可以被容忍的 因为一眼看不出来
            针对逻辑上的错误  可以采用异常处理机制进行捕获
                                    

    常见的错误类型

    NAMERROR     名字错误
    SyntaxError    语法错误
    KeyError         键不存在
    ValueError      值错误
    IndexError      索引错误
    TypeError       类型错误

    如何避免

    异常处理   
    在你认为可能会出现bug的代码块上方try一下:注意try内部的代码块越少越好

    try:
    可能出错的代码
    except 出错的类型 as e: # 将报错信息赋值给变量e
    出错之后的处理机制

    UDP

    UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考模型中一种无连接的传输层协议,提供简单不可靠信息传送服务

    是一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成。且不对传送数据包进行可靠性保证,适合于一次传输少量数据

    UDP使用

    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    # 不需要建立双向连接  直接收发数据
    server_addres = ('127.0.0.1',8080)
    # 发送数据需要对方的大致
    while True:
        client.sendto(b'strawberry',server_addres)
        data , addr = client.recvfrom(1024)
        print('服务端发的数据',data)
        print('服务端发的地址',addr)
    服务端
    import socket
    
    # UDP中要指定type=socket.SOCK_DGRAM,默认代表的是TCP
    server = socket.socket(type=socket.SOCK_DGRAM)
    server.bind(('127.0.0.1',8080))
    # UDP不需要设置半连接池,它也没有半连接池的概念
    
    # 因为没有双向通道, 不需要accept来建立双向连接,也就不需要循环连接,直接通信循环
    while True:
        data, addr = server.recvfrom(1024)
        print('数据', data)
        # 客户端的数据
        print('地址',addr)
        #客户端的地址
        server.sendto(data.upper(),addr)
        #发送数据需要客户端的地址
    客户端

    TCP与UDP之间的区别 

    tcp 和udp 
    tcp:可靠,传输安全,粘包
    通过连接传输:在发送数据时,会等到对方确定接收完成时,将数据删除,如果没有,就会保存到内存,等待确认
    udp:不可靠,不须建立连接,不粘包,
    发送数据,一旦发送,就会删除缓存数据,如果没收到,那就没收到
    与TCP的区别    *****
        不可靠传输
        不需要建立连接
        不会粘包
        单次数据包不能太大
    服务器端
        服务器不需要监听 listen
        不需要接收请求 accept
        收数据  recvfrom(缓冲区大小)
        发数据  sendto(数据,地址)
    
    客户端:
        不需要建立连接
        收数据  recvfrom(缓冲区大小)
        发数据  sendto(数据,地址)
    流程
    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)
    data, addr1 = server.recvfrom(1024)
    print(data)
    data, addr2 = server.recvfrom(1024)
    print(data)
    服务端
    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_address = ('127.0.0.1',8080)
    # while True:
        # msg = input('>>>:')
    client.sendto(b'hello',server_address)
    client.sendto(b'hello',server_address)
    client.sendto(b'hello',server_address)
        # data, server_addr = client.recvfrom(1024)
        # print(data
    客户端

     UDP实现简易版本的QQ

    实现多个用户同时发信息(实现并发)

    并发:看起来像同时运行的
    并行:真正意义上的同时运行
    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'))
        msg = input('>>>:')
        server.sendto(msg.encode('utf-8'),addr)
    服务端
    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_address = ('127.0.0.1',8080)
    
    while True:
        msg = input('>>>:')
        msg = '来自客户端1的消息:%s'%msg
        client.sendto(msg.encode('utf-8'),server_address)
        data, server_addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
    客户端1
    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_address = ('127.0.0.1',8080)
    
    while True:
        msg = input('>>>:')
        msg = '来自客户端2的消息:%s'%msg
        client.sendto(msg.encode('utf-8'),server_address)
        data, server_addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
    客户端2
    import socket
    
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_address = ('127.0.0.1',8080)
    
    while True:
        msg = input('>>>:')
        msg = '来自客户端3的消息:%s'%msg
        client.sendto(msg.encode('utf-8'),server_address)
        data, server_addr = client.recvfrom(1024)
        print(data.decode('utf-8'))
    客户端3

    socketserver模块

    socketserver模块是基于socket而来的模块,它是在socket的基础上进行了一层封装,并且实现并发等功能。

    ThreadingTCPServer的使用方法

    1、创建一个继承socketserver.BaseRequestHandler的类
    
    2、类中必须重写一个名为handler的方法
    
    3、实例化一个服务器类,传入服务器的地址和请求处理程序类
    
    4、调用serve_forever()事件循环监听

    代码

    #!/usr/bin/env python3
    import socketserver
    
    class Handler(socketserver.BaseRequestHandler):    # 必须继承BaseRequestHandler
        def handle(self):        # 必须有handle方法
            print('New connection:',self.client_address)
            while True:
                data = self.request.recv(1024)
                if not data:break
                print('Client data:',data.decode())
                self.request.send(data)
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),Handler)    # 实例化对象,实现多线程的socket
        server.serve_forever()    # 事件监听,并调用handler方法
    View Code

    让tcp也能够实现udp能够看起来多个客户在运行

    import socketserver
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # print('来啦 老弟')
            while True:
                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()  # 启动该服务对象
    服务端
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)  # 创建一个基于TCP的对象
     ThreadingTCPServer多线程tcpserver 会帮你实时监听代码中的地址,一旦有用户来进行请求,它会将这个用户交给MyServer类中的handl方法来处理,会自动触发方法.
    

      

    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    while True:
        client.send(b'hello')
        data = client.recv(1024)
        print(data.decode('utf-8'))
    客户端

    开udp 支持并发

    import socketserver
    
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # print('来啦 老弟')
            while True:
                data,sock = self.request
                print(self.client_address)  # 客户端地址
                print(data.decode('utf-8'))
                sock.sendto(data.upper(),self.client_address)
    
    
    if __name__ == '__main__':
        """只要有客户端连接  会自动交给自定义类中的handle方法去处理"""
        server = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer)  # 创建一个基于TCP的对象
        server.serve_forever()  # 启动该服务对象
    服务端
    import socket
    import time
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_address = ('127.0.0.1',8080)
    
    while True:
        client.sendto(b'hello',server_address)
        data,addr = client.recvfrom(1024)
        print(data.decode('utf-8'),addr)
        time.sleep(1)
    客户端
  • 相关阅读:
    最长上升子序列
    system call filters failed to install; check the logs and fix your configuration or disable system c
    linux centos 7 安装vnc远程服务
    Delphi XE 错误提示: [MySQL]-314. Cannot load vendor library [libmysql.dll orlibmysqlld.dll]
    MYSQL 修改密码的几种方式
    MySQL 常用操作和字段类型
    Java 获取GUID
    C# 获取GUID
    C++ 获取GUID
    Delphi GUID[2] 获取GUID值的方式
  • 原文地址:https://www.cnblogs.com/komorebi/p/11324065.html
Copyright © 2011-2022 走看看